diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 6782eb520c7a..d79c49379923 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -6,27 +6,28 @@ on: push: branches: [main] pull_request: - branches: [optimism] + branches: [main] merge_group: jobs: - #check-reth: - # runs-on: ubuntu-20.04 - # timeout-minutes: 60 + check-reth: + runs-on: ubuntu-20.04 + timeout-minutes: 60 - # steps: - # - uses: actions/checkout@v4 - # - uses: dtolnay/rust-toolchain@stable - # with: - # target: x86_64-pc-windows-gnu - # - uses: taiki-e/install-action@cross - # - uses: Swatinem/rust-cache@v2 - # with: - # cache-on-failure: true - # - name: mingw-w64 - # run: sudo apt-get install -y mingw-w64 - # - name: Check Reth - # run: cargo check --target x86_64-pc-windows-gnu + steps: + - uses: actions/checkout@v4 + - uses: rui314/setup-mold@v1 + - uses: dtolnay/rust-toolchain@stable + with: + target: x86_64-pc-windows-gnu + - uses: taiki-e/install-action@cross + - uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: true + - name: mingw-w64 + run: sudo apt-get install -y mingw-w64 + - name: Check Reth + run: cargo check --target x86_64-pc-windows-gnu check-op-reth: runs-on: ubuntu-20.04 diff --git a/Cargo.lock b/Cargo.lock index 256f538b176d..e9c50ff20b2b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -106,9 +106,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy-chains" -version = "0.1.52" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f15afc5993458b42739ab3b69bdb6b4c8112acd3997dbea9bc092c9517137c" +checksum = "4ab9d1367c6ffb90c93fb4a9a4989530aa85112438c6f73a734067255d348469" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -170,14 +170,14 @@ dependencies = [ "alloy-transport", "futures", "futures-util", - "thiserror 2.0.9", + "thiserror 2.0.11", ] [[package]] name = "alloy-dyn-abi" -version = "0.8.18" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44e3b98c37b3218924cd1d2a8570666b89662be54e5b182643855f783ea68b33" +checksum = "bc9138f4f0912793642d453523c3116bd5d9e11de73b70177aa7cb3e94b98ad2" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -203,7 +203,7 @@ dependencies = [ "crc", "rand 0.8.5", "serde", - "thiserror 2.0.9", + "thiserror 2.0.11", ] [[package]] @@ -271,9 +271,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "0.8.18" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "731ea743b3d843bc657e120fb1d1e9cc94f5dab8107e35a82125a63e6420a102" +checksum = "24acd2f5ba97c7a320e67217274bc81fe3c3174b8e6144ec875d9d54e760e278" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -291,7 +291,7 @@ dependencies = [ "alloy-sol-types", "serde", "serde_json", - "thiserror 2.0.9", + "thiserror 2.0.11", "tracing", ] @@ -317,7 +317,7 @@ dependencies = [ "futures-utils-wasm", "serde", "serde_json", - "thiserror 2.0.9", + "thiserror 2.0.11", ] [[package]] @@ -345,16 +345,16 @@ dependencies = [ "rand 0.8.5", "serde_json", "tempfile", - "thiserror 2.0.9", + "thiserror 2.0.11", "tracing", "url", ] [[package]] name = "alloy-primitives" -version = "0.8.18" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "788bb18e8f61d5d9340b52143f27771daf7e1dccbaf2741621d2493f9debf52e" +checksum = "ec878088ec6283ce1e90d280316aadd3d6ce3de06ff63d68953c855e7e447e92" dependencies = [ "alloy-rlp", "arbitrary", @@ -366,7 +366,7 @@ dependencies = [ "foldhash", "getrandom 0.2.15", "hashbrown 0.15.2", - "indexmap 2.7.0", + "indexmap 2.7.1", "itoa", "k256", "keccak-asm", @@ -415,7 +415,7 @@ dependencies = [ "schnellru", "serde", "serde_json", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", "tracing", "url", @@ -443,9 +443,9 @@ dependencies = [ [[package]] name = "alloy-rlp" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f542548a609dca89fcd72b3b9f355928cf844d4363c5eed9c5273a3dd225e097" +checksum = "3d6c1d995bff8d011f7cd6c81820d51825e6e06d6db73914c1630ecf544d83d6" dependencies = [ "alloy-rlp-derive", "arrayvec", @@ -454,13 +454,13 @@ dependencies = [ [[package]] name = "alloy-rlp-derive" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a833d97bf8a5f0f878daf2c8451fff7de7f9de38baa5a45d936ec718d81255a" +checksum = "a40e1ef334153322fd878d07e86af7a529bcb86b2439525920a88eba87bcf943" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -549,7 +549,7 @@ dependencies = [ "ethereum_ssz_derive", "serde", "serde_with", - "thiserror 2.0.9", + "thiserror 2.0.11", ] [[package]] @@ -602,7 +602,7 @@ dependencies = [ "jsonrpsee-types", "serde", "serde_json", - "thiserror 2.0.9", + "thiserror 2.0.11", ] [[package]] @@ -630,7 +630,7 @@ dependencies = [ "alloy-serde", "serde", "serde_json", - "thiserror 2.0.9", + "thiserror 2.0.11", ] [[package]] @@ -668,7 +668,7 @@ dependencies = [ "auto_impl", "elliptic-curve", "k256", - "thiserror 2.0.9", + "thiserror 2.0.11", ] [[package]] @@ -686,61 +686,61 @@ dependencies = [ "coins-bip39", "k256", "rand 0.8.5", - "thiserror 2.0.9", + "thiserror 2.0.11", ] [[package]] name = "alloy-sol-macro" -version = "0.8.18" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a07b74d48661ab2e4b50bb5950d74dbff5e61dd8ed03bb822281b706d54ebacb" +checksum = "8d039d267aa5cbb7732fa6ce1fd9b5e9e29368f580f80ba9d7a8450c794de4b2" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] name = "alloy-sol-macro-expander" -version = "0.8.18" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19cc9c7f20b90f9be1a8f71a3d8e283a43745137b0837b1a1cb13159d37cad72" +checksum = "620ae5eee30ee7216a38027dec34e0585c55099f827f92f50d11e3d2d3a4a954" dependencies = [ "alloy-sol-macro-input", "const-hex", "heck", - "indexmap 2.7.0", + "indexmap 2.7.1", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", "syn-solidity", "tiny-keccak", ] [[package]] name = "alloy-sol-macro-input" -version = "0.8.18" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713b7e6dfe1cb2f55c80fb05fd22ed085a1b4e48217611365ed0ae598a74c6ac" +checksum = "ad9f7d057e00f8c5994e4ff4492b76532c51ead39353aa2ed63f8c50c0f4d52e" dependencies = [ "const-hex", "dunce", "heck", "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", "syn-solidity", ] [[package]] name = "alloy-sol-type-parser" -version = "0.8.18" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1eda2711ab2e1fb517fc6e2ffa9728c9a232e296d16810810e6957b781a1b8bc" +checksum = "74e60b084fe1aef8acecda2743ff2d93c18ff3eb67a2d3b12f62582a1e66ef5e" dependencies = [ "serde", "winnow", @@ -748,9 +748,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "0.8.18" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b478bc9c0c4737a04cd976accde4df7eba0bdc0d90ad6ff43d58bc93cf79c1" +checksum = "c1382302752cd751efd275f4d6ef65877ddf61e0e6f5ac84ef4302b79a33a31a" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -771,7 +771,7 @@ dependencies = [ "futures-utils-wasm", "serde", "serde_json", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", "tower 0.5.2", "tracing", @@ -913,11 +913,12 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "3.0.6" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", + "once_cell", "windows-sys 0.59.0", ] @@ -938,7 +939,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -1136,7 +1137,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -1172,18 +1173,18 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] name = "async-trait" -version = "0.1.84" +version = "0.1.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1244b10dcd56c92219da4e14caa97e312079e185f04ba3eea25061561dc0a0" +checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -1215,13 +1216,13 @@ dependencies = [ [[package]] name = "auto_impl" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" +checksum = "e12882f59de5360c748c4cbf569a042d5fb0eb515f7bea9c1f470b47f6ffbd73" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -1318,7 +1319,7 @@ version = "0.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cexpr", "clang-sys", "itertools 0.13.0", @@ -1327,23 +1328,23 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] name = "bit-set" -version = "0.5.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" dependencies = [ "bit-vec", ] [[package]] name = "bit-vec" -version = "0.6.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bitflags" @@ -1353,9 +1354,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" dependencies = [ "arbitrary", "serde", @@ -1374,6 +1375,19 @@ dependencies = [ "wyz", ] +[[package]] +name = "blake3" +version = "1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8ee0c1824c4dea5b5f81736aff91bae041d2c07ee1192bec91054e10e3e601e" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", +] + [[package]] name = "block-buffer" version = "0.9.0" @@ -1419,11 +1433,11 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c340fe0f0b267787095cbe35240c6786ff19da63ec7b69367ba338eace8169b" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "boa_interner", "boa_macros", "boa_string", - "indexmap 2.7.0", + "indexmap 2.7.1", "num-bigint", "rustc-hash 2.1.0", ] @@ -1435,7 +1449,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f620c3f06f51e65c0504ddf04978be1b814ac6586f0b45f6019801ab5efd37f9" dependencies = [ "arrayvec", - "bitflags 2.6.0", + "bitflags 2.8.0", "boa_ast", "boa_gc", "boa_interner", @@ -1449,7 +1463,7 @@ dependencies = [ "fast-float2", "hashbrown 0.15.2", "icu_normalizer", - "indexmap 2.7.0", + "indexmap 2.7.1", "intrusive-collections", "itertools 0.13.0", "num-bigint", @@ -1469,7 +1483,7 @@ dependencies = [ "static_assertions", "tap", "thin-vec", - "thiserror 2.0.9", + "thiserror 2.0.11", "time", ] @@ -1495,7 +1509,7 @@ dependencies = [ "boa_gc", "boa_macros", "hashbrown 0.15.2", - "indexmap 2.7.0", + "indexmap 2.7.1", "once_cell", "phf", "rustc-hash 2.1.0", @@ -1510,7 +1524,7 @@ checksum = "9fd3f870829131332587f607a7ff909f1af5fc523fd1b192db55fbbdf52e8d3c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", "synstructure", ] @@ -1520,7 +1534,7 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cc142dac798cdc6e2dbccfddeb50f36d2523bb977a976e19bdb3ae19b740804" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "boa_ast", "boa_interner", "boa_macros", @@ -1574,9 +1588,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "4.0.1" +version = "4.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" +checksum = "74fa05ad7d803d413eb8380983b092cbbaf9a85f151b871360e7b00cd7060b37" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -1632,7 +1646,7 @@ checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -1691,7 +1705,7 @@ checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" dependencies = [ "camino", "cargo-platform", - "semver 1.0.24", + "semver 1.0.25", "serde", "serde_json", "thiserror 1.0.69", @@ -1720,9 +1734,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.7" +version = "1.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7" +checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229" dependencies = [ "jobserver", "libc", @@ -1821,9 +1835,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.23" +version = "4.5.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" +checksum = "769b0145982b4b48713e01ec42d61614425f27b7058bda7180a3a41f30104796" dependencies = [ "clap_builder", "clap_derive", @@ -1831,9 +1845,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.23" +version = "4.5.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" +checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7" dependencies = [ "anstream", "anstyle", @@ -1843,14 +1857,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.18" +version = "4.5.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -2044,6 +2058,12 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "constant_time_eq" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" + [[package]] name = "convert_case" version = "0.6.0" @@ -2099,9 +2119,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] @@ -2214,7 +2234,7 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "crossterm_winapi", "mio 1.0.3", "parking_lot", @@ -2235,9 +2255,9 @@ dependencies = [ [[package]] name = "crunchy" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" [[package]] name = "crypto-bigint" @@ -2326,7 +2346,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -2350,7 +2370,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -2361,7 +2381,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -2381,15 +2401,15 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f" [[package]] name = "data-encoding-macro" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1559b6cba622276d6d63706db152618eeb15b89b3e4041446b05876e352e639" +checksum = "5b16d9d0d88a5273d830dac8b78ceb217ffc9b1d5404e5597a3542515329405b" dependencies = [ "data-encoding", "data-encoding-macro-internal", @@ -2397,12 +2417,12 @@ dependencies = [ [[package]] name = "data-encoding-macro-internal" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "332d754c0af53bc87c108fed664d121ecf59207ec4196041f04d6ab9002ad33f" +checksum = "1145d32e826a7748b69ee8fc62d3e6355ff7f1051df53141e7048162fc90481b" dependencies = [ "data-encoding", - "syn 1.0.109", + "syn 2.0.96", ] [[package]] @@ -2470,7 +2490,7 @@ checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -2491,7 +2511,7 @@ dependencies = [ "convert_case", "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", "unicode-xid", ] @@ -2605,7 +2625,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -2686,7 +2706,7 @@ dependencies = [ "revm", "serde", "serde_json", - "thiserror 2.0.9", + "thiserror 2.0.11", "walkdir", ] @@ -2751,7 +2771,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -2762,7 +2782,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -2782,7 +2802,7 @@ checksum = "3bf679796c0322556351f287a51b49e48f7c4986e727b5dd78c972d30e2e16cc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -2816,9 +2836,9 @@ dependencies = [ [[package]] name = "ethereum_ssz" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "036c84bd29bff35e29bbee3c8fc0e2fb95db12b6f2f3cae82a827fbc97256f3a" +checksum = "862e41ea8eea7508f70cfd8cd560f0c34bb0af37c719a8e06c2672f0f031d8e5" dependencies = [ "alloy-primitives", "ethereum_serde_utils", @@ -2831,14 +2851,14 @@ dependencies = [ [[package]] name = "ethereum_ssz_derive" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dc8e67e1f770f5aa4c2c2069aaaf9daee7ac21bed357a71b911b37a58966cfb" +checksum = "d31ecf6640112f61dc34b4d8359c081102969af0edd18381fed2052f6db6a410" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -2862,7 +2882,7 @@ dependencies = [ "reth-node-ethereum", "serde", "serde_json", - "thiserror 2.0.9", + "thiserror 2.0.11", ] [[package]] @@ -2951,7 +2971,7 @@ dependencies = [ "reth-tracing", "reth-trie-db", "serde", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", ] @@ -3221,17 +3241,6 @@ dependencies = [ "bytes", ] -[[package]] -name = "fastrlp" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce8dba4714ef14b8274c371879b175aa55b16b30f269663f19d576f380018dc4" -dependencies = [ - "arrayvec", - "auto_impl", - "bytes", -] - [[package]] name = "fdlimit" version = "0.3.0" @@ -3411,7 +3420,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -3600,7 +3609,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.7.0", + "indexmap 2.7.1", "slab", "tokio", "tokio-util", @@ -3642,7 +3651,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", - "allocator-api2", ] [[package]] @@ -3722,7 +3730,7 @@ dependencies = [ "once_cell", "rand 0.8.5", "serde", - "thiserror 2.0.9", + "thiserror 2.0.11", "tinyvec", "tokio", "tracing", @@ -3746,7 +3754,7 @@ dependencies = [ "resolv-conf", "serde", "smallvec", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", "tracing", ] @@ -4093,7 +4101,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -4150,7 +4158,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -4191,9 +4199,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.7.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" dependencies = [ "arbitrary", "equivalent", @@ -4220,7 +4228,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "232929e1d75fe899576a3d5c7416ad0d88dbfbb3c3d6aa00873a7408a50ddb88" dependencies = [ "ahash", - "indexmap 2.7.0", + "indexmap 2.7.1", "is-terminal", "itoa", "log", @@ -4263,15 +4271,15 @@ dependencies = [ [[package]] name = "instability" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "894813a444908c0c8c0e221b041771d107c4a21de1d317dc49bcc66e3c9e5b3f" +checksum = "0bf9fed6d91cfb734e7476a06bde8300a1b94e217e1b523b6f0cd1a01998c71d" dependencies = [ "darling", "indoc", "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -4321,9 +4329,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.10.1" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "iri-string" @@ -4337,13 +4345,13 @@ dependencies = [ [[package]] name = "is-terminal" -version = "0.4.13" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +checksum = "e19b23d53f35ce9f56aebc7d1bb4e6ac1e9c0db7ac85c8d1760c04379edced37" dependencies = [ "hermit-abi 0.4.0", "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -4407,9 +4415,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.76" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ "once_cell", "wasm-bindgen", @@ -4417,9 +4425,9 @@ dependencies = [ [[package]] name = "jsonrpsee" -version = "0.24.7" +version = "0.24.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5c71d8c1a731cc4227c2f698d377e7848ca12c8a48866fc5e6951c43a4db843" +checksum = "834af00800e962dee8f7bfc0f60601de215e73e78e5497d733a2919da837d3c8" dependencies = [ "jsonrpsee-client-transport", "jsonrpsee-core", @@ -4435,9 +4443,9 @@ dependencies = [ [[package]] name = "jsonrpsee-client-transport" -version = "0.24.7" +version = "0.24.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "548125b159ba1314104f5bb5f38519e03a41862786aa3925cf349aae9cdd546e" +checksum = "def0fd41e2f53118bd1620478d12305b2c75feef57ea1f93ef70568c98081b7e" dependencies = [ "base64 0.22.1", "futures-channel", @@ -4460,9 +4468,9 @@ dependencies = [ [[package]] name = "jsonrpsee-core" -version = "0.24.7" +version = "0.24.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2882f6f8acb9fdaec7cefc4fd607119a9bd709831df7d7672a1d3b644628280" +checksum = "76637f6294b04e747d68e69336ef839a3493ca62b35bf488ead525f7da75c5bb" dependencies = [ "async-trait", "bytes", @@ -4487,9 +4495,9 @@ dependencies = [ [[package]] name = "jsonrpsee-http-client" -version = "0.24.7" +version = "0.24.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3638bc4617f96675973253b3a45006933bde93c2fd8a6170b33c777cc389e5b" +checksum = "87c24e981ad17798bbca852b0738bfb7b94816ed687bd0d5da60bfa35fa0fdc3" dependencies = [ "async-trait", "base64 0.22.1", @@ -4512,22 +4520,22 @@ dependencies = [ [[package]] name = "jsonrpsee-proc-macros" -version = "0.24.7" +version = "0.24.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06c01ae0007548e73412c08e2285ffe5d723195bf268bce67b1b77c3bb2a14d" +checksum = "6fcae0c6c159e11541080f1f829873d8f374f81eda0abc67695a13fc8dc1a580" dependencies = [ "heck", "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] name = "jsonrpsee-server" -version = "0.24.7" +version = "0.24.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82ad8ddc14be1d4290cd68046e7d1d37acd408efed6d3ca08aefcc3ad6da069c" +checksum = "66b7a3df90a1a60c3ed68e7ca63916b53e9afa928e33531e87f61a9c8e9ae87b" dependencies = [ "futures-util", "http", @@ -4552,9 +4560,9 @@ dependencies = [ [[package]] name = "jsonrpsee-types" -version = "0.24.7" +version = "0.24.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a178c60086f24cc35bb82f57c651d0d25d99c4742b4d335de04e97fa1f08a8a1" +checksum = "ddb81adb1a5ae9182df379e374a79e24e992334e7346af4d065ae5b2acb8d4c6" dependencies = [ "http", "serde", @@ -4564,9 +4572,9 @@ dependencies = [ [[package]] name = "jsonrpsee-wasm-client" -version = "0.24.7" +version = "0.24.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a01cd500915d24ab28ca17527e23901ef1be6d659a2322451e1045532516c25" +checksum = "42e41af42ca39657313748174d02766e5287d3a57356f16756dbd8065b933977" dependencies = [ "jsonrpsee-client-transport", "jsonrpsee-core", @@ -4575,9 +4583,9 @@ dependencies = [ [[package]] name = "jsonrpsee-ws-client" -version = "0.24.7" +version = "0.24.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fe322e0896d0955a3ebdd5bf813571c53fea29edd713bc315b76620b327e86d" +checksum = "6f4f3642a292f5b76d8a16af5c88c16a0860f2ccc778104e5c848b28183d9538" dependencies = [ "http", "jsonrpsee-client-transport", @@ -4680,12 +4688,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "libm" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" - [[package]] name = "libp2p-identity" version = "0.2.10" @@ -4722,7 +4724,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "libc", "redox_syscall", ] @@ -4793,9 +4795,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "litemap" @@ -4816,9 +4818,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" [[package]] name = "loom" @@ -4915,19 +4917,19 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] name = "metrics-exporter-prometheus" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b6f8152da6d7892ff1b7a1c0fa3f435e92b5918ad67035c3bb432111d9a29b" +checksum = "12779523996a67c13c84906a876ac6fe4d07a6e1adb54978378e13f199251a62" dependencies = [ "base64 0.22.1", - "indexmap 2.7.0", + "indexmap 2.7.1", "metrics", - "metrics-util 0.18.0", + "metrics-util", "quanta", "thiserror 1.0.69", ] @@ -4948,20 +4950,6 @@ dependencies = [ "windows 0.58.0", ] -[[package]] -name = "metrics-util" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15b482df36c13dd1869d73d14d28cd4855fbd6cfc32294bee109908a9f4a4ed7" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", - "hashbrown 0.15.2", - "metrics", - "quanta", - "sketches-ddsketch", -] - [[package]] name = "metrics-util" version = "0.19.0" @@ -4971,9 +4959,10 @@ dependencies = [ "crossbeam-epoch", "crossbeam-utils", "hashbrown 0.15.2", - "indexmap 2.7.0", + "indexmap 2.7.1", "metrics", "ordered-float", + "quanta", "rand 0.8.5", "rand_xoshiro", "sketches-ddsketch", @@ -5023,9 +5012,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" +checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" dependencies = [ "adler2", ] @@ -5077,16 +5066,16 @@ dependencies = [ [[package]] name = "moka" -version = "0.12.9" +version = "0.12.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23db87a7f248211f6a7c8644a1b750541f8a4c68ae7de0f908860e44c0c201f6" +checksum = "a9321642ca94a4282428e6ea4af8cc2ca4eac48ac7a6a4ea8f33f76d0ce70926" dependencies = [ "crossbeam-channel", "crossbeam-epoch", "crossbeam-utils", "loom", "parking_lot", - "quanta", + "portable-atomic", "rustc_version 0.4.1", "smallvec", "tagptr", @@ -5167,7 +5156,7 @@ version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "filetime", "fsevent-sys", "inotify", @@ -5287,7 +5276,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", - "libm", ] [[package]] @@ -5318,7 +5306,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -5332,9 +5320,9 @@ dependencies = [ [[package]] name = "nybbles" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3409fc85ac27b27d971ea7cd1aabafd2eefa6de7e481c8d4f707225c117e81a" +checksum = "8983bb634df7248924ee0c4c3a749609b5abcb082c28fffe3254b3eb3602b307" dependencies = [ "alloy-rlp", "arbitrary", @@ -5371,9 +5359,9 @@ checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" [[package]] name = "op-alloy-consensus" -version = "0.9.0" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0adb232ec805af3aa35606c19329aa7dc44c4457ae318ed0b8fc7f799dd7dbfe" +checksum = "e28dc4e397dd8969f7f98ea6454a5c531349a58c76e12448b0c2de6581df7b8c" dependencies = [ "alloy-consensus", "alloy-eips", @@ -5384,29 +5372,20 @@ dependencies = [ "derive_more", "serde", "serde_with", - "thiserror 2.0.9", + "thiserror 2.0.11", ] [[package]] -name = "op-alloy-genesis" -version = "0.9.0" +name = "op-alloy-flz" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c272cfd65317538f5815c2b7059445230b050d48ebe2d0bab3e861d419a785" -dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-primitives", - "alloy-sol-types", - "serde", - "serde_repr", - "thiserror 2.0.9", -] +checksum = "76b44f5edbd5293636934a88ec38d683ee2075bfce43658d1033d76191e7aff6" [[package]] name = "op-alloy-network" -version = "0.9.0" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19872a58b7acceeffb8e88ea048bee1690e7cde53068bd652976435d61fcd1de" +checksum = "818a4ea62c0968fbf4e3bee2aa1349ad7ae6504b8aba73889a40160bebaa6818" dependencies = [ "alloy-consensus", "alloy-network", @@ -5417,48 +5396,21 @@ dependencies = [ "op-alloy-rpc-types", ] -[[package]] -name = "op-alloy-protocol" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad65d040648e0963ed378e88489f5805e24fb56b7e6611362299cd4c24debeb2" -dependencies = [ - "alloc-no-stdlib", - "alloy-consensus", - "alloy-eips", - "alloy-primitives", - "alloy-rlp", - "alloy-serde", - "async-trait", - "brotli", - "cfg-if", - "miniz_oxide", - "op-alloy-consensus", - "op-alloy-genesis", - "serde", - "thiserror 2.0.9", - "tracing", - "unsigned-varint", -] - [[package]] name = "op-alloy-rpc-jsonrpsee" -version = "0.9.0" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36b1f2547067c5b60f3144ae1033a54ce1d11341d8327fa8f203b048d51465e9" +checksum = "6446c38f9e3e86acac6f2972a4953ffdd8bfef032bec633b0280a95783c0babd" dependencies = [ - "alloy-eips", "alloy-primitives", "jsonrpsee", - "op-alloy-rpc-types", - "op-alloy-rpc-types-engine", ] [[package]] name = "op-alloy-rpc-types" -version = "0.9.0" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e68d1a51fe3ee143f102b82f54fa237f21d12635da363276901e6d3ef6c65b7b" +checksum = "4b9c83c664b953d474d6b58825800b6ff1d61876a686407e646cbf76891c1f9b" dependencies = [ "alloy-consensus", "alloy-eips", @@ -5474,19 +5426,19 @@ dependencies = [ [[package]] name = "op-alloy-rpc-types-engine" -version = "0.9.0" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f8833ef149ceb74f8f25a79801d110d88ec2db32e700fa10db6c5f5b5cbb71a" +checksum = "e8d05b5b5b3cff7f24eec2bc366f86a7ff0934678b1bda36d0ecaadba9504170" dependencies = [ "alloy-eips", "alloy-primitives", "alloy-rpc-types-engine", "alloy-serde", - "derive_more", + "ethereum_ssz", "op-alloy-consensus", - "op-alloy-protocol", "serde", - "thiserror 2.0.9", + "snap", + "thiserror 2.0.11", ] [[package]] @@ -5517,9 +5469,9 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl-probe" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "option-ext" @@ -5660,7 +5612,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc" dependencies = [ "memchr", - "thiserror 2.0.9", + "thiserror 2.0.11", "ucd-trie", ] @@ -5676,9 +5628,9 @@ dependencies = [ [[package]] name = "phf" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ "phf_macros", "phf_shared", @@ -5686,9 +5638,9 @@ dependencies = [ [[package]] name = "phf_generator" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ "phf_shared", "rand 0.8.5", @@ -5696,51 +5648,51 @@ dependencies = [ [[package]] name = "phf_macros" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" dependencies = [ "phf_generator", "phf_shared", "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] name = "phf_shared" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" dependencies = [ "siphasher", ] [[package]] name = "pin-project" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" +checksum = "1e2ec53ad785f4d35dac0adea7f7dc6f1bb277ad84a680c7afefeae05d1f5916" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" +checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] name = "pin-project-lite" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -5875,12 +5827,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.25" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" dependencies = [ "proc-macro2", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -5931,14 +5883,14 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] name = "proc-macro2" -version = "1.0.92" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] @@ -5949,7 +5901,7 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "731e0d9356b0c25f16f33b5be79b1c57b562f141ebfcdb0ad8ac2c13a24293b4" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "chrono", "flate2", "hex", @@ -5964,7 +5916,7 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc5b72d8145275d844d4b5f6d4e1eef00c8cd889edb6035c21675d1bb1f45c9f" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "hex", "procfs-core 0.17.0", "rustix", @@ -5976,7 +5928,7 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d3554923a69f4ce04c4a754260c338f505ce22642d3830e049a399fc2059a29" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "chrono", "hex", ] @@ -5987,19 +5939,19 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "239df02d8349b06fc07398a3a1697b06418223b1c7725085e801e7c0fc6a12ec" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "hex", ] [[package]] name = "proptest" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" +checksum = "14cae93065090804185d3b75f0bf93b8eeda30c7a9b4a33d3bdb3988d6229e50" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.6.0", + "bitflags 2.8.0", "lazy_static", "num-traits", "rand 0.8.5", @@ -6029,7 +5981,7 @@ checksum = "4ee1c9ac207483d5e7db4940700de86a9aae46ef90c48b57f99fe7edb8345e49" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -6084,7 +6036,7 @@ dependencies = [ "rustc-hash 2.1.0", "rustls", "socket2", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", "tracing", ] @@ -6103,7 +6055,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.9", + "thiserror 2.0.11", "tinyvec", "tracing", "web-time", @@ -6234,7 +6186,7 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdef7f9be5c0122f890d58bdf4d964349ba6a6161f705907526d891efabba57d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cassowary", "compact_str", "crossterm", @@ -6251,11 +6203,11 @@ dependencies = [ [[package]] name = "raw-cpuid" -version = "11.2.0" +version = "11.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ab240315c661615f2ee9f0f2cd32d5a7343a84d5ebcccb99d46e6637565e7b0" +checksum = "c6928fa44c097620b706542d428957635951bade7143269085389d42c8a4927e" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", ] [[package]] @@ -6290,7 +6242,7 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", ] [[package]] @@ -6350,11 +6302,11 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "regress" -version = "0.10.1" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1541daf4e4ed43a0922b7969bdc2170178bcacc5dabf7e39bc508a9fa3953a7a" +checksum = "78ef7fa9ed0256d64a688a3747d0fef7a88851c18a5e1d57f115f38ec2e09366" dependencies = [ - "hashbrown 0.14.5", + "hashbrown 0.15.2", "memchr", ] @@ -6372,6 +6324,7 @@ checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" dependencies = [ "base64 0.22.1", "bytes", + "futures-channel", "futures-core", "futures-util", "http", @@ -6507,6 +6460,7 @@ dependencies = [ "reth-payload-builder-primitives", "reth-payload-primitives", "reth-primitives", + "reth-primitives-traits", "reth-provider", "reth-revm", "reth-tasks", @@ -6546,7 +6500,7 @@ dependencies = [ "reth-rpc-types-compat", "reth-tracing", "serde", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", "tower 0.4.13", "tracing", @@ -6704,7 +6658,7 @@ dependencies = [ "secp256k1", "serde", "snmalloc-rs", - "thiserror 2.0.9", + "thiserror 2.0.11", "tikv-jemallocator", "tracy-client", ] @@ -6740,7 +6694,7 @@ dependencies = [ "proc-macro2", "quote", "similar-asserts", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -6824,7 +6778,6 @@ dependencies = [ "metrics", "page_size", "parking_lot", - "paste", "pprof", "proptest", "reth-db-api", @@ -6846,7 +6799,7 @@ dependencies = [ "sysinfo", "tempfile", "test-fuzz", - "thiserror 2.0.9", + "thiserror 2.0.11", ] [[package]] @@ -6904,7 +6857,7 @@ dependencies = [ "reth-trie-db", "serde", "serde_json", - "thiserror 2.0.9", + "thiserror 2.0.11", "tracing", ] @@ -6946,7 +6899,7 @@ dependencies = [ "schnellru", "secp256k1", "serde", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", "tokio-stream", "tracing", @@ -6971,7 +6924,7 @@ dependencies = [ "reth-network-peers", "reth-tracing", "secp256k1", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", "tracing", ] @@ -6998,7 +6951,7 @@ dependencies = [ "secp256k1", "serde", "serde_with", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", "tokio-stream", "tracing", @@ -7036,7 +6989,7 @@ dependencies = [ "reth-testing-utils", "reth-tracing", "tempfile", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", "tokio-stream", "tokio-util", @@ -7112,7 +7065,7 @@ dependencies = [ "secp256k1", "sha2 0.10.8", "sha3", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", "tokio-stream", "tokio-util", @@ -7155,6 +7108,7 @@ name = "reth-engine-primitives" version = "1.1.5" dependencies = [ "alloy-consensus", + "alloy-eips", "alloy-primitives", "alloy-rpc-types-engine", "futures", @@ -7166,7 +7120,7 @@ dependencies = [ "reth-primitives-traits", "reth-trie", "serde", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", ] @@ -7193,7 +7147,7 @@ dependencies = [ "reth-prune", "reth-stages-api", "reth-tasks", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", "tokio-stream", ] @@ -7228,7 +7182,6 @@ dependencies = [ "reth-evm", "reth-exex-types", "reth-metrics", - "reth-network", "reth-network-p2p", "reth-payload-builder", "reth-payload-builder-primitives", @@ -7252,7 +7205,7 @@ dependencies = [ "reth-trie-sparse", "revm-primitives", "schnellru", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", "tracing", ] @@ -7275,6 +7228,7 @@ dependencies = [ "reth-ethereum-forks", "reth-evm", "reth-fs-util", + "reth-payload-primitives", "reth-payload-validator", "reth-primitives", "reth-primitives-traits", @@ -7298,7 +7252,7 @@ dependencies = [ "reth-execution-errors", "reth-fs-util", "reth-storage-errors", - "thiserror 2.0.9", + "thiserror 2.0.11", ] [[package]] @@ -7331,7 +7285,7 @@ dependencies = [ "serde", "snap", "test-fuzz", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", "tokio-stream", "tokio-util", @@ -7357,10 +7311,10 @@ dependencies = [ "reth-chainspec", "reth-codecs-derive", "reth-ethereum-forks", - "reth-primitives", + "reth-ethereum-primitives", "reth-primitives-traits", "serde", - "thiserror 2.0.9", + "thiserror 2.0.11", ] [[package]] @@ -7499,7 +7453,7 @@ dependencies = [ "auto_impl", "futures-util", "metrics", - "metrics-util 0.19.0", + "metrics-util", "parking_lot", "reth-chainspec", "reth-consensus", @@ -7526,6 +7480,7 @@ dependencies = [ "alloy-genesis", "alloy-primitives", "alloy-sol-types", + "derive_more", "reth-chainspec", "reth-consensus", "reth-ethereum-consensus", @@ -7553,7 +7508,7 @@ dependencies = [ "reth-prune-types", "reth-storage-errors", "revm-primitives", - "thiserror 2.0.9", + "thiserror 2.0.11", ] [[package]] @@ -7648,7 +7603,7 @@ dependencies = [ "reth-transaction-pool", "reth-trie-db", "tempfile", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", ] @@ -7675,7 +7630,7 @@ version = "1.1.5" dependencies = [ "serde", "serde_json", - "thiserror 2.0.9", + "thiserror 2.0.11", ] [[package]] @@ -7718,7 +7673,7 @@ dependencies = [ "rand 0.8.5", "reth-tracing", "serde_json", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", "tokio-stream", "tokio-util", @@ -7730,12 +7685,12 @@ dependencies = [ name = "reth-libmdbx" version = "1.1.5" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "byteorder", "codspeed-criterion-compat", "dashmap", "derive_more", - "indexmap 2.7.0", + "indexmap 2.7.1", "parking_lot", "pprof", "rand 0.8.5", @@ -7743,7 +7698,7 @@ dependencies = [ "reth-mdbx-sys", "smallvec", "tempfile", - "thiserror 2.0.9", + "thiserror 2.0.11", "tracing", ] @@ -7782,7 +7737,7 @@ dependencies = [ "reqwest", "reth-tracing", "serde_with", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", "tracing", ] @@ -7842,7 +7797,7 @@ dependencies = [ "serial_test", "smallvec", "tempfile", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", "tokio-stream", "tokio-util", @@ -7867,7 +7822,7 @@ dependencies = [ "reth-network-types", "reth-tokio-util", "serde", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", "tokio-stream", ] @@ -7905,7 +7860,7 @@ dependencies = [ "secp256k1", "serde_json", "serde_with", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", "url", ] @@ -7914,8 +7869,8 @@ dependencies = [ name = "reth-network-types" version = "1.1.5" dependencies = [ + "alloy-eip2124", "humantime-serde", - "reth-ethereum-forks", "reth-net-banlist", "reth-network-peers", "serde", @@ -7936,7 +7891,7 @@ dependencies = [ "reth-fs-util", "serde", "tempfile", - "thiserror 2.0.9", + "thiserror 2.0.11", "tracing", "zstd", ] @@ -8066,7 +8021,7 @@ dependencies = [ "serde", "shellexpand", "strum", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", "toml", "tracing", @@ -8155,7 +8110,7 @@ dependencies = [ "metrics", "metrics-exporter-prometheus", "metrics-process", - "metrics-util 0.19.0", + "metrics-util", "procfs 0.16.0", "reqwest", "reth-metrics", @@ -8175,6 +8130,7 @@ dependencies = [ "reth-chainspec", "reth-db-api", "reth-engine-primitives", + "reth-payload-primitives", "reth-primitives-traits", "reth-trie-db", ] @@ -8198,7 +8154,7 @@ dependencies = [ "reth-optimism-forks", "reth-primitives-traits", "serde_json", - "thiserror 2.0.9", + "thiserror 2.0.11", ] [[package]] @@ -8295,7 +8251,7 @@ dependencies = [ "reth-revm", "revm", "revm-primitives", - "thiserror 2.0.9", + "thiserror 2.0.11", "tracing", ] @@ -8327,6 +8283,7 @@ dependencies = [ "eyre", "futures", "op-alloy-consensus", + "op-alloy-flz", "op-alloy-rpc-types-engine", "parking_lot", "reth-basic-payload-builder", @@ -8402,7 +8359,7 @@ dependencies = [ "reth-transaction-pool", "revm", "sha2 0.10.8", - "thiserror 2.0.9", + "thiserror 2.0.11", "tracing", ] @@ -8418,7 +8375,6 @@ dependencies = [ "bytes", "derive_more", "modular-bitfield", - "once_cell", "op-alloy-consensus", "proptest", "proptest-arbitrary-interop", @@ -8473,7 +8429,7 @@ dependencies = [ "reth-transaction-pool", "revm", "serde_json", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", "tracing", ] @@ -8540,7 +8496,7 @@ dependencies = [ "reth-primitives", "revm-primitives", "serde", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", ] @@ -8572,9 +8528,7 @@ dependencies = [ "alloy-genesis", "alloy-primitives", "alloy-rlp", - "alloy-trie", "arbitrary", - "assert_matches", "c-kzg", "codspeed-criterion-compat", "derive_more", @@ -8582,13 +8536,11 @@ dependencies = [ "pprof", "proptest", "proptest-arbitrary-interop", - "reth-chainspec", "reth-codecs", "reth-ethereum-forks", "reth-ethereum-primitives", "reth-primitives-traits", "reth-static-file-types", - "reth-trie-common", "serde", "serde_json", ] @@ -8617,6 +8569,7 @@ dependencies = [ "proptest-arbitrary-interop", "rand 0.8.5", "rayon", + "reth-chainspec", "reth-codecs", "revm-primitives", "secp256k1", @@ -8624,7 +8577,7 @@ dependencies = [ "serde_json", "serde_with", "test-fuzz", - "thiserror 2.0.9", + "thiserror 2.0.11", ] [[package]] @@ -8703,7 +8656,7 @@ dependencies = [ "reth-tokio-util", "reth-tracing", "rustc-hash 2.1.0", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", "tracing", ] @@ -8723,7 +8676,7 @@ dependencies = [ "serde", "serde_json", "test-fuzz", - "thiserror 2.0.9", + "thiserror 2.0.11", "toml", ] @@ -8735,9 +8688,7 @@ dependencies = [ "alloy-eips", "alloy-primitives", "reth-ethereum-forks", - "reth-execution-errors", "reth-primitives", - "reth-primitives-traits", "reth-prune-types", "reth-storage-api", "reth-storage-errors", @@ -8808,7 +8759,7 @@ dependencies = [ "revm-primitives", "serde", "serde_json", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", "tokio-stream", "tower 0.4.13", @@ -8901,7 +8852,7 @@ dependencies = [ "reth-transaction-pool", "serde", "serde_json", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", "tokio-util", "tower 0.4.13", @@ -8931,6 +8882,7 @@ dependencies = [ "reth-payload-builder-primitives", "reth-payload-primitives", "reth-primitives", + "reth-primitives-traits", "reth-provider", "reth-rpc-api", "reth-rpc-types-compat", @@ -8940,7 +8892,7 @@ dependencies = [ "reth-tokio-util", "reth-transaction-pool", "serde", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", "tracing", ] @@ -9025,7 +8977,7 @@ dependencies = [ "schnellru", "serde", "serde_json", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", "tokio-stream", "tracing", @@ -9089,6 +9041,7 @@ dependencies = [ "alloy-rlp", "assert_matches", "bincode", + "blake3", "codspeed-criterion-compat", "futures-util", "itertools 0.13.0", @@ -9097,6 +9050,7 @@ dependencies = [ "pprof", "rand 0.8.5", "rayon", + "reqwest", "reth-chainspec", "reth-codecs", "reth-config", @@ -9110,6 +9064,7 @@ dependencies = [ "reth-execution-errors", "reth-execution-types", "reth-exex", + "reth-fs-util", "reth-network-p2p", "reth-network-peers", "reth-primitives", @@ -9122,10 +9077,12 @@ dependencies = [ "reth-static-file", "reth-storage-errors", "reth-testing-utils", + "reth-tracing", "reth-trie", "reth-trie-db", + "serde", "tempfile", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", "tracing", ] @@ -9153,7 +9110,7 @@ dependencies = [ "reth-static-file-types", "reth-testing-utils", "reth-tokio-util", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", "tokio-stream", "tracing", @@ -9246,8 +9203,9 @@ dependencies = [ "derive_more", "reth-fs-util", "reth-primitives-traits", + "reth-prune-types", "reth-static-file-types", - "thiserror 2.0.9", + "thiserror 2.0.11", ] [[package]] @@ -9261,7 +9219,7 @@ dependencies = [ "pin-project", "rayon", "reth-metrics", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", "tracing", "tracing-futures", @@ -9315,7 +9273,7 @@ dependencies = [ "aquamarine", "assert_matches", "auto_impl", - "bitflags 2.6.0", + "bitflags 2.8.0", "codspeed-criterion-compat", "futures-util", "metrics", @@ -9346,7 +9304,7 @@ dependencies = [ "serde_json", "smallvec", "tempfile", - "thiserror 2.0.9", + "thiserror 2.0.11", "tokio", "tokio-stream", "tracing", @@ -9461,7 +9419,7 @@ dependencies = [ "reth-trie", "reth-trie-common", "reth-trie-db", - "thiserror 2.0.9", + "thiserror 2.0.11", "tracing", ] @@ -9481,12 +9439,15 @@ dependencies = [ "rand 0.8.5", "reth-execution-errors", "reth-primitives-traits", + "reth-provider", + "reth-storage-api", "reth-testing-utils", "reth-tracing", "reth-trie", "reth-trie-common", + "reth-trie-db", "smallvec", - "thiserror 2.0.9", + "thiserror 2.0.11", ] [[package]] @@ -9528,7 +9489,7 @@ dependencies = [ "colorchoice", "revm", "serde_json", - "thiserror 2.0.9", + "thiserror 2.0.11", ] [[package]] @@ -9571,7 +9532,7 @@ dependencies = [ "alloy-eip7702", "alloy-primitives", "auto_impl", - "bitflags 2.6.0", + "bitflags 2.8.0", "bitvec", "c-kzg", "cfg-if", @@ -9673,9 +9634,9 @@ dependencies = [ [[package]] name = "roaring" -version = "0.10.9" +version = "0.10.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41589aba99537475bf697f2118357cad1c31590c5a1b9f6d9fc4ad6d07503661" +checksum = "a652edd001c53df0b3f96a36a8dc93fce6866988efc16808235653c6bcac8bf2" dependencies = [ "bytemuck", "byteorder", @@ -9722,25 +9683,23 @@ dependencies = [ "regex", "relative-path", "rustc_version 0.4.1", - "syn 2.0.94", + "syn 2.0.96", "unicode-ident", ] [[package]] name = "ruint" -version = "1.12.4" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5ef8fb1dd8de3870cb8400d51b4c2023854bbafd5431a3ac7e7317243e22d2f" +checksum = "2c3cc4c2511671f327125da14133d0c5c5d137f006a1017a16f557bc85b16286" dependencies = [ "alloy-rlp", "arbitrary", "ark-ff 0.3.0", "ark-ff 0.4.2", "bytes", - "fastrlp 0.3.1", - "fastrlp 0.4.0", + "fastrlp", "num-bigint", - "num-integer", "num-traits", "parity-scale-codec", "primitive-types", @@ -9801,16 +9760,16 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver 1.0.24", + "semver 1.0.25", ] [[package]] name = "rustix" -version = "0.38.42" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "errno", "libc", "linux-raw-sys", @@ -9819,9 +9778,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.20" +version = "0.23.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" +checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8" dependencies = [ "log", "once_cell", @@ -9854,7 +9813,7 @@ dependencies = [ "openssl-probe", "rustls-pki-types", "schannel", - "security-framework 3.1.0", + "security-framework 3.2.0", ] [[package]] @@ -9868,9 +9827,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" dependencies = [ "web-time", ] @@ -10040,7 +9999,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -10050,11 +10009,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81d3f8c9bfcc3cbb6b0179eb57042d75b1582bdc65c3cb95f3fa999509c03cbc" +checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "core-foundation 0.10.0", "core-foundation-sys", "libc", @@ -10063,9 +10022,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.13.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", "libc", @@ -10082,9 +10041,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" +checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" dependencies = [ "serde", ] @@ -10127,16 +10086,16 @@ checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] name = "serde_json" -version = "1.0.134" +version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" +checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b" dependencies = [ - "indexmap 2.7.0", + "indexmap 2.7.1", "itoa", "memchr", "ryu", @@ -10154,17 +10113,6 @@ dependencies = [ "thiserror 1.0.69", ] -[[package]] -name = "serde_repr" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.94", -] - [[package]] name = "serde_spanned" version = "0.6.8" @@ -10196,7 +10144,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.7.0", + "indexmap 2.7.1", "serde", "serde_derive", "serde_json", @@ -10213,7 +10161,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -10246,7 +10194,7 @@ checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -10370,9 +10318,9 @@ dependencies = [ [[package]] name = "similar" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" dependencies = [ "bstr", "unicode-segmentation", @@ -10380,9 +10328,9 @@ dependencies = [ [[package]] name = "similar-asserts" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfe85670573cd6f0fa97940f26e7e6601213c3b0555246c24234131f88c5709e" +checksum = "9f08357795f0d604ea7d7130f22c74b03838c959bdb14adde3142aab4d18a293" dependencies = [ "console", "serde", @@ -10391,21 +10339,21 @@ dependencies = [ [[package]] name = "simple_asn1" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" +checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" dependencies = [ "num-bigint", "num-traits", - "thiserror 1.0.69", + "thiserror 2.0.11", "time", ] [[package]] name = "siphasher" -version = "0.3.11" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] name = "sketches-ddsketch" @@ -10548,7 +10496,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -10572,9 +10520,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "symbolic-common" -version = "12.12.4" +version = "12.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd33e73f154e36ec223c18013f7064a2c120f1162fc086ac9933542def186b00" +checksum = "13a4dfe4bbeef59c1f32fc7524ae7c95b9e1de5e79a43ce1604e181081d71b0c" dependencies = [ "debugid", "memmap2", @@ -10584,9 +10532,9 @@ dependencies = [ [[package]] name = "symbolic-demangle" -version = "12.12.4" +version = "12.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e51191290147f071777e37fe111800bb82a9059f9c95b19d2dd41bfeddf477" +checksum = "98cf6a95abff97de4d7ff3473f33cacd38f1ddccad5c1feab435d6760300e3b6" dependencies = [ "cpp_demangle", "rustc-demangle", @@ -10606,9 +10554,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.94" +version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "987bc0be1cdea8b10216bd06e2ca407d40b9543468fafd3ddfb02f36e77f71f3" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", @@ -10617,14 +10565,14 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.8.18" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e89d8bf2768d277f40573c83a02a099e96d96dd3104e13ea676194e61ac4b0" +checksum = "b84e4d83a0a6704561302b917a932484e1cae2d8c6354c64be8b7bac1c1fe057" dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -10644,7 +10592,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -10722,7 +10670,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -10755,11 +10703,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.9" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" dependencies = [ - "thiserror-impl 2.0.9", + "thiserror-impl 2.0.11", ] [[package]] @@ -10770,18 +10718,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] name = "thiserror-impl" -version = "2.0.9" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -10914,9 +10862,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.42.0" +version = "1.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" +checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" dependencies = [ "backtrace", "bytes", @@ -10932,13 +10880,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -11021,7 +10969,7 @@ version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap 2.7.0", + "indexmap 2.7.1", "serde", "serde_spanned", "toml_datetime", @@ -11072,7 +11020,7 @@ checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" dependencies = [ "async-compression", "base64 0.22.1", - "bitflags 2.6.0", + "bitflags 2.8.0", "bytes", "futures-core", "futures-util", @@ -11139,7 +11087,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -11335,9 +11283,9 @@ checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-ident" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +checksum = "11cd88e12b17c6494200a9c1b683a04fcac9573ed74cd1b62aeb2727c5592243" [[package]] name = "unicode-segmentation" @@ -11434,18 +11382,18 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.11.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b" dependencies = [ "getrandom 0.2.15", ] [[package]] name = "valuable" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "vergen" @@ -11475,7 +11423,7 @@ checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -11526,34 +11474,35 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.49" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", @@ -11564,9 +11513,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -11574,22 +11523,25 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasm-streams" @@ -11620,9 +11572,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.76" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -11746,7 +11698,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -11757,7 +11709,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -11768,7 +11720,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -11779,7 +11731,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -11971,9 +11923,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.22" +version = "0.6.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39281189af81c07ec09db316b302a3e67bf9bd7cbf6c820b50e35fee9c2fa980" +checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" dependencies = [ "memchr", ] @@ -12054,7 +12006,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", "synstructure", ] @@ -12076,7 +12028,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -12096,7 +12048,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", "synstructure", ] @@ -12117,7 +12069,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] @@ -12139,7 +12091,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.94", + "syn 2.0.96", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index a112a20d2882..1eb3881edd9b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -263,7 +263,7 @@ unarray.opt-level = 3 [profile.hivetests] inherits = "test" lto = "thin" -opt-level = 0 +opt-level = 3 [profile.release] codegen-units = 16 @@ -289,13 +289,6 @@ codegen-units = 1 inherits = "release" lto = "fat" -[profile.reproducible] -inherits = "release" -debug = false -panic = "abort" -codegen-units = 1 -overflow-checks = true - [workspace.dependencies] # reth op-reth = { path = "crates/optimism/bin" } @@ -325,7 +318,7 @@ reth-downloaders = { path = "crates/net/downloaders" } reth-e2e-test-utils = { path = "crates/e2e-test-utils" } reth-ecies = { path = "crates/net/ecies" } reth-engine-local = { path = "crates/engine/local" } -reth-engine-primitives = { path = "crates/engine/primitives" } +reth-engine-primitives = { path = "crates/engine/primitives", default-features = false } reth-engine-tree = { path = "crates/engine/tree" } reth-engine-service = { path = "crates/engine/service" } reth-engine-util = { path = "crates/engine/util" } @@ -334,7 +327,7 @@ reth-eth-wire = { path = "crates/net/eth-wire" } reth-eth-wire-types = { path = "crates/net/eth-wire-types" } reth-ethereum-cli = { path = "crates/ethereum/cli" } reth-ethereum-consensus = { path = "crates/ethereum/consensus" } -reth-ethereum-engine-primitives = { path = "crates/ethereum/engine-primitives" } +reth-ethereum-engine-primitives = { path = "crates/ethereum/engine-primitives", default-features = false } reth-ethereum-forks = { path = "crates/ethereum-forks", default-features = false } reth-ethereum-payload-builder = { path = "crates/ethereum/payload" } reth-ethereum-primitives = { path = "crates/ethereum/primitives", default-features = false } @@ -470,11 +463,12 @@ alloy-transport-ipc = { version = "0.9.2", default-features = false } alloy-transport-ws = { version = "0.9.2", default-features = false } # op -op-alloy-rpc-types = { version = "0.9.0", default-features = false } -op-alloy-rpc-types-engine = { version = "0.9.0", default-features = false } -op-alloy-rpc-jsonrpsee = { version = "0.9.0", default-features = false } -op-alloy-network = { version = "0.9.0", default-features = false } -op-alloy-consensus = { version = "0.9.0", default-features = false } +op-alloy-rpc-types = { version = "0.9.6", default-features = false } +op-alloy-rpc-types-engine = { version = "0.9.6", default-features = false } +op-alloy-network = { version = "0.9.6", default-features = false } +op-alloy-consensus = { version = "0.9.6", default-features = false } +op-alloy-flz = { version = "0.9.6", default-features = false } +op-alloy-rpc-jsonrpsee = { version = "0.9.6", default-features = false } # misc aquamarine = "0.6" @@ -485,6 +479,7 @@ backon = { version = "1.2", default-features = false, features = [ ] } bincode = "1.3" bitflags = "2.4" +blake3 = "1.5.5" boyer-moore-magiclen = "0.2.16" bytes = { version = "1.5", default-features = false } cfg-if = "1.0" @@ -610,6 +605,12 @@ tikv-jemallocator = "0.6" tracy-client = "0.17.3" snmalloc-rs = { version = "0.3.7", features = ["build_cc"] } +# TODO: When we build for a windows target on an ubuntu runner, crunchy tries to +# get the wrong path, update this when the workflow has been updated +# +# See: https://github.com/eira-fransham/crunchy/issues/13 +crunchy = "=0.2.2" + # [patch.crates-io] # alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "5492e40" } # alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "5492e40" } diff --git a/bin/reth/src/commands/debug_cmd/build_block.rs b/bin/reth/src/commands/debug_cmd/build_block.rs index 88f2b322bca0..6455b728e67f 100644 --- a/bin/reth/src/commands/debug_cmd/build_block.rs +++ b/bin/reth/src/commands/debug_cmd/build_block.rs @@ -100,7 +100,7 @@ impl> Command { provider .block(best_number.into())? .expect("the header for the latest block is missing, database is corrupt") - .seal(best_hash), + .seal_unchecked(best_hash), )) } @@ -166,8 +166,8 @@ impl> Command { for tx_bytes in &self.transactions { debug!(target: "reth::cli", bytes = ?tx_bytes, "Decoding transaction"); let transaction = TransactionSigned::decode(&mut &Bytes::from_str(tx_bytes)?[..])? - .try_ecrecovered() - .ok_or_else(|| eyre::eyre!("failed to recover tx"))?; + .try_clone_into_recovered() + .map_err(|e| eyre::eyre!("failed to recover tx: {e}"))?; let encoded_length = match &transaction.transaction { Transaction::Eip4844(TxEip4844 { blob_versioned_hashes, .. }) => { diff --git a/crates/chain-state/src/in_memory.rs b/crates/chain-state/src/in_memory.rs index 6358370e2b5d..f4f0094f0d56 100644 --- a/crates/chain-state/src/in_memory.rs +++ b/crates/chain-state/src/in_memory.rs @@ -240,7 +240,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: ExecutedBlock) { + pub fn set_pending_block(&self, pending: ExecutedBlockWithTrieUpdates) { // 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); @@ -254,9 +254,10 @@ impl CanonicalInMemoryState { /// /// This removes all reorged blocks and appends the new blocks to the tracked chain and connects /// them to their parent blocks. - fn update_blocks(&self, new_blocks: I, reorged: I) + fn update_blocks(&self, new_blocks: I, reorged: R) where - I: IntoIterator>, + I: IntoIterator>, + R: IntoIterator>, { { // acquire locks, starting with the numbers lock @@ -544,18 +545,10 @@ impl CanonicalInMemoryState { } /// Returns [`SignedTransaction`] type for the given `TxHash` if found. - pub fn transaction_by_hash(&self, hash: TxHash) -> Option - where - N::SignedTx: Encodable2718, - { + pub fn transaction_by_hash(&self, hash: TxHash) -> Option { for block_state in self.canonical_chain() { - if let Some(tx) = block_state - .block_ref() - .recovered_block() - .body() - .transactions() - .iter() - .find(|tx| tx.trie_hash() == hash) + if let Some(tx) = + block_state.block_ref().recovered_block().body().transaction_by_hash(&hash) { return Some(tx.clone()) } @@ -568,17 +561,13 @@ impl CanonicalInMemoryState { pub fn transaction_by_hash_with_meta( &self, tx_hash: TxHash, - ) -> Option<(N::SignedTx, TransactionMeta)> - where - N::SignedTx: Encodable2718, - { + ) -> Option<(N::SignedTx, TransactionMeta)> { for block_state in self.canonical_chain() { if let Some((index, tx)) = block_state .block_ref() .recovered_block() .body() - .transactions() - .iter() + .transactions_iter() .enumerate() .find(|(_, tx)| tx.trie_hash() == tx_hash) { @@ -603,7 +592,7 @@ impl CanonicalInMemoryState { #[derive(Debug, PartialEq, Eq, Clone)] pub struct BlockState { /// The executed block that determines the state after this block has been executed. - block: ExecutedBlock, + block: ExecutedBlockWithTrieUpdates, /// The block's parent block if it exists. parent: Option>>, } @@ -611,12 +600,15 @@ pub struct BlockState { #[allow(dead_code)] impl BlockState { /// [`BlockState`] constructor. - pub const fn new(block: ExecutedBlock) -> Self { + pub const fn new(block: ExecutedBlockWithTrieUpdates) -> Self { Self { block, parent: None } } /// [`BlockState`] constructor with parent. - pub const fn with_parent(block: ExecutedBlock, parent: Option>) -> Self { + pub const fn with_parent( + block: ExecutedBlockWithTrieUpdates, + parent: Option>, + ) -> Self { Self { block, parent } } @@ -630,12 +622,12 @@ impl BlockState { } /// Returns the executed block that determines the state. - pub fn block(&self) -> ExecutedBlock { + pub fn block(&self) -> ExecutedBlockWithTrieUpdates { self.block.clone() } /// Returns a reference to the executed block that determines the state. - pub const fn block_ref(&self) -> &ExecutedBlock { + pub const fn block_ref(&self) -> &ExecutedBlockWithTrieUpdates { &self.block } @@ -673,13 +665,7 @@ impl BlockState { receipts.receipt_vec.len() ); - receipts - .receipt_vec - .first() - .map(|block_receipts| { - block_receipts.iter().filter_map(|opt_receipt| opt_receipt.clone()).collect() - }) - .unwrap_or_default() + receipts.receipt_vec.first().cloned().unwrap_or_default() } /// Returns a vector of __parent__ `BlockStates`. @@ -738,19 +724,9 @@ impl BlockState { } /// Tries to find a transaction by [`TxHash`] in the chain ending at this block. - pub fn transaction_on_chain(&self, hash: TxHash) -> Option - where - N::SignedTx: Encodable2718, - { + pub fn transaction_on_chain(&self, hash: TxHash) -> Option { self.chain().find_map(|block_state| { - block_state - .block_ref() - .recovered_block() - .body() - .transactions() - .iter() - .find(|tx| tx.trie_hash() == hash) - .cloned() + block_state.block_ref().recovered_block().body().transaction_by_hash(&hash).cloned() }) } @@ -758,17 +734,13 @@ impl BlockState { pub fn transaction_meta_on_chain( &self, tx_hash: TxHash, - ) -> Option<(N::SignedTx, TransactionMeta)> - where - N::SignedTx: Encodable2718, - { + ) -> Option<(N::SignedTx, TransactionMeta)> { self.chain().find_map(|block_state| { block_state .block_ref() .recovered_block() .body() - .transactions() - .iter() + .transactions_iter() .enumerate() .find(|(_, tx)| tx.trie_hash() == tx_hash) .map(|(index, tx)| { @@ -791,7 +763,7 @@ impl BlockState { } /// Represents an executed block stored in-memory. -#[derive(Clone, Debug, PartialEq, Eq, Default)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct ExecutedBlock { /// Recovered Block pub recovered_block: Arc>, @@ -799,37 +771,81 @@ pub struct ExecutedBlock { pub execution_output: Arc>, /// Block's hashed state. pub hashed_state: Arc, - /// Trie updates that result of applying the block. - pub trie: Arc, +} + +impl Default for ExecutedBlock { + fn default() -> Self { + Self { + recovered_block: Default::default(), + execution_output: Default::default(), + hashed_state: Default::default(), + } + } } impl ExecutedBlock { - /// [`ExecutedBlock`] constructor. - pub const fn new( - recovered_block: Arc>, - execution_output: Arc>, - hashed_state: Arc, - trie: Arc, - ) -> Self { - Self { recovered_block, execution_output, hashed_state, trie } + /// Returns a reference to an inner [`SealedBlock`] + #[inline] + pub fn sealed_block(&self) -> &SealedBlock { + self.recovered_block.sealed_block() } /// Returns a reference to [`RecoveredBlock`] + #[inline] pub fn recovered_block(&self) -> &RecoveredBlock { &self.recovered_block } /// Returns a reference to the block's execution outcome + #[inline] pub fn execution_outcome(&self) -> &ExecutionOutcome { &self.execution_output } /// Returns a reference to the hashed state result of the execution outcome + #[inline] pub fn hashed_state(&self) -> &HashedPostState { &self.hashed_state } +} + +/// 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, + Default, + 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 of applying the block. + pub trie: Arc, +} + +impl ExecutedBlockWithTrieUpdates { + /// [`ExecutedBlock`] constructor. + pub const fn new( + recovered_block: Arc>, + execution_output: Arc>, + hashed_state: Arc, + trie: Arc, + ) -> Self { + Self { block: ExecutedBlock { recovered_block, execution_output, hashed_state }, trie } + } /// Returns a reference to the trie updates for the block + #[inline] pub fn trie_updates(&self) -> &TrieUpdates { &self.trie } @@ -841,14 +857,18 @@ 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>, }, } @@ -1224,7 +1244,7 @@ mod tests { #[test] fn test_state_receipts() { - let receipts = Receipts { receipt_vec: vec![vec![Some(Receipt::default())]] }; + let receipts = Receipts { receipt_vec: vec![vec![Receipt::default()]] }; let mut test_block_builder: TestBlockBuilder = TestBlockBuilder::default(); let block = test_block_builder.get_executed_block_with_receipts(receipts.clone(), B256::random()); @@ -1251,7 +1271,7 @@ mod tests { block1.recovered_block().hash() ); - let chain = NewCanonicalChain::Reorg { new: vec![block2.clone()], old: vec![block1] }; + let chain = NewCanonicalChain::Reorg { new: vec![block2.clone()], old: vec![block1.block] }; state.update_chain(chain); assert_eq!( state.head_state().unwrap().block_ref().recovered_block().hash(), @@ -1534,7 +1554,7 @@ mod tests { // Test reorg notification let chain_reorg = NewCanonicalChain::Reorg { new: vec![block1a.clone(), block2a.clone()], - old: vec![block1.clone(), block2.clone()], + old: vec![block1.block.clone(), block2.block.clone()], }; assert_eq!( diff --git a/crates/chain-state/src/memory_overlay.rs b/crates/chain-state/src/memory_overlay.rs index 51594890b8ac..38c384dcdb5c 100644 --- a/crates/chain-state/src/memory_overlay.rs +++ b/crates/chain-state/src/memory_overlay.rs @@ -1,4 +1,4 @@ -use super::ExecutedBlock; +use super::ExecutedBlockWithTrieUpdates; use alloy_consensus::BlockHeader; use alloy_primitives::{ keccak256, map::B256HashMap, Address, BlockNumber, Bytes, StorageKey, StorageValue, B256, @@ -23,7 +23,7 @@ pub struct MemoryOverlayStateProviderRef<'a, N: NodePrimitives = reth_primitives /// Historical state provider for state lookups that are not found in 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_state: OnceLock, } @@ -40,7 +40,10 @@ 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_state: OnceLock::new() } } diff --git a/crates/chain-state/src/notifications.rs b/crates/chain-state/src/notifications.rs index 97a4faaefa60..a113dd77db2d 100644 --- a/crates/chain-state/src/notifications.rs +++ b/crates/chain-state/src/notifications.rs @@ -334,7 +334,7 @@ mod tests { }; // Wrap the receipt in a `Receipts` structure, as expected in the `ExecutionOutcome`. - let receipts = Receipts { receipt_vec: vec![vec![Some(receipt1.clone())]] }; + let receipts = Receipts { receipt_vec: vec![vec![receipt1.clone()]] }; // Define an `ExecutionOutcome` with the created receipts. let execution_outcome = ExecutionOutcome { receipts, ..Default::default() }; @@ -393,7 +393,7 @@ mod tests { success: false, ..Default::default() }; - let old_receipts = Receipts { receipt_vec: vec![vec![Some(old_receipt.clone())]] }; + let old_receipts = Receipts { receipt_vec: vec![vec![old_receipt.clone()]] }; let old_execution_outcome = ExecutionOutcome { receipts: old_receipts, ..Default::default() }; @@ -424,7 +424,7 @@ mod tests { success: true, ..Default::default() }; - let new_receipts = Receipts { receipt_vec: vec![vec![Some(new_receipt.clone())]] }; + let new_receipts = Receipts { receipt_vec: vec![vec![new_receipt.clone()]] }; let new_execution_outcome = ExecutionOutcome { receipts: new_receipts, ..Default::default() }; diff --git a/crates/chain-state/src/test_utils.rs b/crates/chain-state/src/test_utils.rs index 1eb68e670eff..3c519d291ad8 100644 --- a/crates/chain-state/src/test_utils.rs +++ b/crates/chain-state/src/test_utils.rs @@ -1,7 +1,7 @@ use core::marker::PhantomData; use crate::{ - in_memory::ExecutedBlock, CanonStateNotification, CanonStateNotifications, + in_memory::ExecutedBlockWithTrieUpdates, CanonStateNotification, CanonStateNotifications, CanonStateSubscriptions, }; use alloy_consensus::{ @@ -18,12 +18,14 @@ use rand::{thread_rng, Rng}; use reth_chainspec::{ChainSpec, EthereumHardfork, MIN_TRANSACTION_GAS}; use reth_execution_types::{Chain, ExecutionOutcome}; use reth_primitives::{ + transaction::SignedTransactionIntoRecoveredExt, BlockBody, EthPrimitives, NodePrimitives, + Receipt, Receipts, Recovered, RecoveredBlock, SealedBlock, SealedHeader, Transaction, + TransactionSigned, +}; +use reth_primitives_traits::{ proofs::{calculate_receipt_root, calculate_transaction_root, calculate_withdrawals_root}, - transaction::SignedTransactionIntoRecoveredExt, - BlockBody, EthPrimitives, NodePrimitives, Receipt, Receipts, RecoveredBlock, RecoveredTx, - SealedBlock, SealedHeader, Transaction, TransactionSigned, + Account, }; -use reth_primitives_traits::Account; use reth_storage_api::NodePrimitivesProvider; use reth_trie::{root::state_root_unhashed, updates::TrieUpdates, HashedPostState}; use revm::{db::BundleState, primitives::AccountInfo}; @@ -97,7 +99,7 @@ impl TestBlockBuilder { ) -> RecoveredBlock { let mut rng = thread_rng(); - let mock_tx = |nonce: u64| -> RecoveredTx<_> { + let mock_tx = |nonce: u64| -> Recovered<_> { let tx = Transaction::Eip1559(TxEip1559 { chain_id: self.chain_spec.chain.id(), nonce, @@ -115,7 +117,7 @@ impl TestBlockBuilder { let num_txs = rng.gen_range(0..5); let signer_balance_decrease = Self::single_tx_cost() * U256::from(num_txs); - let transactions: Vec> = (0..num_txs) + let transactions: Vec> = (0..num_txs) .map(|_| { let tx = mock_tx(self.signer_build_account_info.nonce); self.signer_build_account_info.nonce += 1; @@ -202,17 +204,17 @@ impl TestBlockBuilder { fork } - /// Gets an [`ExecutedBlock`] with [`BlockNumber`], [`Receipts`] and parent hash. + /// Gets an [`ExecutedBlockWithTrieUpdates`] with [`BlockNumber`], [`Receipts`] and parent hash. fn get_executed_block( &mut self, block_number: BlockNumber, receipts: Receipts, parent_hash: B256, - ) -> ExecutedBlock { + ) -> ExecutedBlockWithTrieUpdates { let block_with_senders = self.generate_random_block(block_number, parent_hash); let (block, senders) = block_with_senders.split_sealed(); - ExecutedBlock::new( + ExecutedBlockWithTrieUpdates::new( Arc::new(RecoveredBlock::new_sealed(block, senders)), Arc::new(ExecutionOutcome::new( BundleState::default(), @@ -225,22 +227,22 @@ impl TestBlockBuilder { ) } - /// Generates an [`ExecutedBlock`] that includes the given [`Receipts`]. + /// Generates an [`ExecutedBlockWithTrieUpdates`] that includes the given [`Receipts`]. pub fn get_executed_block_with_receipts( &mut self, receipts: Receipts, parent_hash: B256, - ) -> ExecutedBlock { + ) -> ExecutedBlockWithTrieUpdates { let number = rand::thread_rng().gen::(); self.get_executed_block(number, receipts, parent_hash) } - /// Generates an [`ExecutedBlock`] with the given [`BlockNumber`]. + /// Generates an [`ExecutedBlockWithTrieUpdates`] with the given [`BlockNumber`]. pub fn get_executed_block_with_number( &mut self, block_number: BlockNumber, parent_hash: B256, - ) -> ExecutedBlock { + ) -> ExecutedBlockWithTrieUpdates { self.get_executed_block(block_number, Receipts { receipt_vec: vec![vec![]] }, parent_hash) } @@ -248,7 +250,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; @@ -294,7 +296,7 @@ impl TestBlockBuilder { let execution_outcome = ExecutionOutcome::new( bundle_state_builder.build(), - vec![vec![None]].into(), + vec![vec![]].into(), block.number, Vec::new(), ); diff --git a/crates/cli/commands/src/stage/run.rs b/crates/cli/commands/src/stage/run.rs index 254b5fe6483e..71f3c4b74e46 100644 --- a/crates/cli/commands/src/stage/run.rs +++ b/crates/cli/commands/src/stage/run.rs @@ -45,8 +45,7 @@ use reth_stages::{ IndexStorageHistoryStage, MerkleStage, SenderRecoveryStage, StorageHashingStage, TransactionLookupStage, }, - ExecInput, ExecOutput, ExecutionStageThresholds, Stage, StageError, StageExt, UnwindInput, - UnwindOutput, + ExecInput, ExecOutput, ExecutionStageThresholds, Stage, StageExt, UnwindInput, UnwindOutput, }; use std::{any::Any, net::SocketAddr, sync::Arc, time::Instant}; use tokio::sync::watch; @@ -190,16 +189,18 @@ impl // Use `to` as the tip for the stage let tip: P::BlockHeader = loop { - match fetch_client.get_header(BlockHashOrNumber::Number(self.to)).await { - Ok(header) => break header, - Err(error) if error.is_retryable() => { - warn!(target: "reth::cli", "Error requesting header: {error}. Retrying...") + match fetch_client.get_header(BlockHashOrNumber::Number(self.to)).await { + Ok(header) => { + if let Some(header) = header.into_data() { + break header } - Err(error) => return Err(error.into()), } + Err(error) if error.is_retryable() => { + warn!(target: "reth::cli", "Error requesting header: {error}. Retrying...") + } + Err(error) => return Err(error.into()), } - .into_data() - .ok_or(StageError::MissingSyncGap)?; + }; let (_, rx) = watch::channel(tip.hash_slow()); ( Box::new(HeaderStage::new( diff --git a/crates/cli/commands/src/test_vectors/compact.rs b/crates/cli/commands/src/test_vectors/compact.rs index 90aafee1e8ab..3d8e7b13f966 100644 --- a/crates/cli/commands/src/test_vectors/compact.rs +++ b/crates/cli/commands/src/test_vectors/compact.rs @@ -17,7 +17,10 @@ use reth_codecs::alloy::{ withdrawal::Withdrawal, }; use reth_db::{ - models::{AccountBeforeTx, StoredBlockBodyIndices, StoredBlockOmmers, StoredBlockWithdrawals}, + models::{ + AccountBeforeTx, StaticFileBlockWithdrawals, StoredBlockBodyIndices, StoredBlockOmmers, + StoredBlockWithdrawals, + }, ClientVersion, }; use reth_fs_util as fs; @@ -110,6 +113,7 @@ compact_types!( StoredBlockOmmers, StoredBlockBodyIndices, StoredBlockWithdrawals, + StaticFileBlockWithdrawals, // Manual implementations TransactionSigned, // Bytecode, // todo revm arbitrary diff --git a/crates/consensus/common/src/validation.rs b/crates/consensus/common/src/validation.rs index 530c508766b2..01bc36ac8c77 100644 --- a/crates/consensus/common/src/validation.rs +++ b/crates/consensus/common/src/validation.rs @@ -340,7 +340,8 @@ mod tests { use alloy_primitives::{Address, Bytes, PrimitiveSignature as Signature, U256}; use rand::Rng; use reth_chainspec::ChainSpecBuilder; - use reth_primitives::{proofs, BlockBody, Transaction, TransactionSigned}; + use reth_primitives::{BlockBody, Transaction, TransactionSigned}; + use reth_primitives_traits::proofs; fn mock_blob_tx(nonce: u64, num_blobs: usize) -> TransactionSigned { let mut rng = rand::thread_rng(); diff --git a/crates/e2e-test-utils/src/lib.rs b/crates/e2e-test-utils/src/lib.rs index 091e871844bd..609205a846d6 100644 --- a/crates/e2e-test-utils/src/lib.rs +++ b/crates/e2e-test-utils/src/lib.rs @@ -13,7 +13,7 @@ use reth_node_builder::{ PayloadTypes, }; use reth_node_core::args::{DiscoveryArgs, NetworkArgs, RpcServerArgs}; -use reth_provider::providers::{BlockchainProvider, NodeTypesForProvider, NodeTypesForTree}; +use reth_provider::providers::{BlockchainProvider, NodeTypesForProvider}; use reth_rpc_server_types::RpcModuleSelection; use reth_tasks::TaskManager; use std::sync::Arc; @@ -51,7 +51,7 @@ pub async fn setup( attributes_generator: impl Fn(u64) -> <::Engine as PayloadTypes>::PayloadBuilderAttributes + Copy + 'static, ) -> eyre::Result<(Vec>, TaskManager, Wallet)> where - N: Default + Node> + NodeTypesForTree + NodeTypesWithEngine, + N: Default + Node> + NodeTypesForProvider + NodeTypesWithEngine, N::ComponentsBuilder: NodeComponentsBuilder< TmpNodeAdapter, Components: NodeComponents, Network: PeersHandleProvider>, diff --git a/crates/e2e-test-utils/src/node.rs b/crates/e2e-test-utils/src/node.rs index e21b85e78a11..63aaf1bd19a6 100644 --- a/crates/e2e-test-utils/src/node.rs +++ b/crates/e2e-test-utils/src/node.rs @@ -11,7 +11,7 @@ use eyre::Ok; use futures_util::Future; use reth_chainspec::EthereumHardforks; use reth_network_api::test_utils::PeersHandleProvider; -use reth_node_api::{Block, BlockBody, BlockTy, EngineTypes, FullNodeComponents}; +use reth_node_api::{Block, BlockBody, BlockTy, EngineTypes, FullNodeComponents, PrimitivesTy}; use reth_node_builder::{rpc::RethRpcAddOns, FullNode, NodeTypes, NodeTypesWithEngine}; use reth_node_core::primitives::SignedTransaction; use reth_payload_primitives::{BuiltPayload, PayloadBuilderAttributes}; @@ -41,7 +41,7 @@ where pub engine_api: EngineApiTestContext< ::Engine, ::ChainSpec, - ::Primitives, + PrimitivesTy, >, /// Context for testing RPC features. pub rpc: RpcTestContext, diff --git a/crates/engine/invalid-block-hooks/src/witness.rs b/crates/engine/invalid-block-hooks/src/witness.rs index b7e3b4f19126..ad5ceb906392 100644 --- a/crates/engine/invalid-block-hooks/src/witness.rs +++ b/crates/engine/invalid-block-hooks/src/witness.rs @@ -1,19 +1,17 @@ use alloy_consensus::BlockHeader; use alloy_primitives::{keccak256, B256}; use alloy_rpc_types_debug::ExecutionWitness; -use eyre::OptionExt; use pretty_assertions::Comparison; use reth_chainspec::{EthChainSpec, EthereumHardforks}; use reth_engine_primitives::InvalidBlockHook; use reth_evm::{ - state_change::post_block_balance_increments, system_calls::SystemCaller, ConfigureEvm, + state_change::post_block_balance_increments, system_calls::SystemCaller, ConfigureEvmFor, Evm, }; use reth_primitives::{NodePrimitives, RecoveredBlock, SealedHeader}; use reth_primitives_traits::{BlockBody, SignedTransaction}; use reth_provider::{BlockExecutionOutput, ChainSpecProvider, StateProviderFactory}; use reth_revm::{ - database::StateProviderDatabase, db::states::bundle_state::BundleRetention, DatabaseCommit, - StateBuilder, + database::StateProviderDatabase, db::states::bundle_state::BundleRetention, StateBuilder, }; use reth_rpc_api::DebugApiClient; use reth_tracing::tracing::warn; @@ -64,7 +62,7 @@ where ) -> eyre::Result<()> where N: NodePrimitives, - EvmConfig: ConfigureEvm
, + EvmConfig: ConfigureEvmFor, { // TODO(alexey): unify with `DebugApi::debug_execution_witness` @@ -88,13 +86,9 @@ where // Re-execute all of the transactions in the block to load all touched accounts into // the cache DB. for tx in block.body().transactions() { - self.evm_config.fill_tx_env( - evm.tx_mut(), - tx, - tx.recover_signer().ok_or_eyre("failed to recover sender")?, - ); - let result = evm.transact()?; - evm.db_mut().commit(result.state); + let signer = + tx.recover_signer().map_err(|_| eyre::eyre!("failed to recover sender"))?; + evm.transact_commit(self.evm_config.tx_env(tx, signer))?; } drop(evm); @@ -295,7 +289,7 @@ where + Send + Sync + 'static, - EvmConfig: ConfigureEvm
, + EvmConfig: ConfigureEvmFor, { fn on_invalid_block( &self, diff --git a/crates/engine/local/src/miner.rs b/crates/engine/local/src/miner.rs index 447e89a00e2b..48cf735b87e2 100644 --- a/crates/engine/local/src/miner.rs +++ b/crates/engine/local/src/miner.rs @@ -5,10 +5,12 @@ use alloy_primitives::{TxHash, B256}; use alloy_rpc_types_engine::ForkchoiceState; use eyre::OptionExt; use futures_util::{stream::Fuse, StreamExt}; -use reth_engine_primitives::{BeaconEngineMessage, EngineApiMessageVersion, EngineTypes}; +use reth_engine_primitives::{BeaconEngineMessage, EngineTypes}; use reth_payload_builder::PayloadBuilderHandle; use reth_payload_builder_primitives::PayloadBuilder; -use reth_payload_primitives::{BuiltPayload, PayloadAttributesBuilder, PayloadKind, PayloadTypes}; +use reth_payload_primitives::{ + BuiltPayload, EngineApiMessageVersion, PayloadAttributesBuilder, PayloadKind, PayloadTypes, +}; use reth_provider::BlockReader; use reth_transaction_pool::TransactionPool; use std::{ diff --git a/crates/engine/primitives/Cargo.toml b/crates/engine/primitives/Cargo.toml index 2da1be9c928e..4a475c558b60 100644 --- a/crates/engine/primitives/Cargo.toml +++ b/crates/engine/primitives/Cargo.toml @@ -24,6 +24,7 @@ reth-errors.workspace = true alloy-primitives.workspace = true alloy-consensus.workspace = true alloy-rpc-types-engine.workspace = true +alloy-eips.workspace = true # async tokio = { workspace = true, features = ["sync"] } @@ -32,3 +33,18 @@ futures.workspace = true # misc serde.workspace = true thiserror.workspace = true + +[features] +default = ["std"] +std = [ + "reth-execution-types/std", + "reth-primitives/std", + "reth-primitives-traits/std", + "alloy-primitives/std", + "alloy-consensus/std", + "alloy-rpc-types-engine/std", + "futures/std", + "serde/std", + "thiserror/std", + "alloy-eips/std" +] diff --git a/crates/engine/primitives/src/error.rs b/crates/engine/primitives/src/error.rs index 18e72fe83e72..7d4010cbcefe 100644 --- a/crates/engine/primitives/src/error.rs +++ b/crates/engine/primitives/src/error.rs @@ -1,3 +1,4 @@ +use alloc::boxed::Box; use alloy_rpc_types_engine::ForkchoiceUpdateError; /// Represents all error cases when handling a new payload. diff --git a/crates/engine/primitives/src/event.rs b/crates/engine/primitives/src/event.rs index 20b41d414099..1afed370d1bf 100644 --- a/crates/engine/primitives/src/event.rs +++ b/crates/engine/primitives/src/event.rs @@ -1,16 +1,16 @@ //! Events emitted by the beacon consensus engine. use crate::ForkchoiceStatus; +use alloc::{boxed::Box, sync::Arc}; use alloy_consensus::BlockHeader; use alloy_primitives::B256; use alloy_rpc_types_engine::ForkchoiceState; -use reth_primitives::{EthPrimitives, SealedBlock}; -use reth_primitives_traits::{NodePrimitives, SealedHeader}; -use std::{ +use core::{ fmt::{Display, Formatter, Result}, - sync::Arc, time::Duration, }; +use reth_primitives::{EthPrimitives, SealedBlock}; +use reth_primitives_traits::{NodePrimitives, SealedHeader}; /// Events emitted by the consensus engine. #[derive(Clone, Debug)] diff --git a/crates/engine/primitives/src/lib.rs b/crates/engine/primitives/src/lib.rs index 1def1a67e2a5..edf4896ad4a0 100644 --- a/crates/engine/primitives/src/lib.rs +++ b/crates/engine/primitives/src/lib.rs @@ -7,7 +7,11 @@ )] #![cfg_attr(not(test), warn(unused_crate_dependencies))] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +#![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + +use reth_payload_primitives::{BuiltPayload, PayloadAttributes}; mod error; use core::fmt; @@ -28,15 +32,16 @@ pub use event::*; mod invalid_block_hook; pub use invalid_block_hook::InvalidBlockHook; -pub use reth_payload_primitives::{ - BuiltPayload, EngineApiMessageVersion, EngineObjectValidationError, PayloadOrAttributes, - PayloadTypes, +use reth_payload_primitives::{ + validate_execution_requests, EngineApiMessageVersion, EngineObjectValidationError, + InvalidPayloadAttributesError, PayloadOrAttributes, PayloadTypes, }; -use reth_payload_primitives::{InvalidPayloadAttributesError, PayloadAttributes}; use reth_primitives::{NodePrimitives, SealedBlock}; use reth_primitives_traits::Block; use serde::{de::DeserializeOwned, ser::Serialize}; +use alloy_eips::eip7685::Requests; + /// This type defines the versioned types of the engine API. /// /// This includes the execution payload types and payload attributes that are used to trigger a @@ -114,6 +119,14 @@ pub trait PayloadValidator: fmt::Debug + Send + Sync + Unpin + 'static { /// Type that validates the payloads processed by the engine. pub trait EngineValidator: PayloadValidator { + /// Validates the execution requests according to [EIP-7685](https://eips.ethereum.org/EIPS/eip-7685). + fn validate_execution_requests( + &self, + requests: &Requests, + ) -> Result<(), EngineObjectValidationError> { + validate_execution_requests(requests) + } + /// Validates the presence or exclusion of fork-specific fields based on the payload attributes /// and the message version. fn validate_version_specific_fields( diff --git a/crates/engine/primitives/src/message.rs b/crates/engine/primitives/src/message.rs index 6e4f4629276b..d055d4e424fa 100644 --- a/crates/engine/primitives/src/message.rs +++ b/crates/engine/primitives/src/message.rs @@ -6,15 +6,15 @@ use alloy_rpc_types_engine::{ ExecutionPayload, ExecutionPayloadSidecar, ForkChoiceUpdateResult, ForkchoiceState, ForkchoiceUpdateError, ForkchoiceUpdated, PayloadId, PayloadStatus, PayloadStatusEnum, }; -use futures::{future::Either, FutureExt, TryFutureExt}; -use reth_errors::RethResult; -use reth_payload_builder_primitives::PayloadBuilderError; -use std::{ - fmt::Display, +use core::{ + fmt::{self, Display}, future::Future, pin::Pin, task::{ready, Context, Poll}, }; +use futures::{future::Either, FutureExt, TryFutureExt}; +use reth_errors::RethResult; +use reth_payload_builder_primitives::PayloadBuilderError; use tokio::sync::{mpsc::UnboundedSender, oneshot}; /// Represents the outcome of forkchoice update. @@ -168,7 +168,7 @@ pub enum BeaconEngineMessage { } impl Display for BeaconEngineMessage { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::NewPayload { payload, .. } => { write!( diff --git a/crates/engine/tree/Cargo.toml b/crates/engine/tree/Cargo.toml index 76ce5a7ac5bb..2b08f0917c73 100644 --- a/crates/engine/tree/Cargo.toml +++ b/crates/engine/tree/Cargo.toml @@ -34,8 +34,6 @@ reth-trie-db.workspace = true reth-trie-parallel.workspace = true reth-trie-sparse.workspace = true reth-trie.workspace = true -# TODO(mattsse): get rid of this by optimizing cache -reth-network.workspace = true # alloy alloy-consensus.workspace = true @@ -126,6 +124,5 @@ test-utils = [ "reth-trie-sparse/test-utils", "reth-prune-types?/test-utils", "reth-trie-db/test-utils", - "reth-trie-parallel/test-utils", - "reth-network/test-utils" + "reth-trie-parallel/test-utils" ] diff --git a/crates/engine/tree/benches/state_root_task.rs b/crates/engine/tree/benches/state_root_task.rs index 8c5b871385ab..1d9faf7a7154 100644 --- a/crates/engine/tree/benches/state_root_task.rs +++ b/crates/engine/tree/benches/state_root_task.rs @@ -14,11 +14,7 @@ use reth_provider::{ test_utils::{create_test_provider_factory, MockNodeTypesWithDB}, AccountReader, HashingWriter, ProviderFactory, }; -use reth_trie::{ - hashed_cursor::HashedPostStateCursorFactory, proof::ProofBlindedProviderFactory, - trie_cursor::InMemoryTrieCursorFactory, TrieInput, -}; -use reth_trie_db::{DatabaseHashedCursorFactory, DatabaseTrieCursorFactory}; +use reth_trie::TrieInput; use revm_primitives::{ Account as RevmAccount, AccountInfo, AccountStatus, Address, EvmState, EvmStorageSlot, HashMap, B256, KECCAK_EMPTY, U256, @@ -210,10 +206,6 @@ fn bench_state_root(c: &mut Criterion) { ConsistentDbView::new(factory, None), trie_input, ); - let provider = config.consistent_view.provider_ro().unwrap(); - let nodes_sorted = config.nodes_sorted.clone(); - let state_sorted = config.state_sorted.clone(); - let prefix_sets = config.prefix_sets.clone(); let num_threads = std::thread::available_parallelism() .map_or(1, |num| (num.get() / 2).max(1)); @@ -225,45 +217,13 @@ fn bench_state_root(c: &mut Criterion) { .expect("Failed to create proof worker thread pool"), ); - ( - config, - state_updates, - provider, - nodes_sorted, - state_sorted, - prefix_sets, - state_root_task_pool, - ) + (config, state_updates, state_root_task_pool) }, - |( - config, - state_updates, - provider, - nodes_sorted, - state_sorted, - prefix_sets, - state_root_task_pool, - )| { - let blinded_provider_factory = ProofBlindedProviderFactory::new( - InMemoryTrieCursorFactory::new( - DatabaseTrieCursorFactory::new(provider.tx_ref()), - &nodes_sorted, - ), - HashedPostStateCursorFactory::new( - DatabaseHashedCursorFactory::new(provider.tx_ref()), - &state_sorted, - ), - prefix_sets, - ); - - black_box(std::thread::scope(|scope| { - let task = StateRootTask::new( - config, - blinded_provider_factory, - state_root_task_pool, - ); + |(config, state_updates, state_root_task_pool)| { + black_box({ + let task = StateRootTask::new(config, state_root_task_pool); let mut hook = task.state_hook(); - let handle = task.spawn(scope); + let handle = task.spawn(); for update in state_updates { hook.on_state(&update) @@ -271,7 +231,7 @@ fn bench_state_root(c: &mut Criterion) { drop(hook); handle.wait_for_result().expect("task failed") - })); + }); }, ) }, diff --git a/crates/engine/tree/src/engine.rs b/crates/engine/tree/src/engine.rs index 1e721627becf..2d6f66d12fb6 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::ExecutedBlock; +use reth_chain_state::ExecutedBlockWithTrieUpdates; use reth_engine_primitives::{BeaconConsensusEngineEvent, BeaconEngineMessage, EngineTypes}; use reth_primitives::{NodePrimitives, RecoveredBlock}; use reth_primitives_traits::Block; @@ -245,7 +245,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(ExecutedBlock), + InsertExecutedBlock(ExecutedBlockWithTrieUpdates), } impl Display for EngineApiRequest { diff --git a/crates/engine/tree/src/persistence.rs b/crates/engine/tree/src/persistence.rs index 5c9d1357d2c7..ffe401cc04c4 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::ExecutedBlock; +use reth_chain_state::ExecutedBlockWithTrieUpdates; use reth_errors::ProviderError; use reth_primitives::{EthPrimitives, NodePrimitives}; use reth_provider::{ @@ -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(); @@ -181,7 +181,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. /// @@ -258,7 +258,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/tree/block_buffer.rs b/crates/engine/tree/src/tree/block_buffer.rs index 0d022f32de0c..607fba21944a 100644 --- a/crates/engine/tree/src/tree/block_buffer.rs +++ b/crates/engine/tree/src/tree/block_buffer.rs @@ -1,9 +1,9 @@ use crate::tree::metrics::BlockBufferMetrics; use alloy_consensus::BlockHeader; use alloy_primitives::{BlockHash, BlockNumber}; -use reth_network::cache::LruCache; use reth_primitives::RecoveredBlock; use reth_primitives_traits::Block; +use schnellru::{ByLength, LruMap}; use std::collections::{BTreeMap, HashMap, HashSet}; /// Contains the tree of pending blocks that cannot be executed due to missing parent. @@ -32,7 +32,7 @@ pub(super) struct BlockBuffer { /// first in line for evicting if `max_blocks` limit is hit. /// /// Used as counter of amount of blocks inside buffer. - pub(crate) lru: LruCache, + pub(crate) lru: LruMap, /// Various metrics for the block buffer. pub(crate) metrics: BlockBufferMetrics, } @@ -44,7 +44,7 @@ impl BlockBuffer { blocks: Default::default(), parent_to_child: Default::default(), earliest_blocks: Default::default(), - lru: LruCache::new(limit), + lru: LruMap::new(ByLength::new(limit)), metrics: Default::default(), } } @@ -71,7 +71,7 @@ impl BlockBuffer { self.earliest_blocks.entry(block.number()).or_default().insert(hash); self.blocks.insert(hash, block); - if let (_, Some(evicted_hash)) = self.lru.insert_and_get_evicted(hash) { + if let Some(evicted_hash) = self.insert_hash_and_get_evicted(hash) { // evict the block if limit is hit if let Some(evicted_block) = self.remove_block(&evicted_hash) { // evict the block if limit is hit @@ -81,6 +81,18 @@ impl BlockBuffer { self.metrics.blocks.set(self.blocks.len() as f64); } + /// Inserts the hash and returns the oldest evicted hash if any. + fn insert_hash_and_get_evicted(&mut self, entry: BlockHash) -> Option { + let new = self.lru.peek(&entry).is_none(); + let evicted = if new && self.lru.limiter().max_length() as usize <= self.lru.len() { + self.lru.pop_oldest().map(|(k, ())| k) + } else { + None + }; + self.lru.get_or_insert(entry, || ()); + evicted + } + /// Removes the given block from the buffer and also all the children of the block. /// /// This is used to get all the blocks that are dependent on the block that is included. diff --git a/crates/engine/tree/src/tree/mod.rs b/crates/engine/tree/src/tree/mod.rs index 059fde3d7414..d519307c0040 100644 --- a/crates/engine/tree/src/tree/mod.rs +++ b/crates/engine/tree/src/tree/mod.rs @@ -21,20 +21,23 @@ use alloy_rpc_types_engine::{ use block_buffer::BlockBuffer; use error::{InsertBlockError, InsertBlockErrorKind, InsertBlockFatalError}; use reth_chain_state::{ - CanonicalInMemoryState, ExecutedBlock, MemoryOverlayStateProvider, NewCanonicalChain, + CanonicalInMemoryState, ExecutedBlock, ExecutedBlockWithTrieUpdates, + MemoryOverlayStateProvider, NewCanonicalChain, }; use reth_consensus::{Consensus, FullConsensus, PostExecutionInput}; pub use reth_engine_primitives::InvalidBlockHook; use reth_engine_primitives::{ - BeaconConsensusEngineEvent, BeaconEngineMessage, BeaconOnNewPayloadError, - EngineApiMessageVersion, EngineTypes, EngineValidator, ForkchoiceStateTracker, - OnForkChoiceUpdated, + BeaconConsensusEngineEvent, BeaconEngineMessage, BeaconOnNewPayloadError, EngineTypes, + EngineValidator, ForkchoiceStateTracker, OnForkChoiceUpdated, }; use reth_errors::{ConsensusError, ProviderResult}; -use reth_evm::{execute::BlockExecutorProvider, system_calls::OnStateHook}; +use reth_evm::{ + execute::BlockExecutorProvider, + system_calls::{NoopHook, OnStateHook}, +}; use reth_payload_builder::PayloadBuilderHandle; use reth_payload_builder_primitives::PayloadBuilder; -use reth_payload_primitives::PayloadBuilderAttributes; +use reth_payload_primitives::{EngineApiMessageVersion, PayloadBuilderAttributes}; use reth_primitives::{ EthPrimitives, GotExpected, NodePrimitives, RecoveredBlock, SealedBlock, SealedHeader, }; @@ -47,17 +50,11 @@ use reth_provider::{ use reth_revm::database::StateProviderDatabase; use reth_stages_api::ControlFlow; use reth_trie::{ - hashed_cursor::HashedPostStateCursorFactory, - prefix_set::TriePrefixSetsMut, - proof::ProofBlindedProviderFactory, - trie_cursor::InMemoryTrieCursorFactory, - updates::{TrieUpdates, TrieUpdatesSorted}, - HashedPostState, HashedPostStateSorted, TrieInput, + trie_cursor::InMemoryTrieCursorFactory, updates::TrieUpdates, HashedPostState, TrieInput, }; -use reth_trie_db::{DatabaseHashedCursorFactory, DatabaseTrieCursorFactory}; +use reth_trie_db::DatabaseTrieCursorFactory; use reth_trie_parallel::root::{ParallelStateRoot, ParallelStateRootError}; -use revm_primitives::EvmState; -use root::{StateRootComputeOutcome, StateRootConfig, StateRootTask}; +use root::{StateRootComputeOutcome, StateRootConfig, StateRootHandle, StateRootTask}; use std::{ cmp::Ordering, collections::{btree_map, hash_map, BTreeMap, VecDeque}, @@ -67,7 +64,7 @@ use std::{ mpsc::{Receiver, RecvError, RecvTimeoutError, Sender}, Arc, }, - time::Instant, + time::{Duration, Instant}, }; use tokio::sync::{ mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, @@ -106,13 +103,13 @@ pub struct TreeState { /// __All__ unique executed blocks by block hash that are connected to the canonical chain. /// /// This includes blocks of all forks. - blocks_by_hash: HashMap>, + 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. - blocks_by_number: BTreeMap>>, + blocks_by_number: BTreeMap>>, /// Map of any parent block hash to its children. parent_to_child: HashMap>, /// Map of hash to trie updates for canonical blocks that are persisted but not finalized. @@ -140,8 +137,8 @@ impl TreeState { self.blocks_by_hash.len() } - /// Returns the [`ExecutedBlock`] by hash. - fn executed_block_by_hash(&self, hash: B256) -> Option<&ExecutedBlock> { + /// Returns the [`ExecutedBlockWithTrieUpdates`] by hash. + fn executed_block_by_hash(&self, hash: B256) -> Option<&ExecutedBlockWithTrieUpdates> { self.blocks_by_hash.get(&hash) } @@ -154,7 +151,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. - fn blocks_by_hash(&self, hash: B256) -> Option<(B256, Vec>)> { + 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]; @@ -167,7 +164,7 @@ impl TreeState { } /// Insert executed block into the state. - fn insert_executed(&mut self, executed: ExecutedBlock) { + fn insert_executed(&mut self, executed: ExecutedBlockWithTrieUpdates) { let hash = executed.recovered_block().hash(); let parent_hash = executed.recovered_block().parent_hash(); let block_number = executed.recovered_block().number(); @@ -198,7 +195,10 @@ impl TreeState { /// ## Returns /// /// The removed block and the block hashes of its children. - fn remove_by_hash(&mut self, hash: B256) -> Option<(ExecutedBlock, HashSet)> { + fn remove_by_hash( + &mut self, + hash: B256, + ) -> Option<(ExecutedBlockWithTrieUpdates, HashSet)> { let executed = self.blocks_by_hash.remove(&hash)?; // Remove this block from collection of children of its parent block. @@ -485,15 +485,6 @@ pub enum TreeAction { }, } -/// Context used to keep alive the required values when returning a state hook -/// from a scoped thread. -struct StateHookContext

{ - provider_ro: P, - nodes_sorted: Arc, - state_sorted: Arc, - prefix_sets: Arc, -} - /// The engine API tree handler implementation. /// /// This type is responsible for processing engine API requests, maintaining the canonical state and @@ -607,8 +598,7 @@ where ) -> Self { let (incoming_tx, incoming) = std::sync::mpsc::channel(); - let num_threads = - std::thread::available_parallelism().map_or(1, |num| (num.get() / 2).max(1)); + let num_threads = root::thread_pool_size(); let state_root_task_pool = Arc::new( rayon::ThreadPoolBuilder::new() @@ -917,7 +907,8 @@ where // This is only done for in-memory blocks, because we should not have persisted any blocks // that are _above_ the current canonical head. while current_number > current_canonical_number { - if let Some(block) = self.executed_block_by_hash(current_hash)? { + if let Some(block) = self.state.tree_state.executed_block_by_hash(current_hash).cloned() + { current_hash = block.recovered_block().parent_hash(); current_number -= 1; new_chain.push(block); @@ -945,7 +936,7 @@ where // If the canonical chain is ahead of the new chain, // gather all blocks until new head number. while current_canonical_number > current_number { - if let Some(block) = self.executed_block_by_hash(old_hash)? { + if let Some(block) = self.canonical_block_by_hash(old_hash)? { old_chain.push(block.clone()); old_hash = block.recovered_block().parent_hash(); current_canonical_number -= 1; @@ -962,7 +953,7 @@ where // Walk both chains from specified hashes at same height until // a common ancestor (fork block) is reached. while old_hash != current_hash { - if let Some(block) = self.executed_block_by_hash(old_hash)? { + if let Some(block) = self.canonical_block_by_hash(old_hash)? { old_hash = block.recovered_block().parent_hash(); old_chain.push(block); } else { @@ -971,7 +962,8 @@ where return Ok(None); } - if let Some(block) = self.executed_block_by_hash(current_hash)? { + if let Some(block) = self.state.tree_state.executed_block_by_hash(current_hash).cloned() + { current_hash = block.recovered_block().parent_hash(); new_chain.push(block); } else { @@ -1194,28 +1186,6 @@ where /// If we're currently awaiting a response this will try to receive the response (non-blocking) /// or send a new persistence action if necessary. fn advance_persistence(&mut self) -> Result<(), AdvancePersistenceError> { - if !self.persistence_state.in_progress() { - if let Some(new_tip_num) = self.persistence_state.remove_above_state.pop_front() { - debug!(target: "engine::tree", ?new_tip_num, remove_state=?self.persistence_state.remove_above_state, last_persisted_block_number=?self.persistence_state.last_persisted_block.number, "Removing blocks using persistence task"); - if new_tip_num < self.persistence_state.last_persisted_block.number { - debug!(target: "engine::tree", ?new_tip_num, "Starting remove blocks job"); - let (tx, rx) = oneshot::channel(); - let _ = self.persistence.remove_blocks_above(new_tip_num, tx); - self.persistence_state.start(rx); - } - } else if self.should_persist() { - let blocks_to_persist = self.get_canonical_blocks_to_persist(); - if blocks_to_persist.is_empty() { - debug!(target: "engine::tree", "Returned empty set of blocks to persist"); - } else { - debug!(target: "engine::tree", blocks = ?blocks_to_persist.iter().map(|block| block.recovered_block().num_hash()).collect::>(), "Persisting blocks"); - let (tx, rx) = oneshot::channel(); - let _ = self.persistence.save_blocks(blocks_to_persist, tx); - self.persistence_state.start(rx); - } - } - } - if self.persistence_state.in_progress() { let (mut rx, start_time) = self .persistence_state @@ -1246,6 +1216,29 @@ where Err(TryRecvError::Empty) => self.persistence_state.rx = Some((rx, start_time)), } } + + if !self.persistence_state.in_progress() { + if let Some(new_tip_num) = self.persistence_state.remove_above_state.pop_front() { + debug!(target: "engine::tree", ?new_tip_num, remove_state=?self.persistence_state.remove_above_state, last_persisted_block_number=?self.persistence_state.last_persisted_block.number, "Removing blocks using persistence task"); + if new_tip_num < self.persistence_state.last_persisted_block.number { + debug!(target: "engine::tree", ?new_tip_num, "Starting remove blocks job"); + let (tx, rx) = oneshot::channel(); + let _ = self.persistence.remove_blocks_above(new_tip_num, tx); + self.persistence_state.start(rx); + } + } else if self.should_persist() { + let blocks_to_persist = self.get_canonical_blocks_to_persist(); + if blocks_to_persist.is_empty() { + debug!(target: "engine::tree", "Returned empty set of blocks to persist"); + } else { + debug!(target: "engine::tree", blocks = ?blocks_to_persist.iter().map(|block| block.recovered_block().num_hash()).collect::>(), "Persisting blocks"); + let (tx, rx) = oneshot::channel(); + let _ = self.persistence.save_blocks(blocks_to_persist, tx); + self.persistence_state.start(rx); + } + } + } + Ok(()) } @@ -1269,7 +1262,7 @@ where EngineApiRequest::InsertExecutedBlock(block) => { debug!(target: "engine::tree", block=?block.recovered_block().num_hash(), "inserting already executed block"); let now = Instant::now(); - let sealed_block = Arc::new(block.recovered_block().sealed_block().clone()); + let sealed_block = Arc::new(block.sealed_block().clone()); self.state.tree_state.insert_executed(block); self.metrics.engine.inserted_already_executed_blocks.increment(1); @@ -1537,7 +1530,7 @@ 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. - fn get_canonical_blocks_to_persist(&self) -> Vec> { + fn get_canonical_blocks_to_persist(&self) -> Vec> { let mut blocks_to_persist = Vec::new(); let mut current_hash = self.state.tree_state.canonical_block_hash(); let last_persisted_number = self.persistence_state.last_persisted_block.number; @@ -1590,19 +1583,13 @@ where /// has in memory. /// /// For finalized blocks, this will return `None`. - fn executed_block_by_hash(&self, hash: B256) -> ProviderResult>> { + fn canonical_block_by_hash(&self, hash: B256) -> ProviderResult>> { trace!(target: "engine::tree", ?hash, "Fetching executed block by hash"); // check memory first - let block = self.state.tree_state.executed_block_by_hash(hash).cloned(); - - if block.is_some() { - return Ok(block) + if let Some(block) = self.state.tree_state.executed_block_by_hash(hash).cloned() { + return Ok(Some(block.block)) } - let Some((_, updates)) = self.state.tree_state.persisted_trie_updates.get(&hash) else { - return Ok(None) - }; - let (block, senders) = self .provider .sealed_block_with_senders(hash.into(), TransactionVariant::WithHash)? @@ -1616,7 +1603,6 @@ where Ok(Some(ExecutedBlock { recovered_block: Arc::new(RecoveredBlock::new_sealed(block, senders)), - trie: updates.clone(), execution_output: Arc::new(execution_output), hashed_state: Arc::new(hashed_state), })) @@ -2055,7 +2041,21 @@ where self.update_reorg_metrics(old.len()); self.reinsert_reorged_blocks(new.clone()); - self.reinsert_reorged_blocks(old.clone()); + // Try reinserting the reorged canonical chain. This is only possible if we have + // `persisted_trie_updatess` for those blocks. + let old = old + .iter() + .filter_map(|block| { + let (_, trie) = self + .state + .tree_state + .persisted_trie_updates + .get(&block.recovered_block.hash()) + .cloned()?; + Some(ExecutedBlockWithTrieUpdates { block: block.clone(), trie }) + }) + .collect::>(); + self.reinsert_reorged_blocks(old); } // update the tracked in-memory state with the new chain @@ -2082,7 +2082,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 @@ -2221,7 +2221,8 @@ where &mut self, block: RecoveredBlock, ) -> Result { - debug!(target: "engine::tree", block=?block.num_hash(), parent = ?block.parent_hash(), state_root = ?block.state_root(), "Inserting new block into tree"); + let block_num_hash = block.num_hash(); + debug!(target: "engine::tree", block=?block_num_hash, parent = ?block.parent_hash(), state_root = ?block.state_root(), "Inserting new block into tree"); if self.block_by_hash(block.hash())?.is_some() { return Ok(InsertPayloadOk::AlreadySeen(BlockStatus::Valid)) @@ -2229,11 +2230,12 @@ where let start = Instant::now(); - trace!(target: "engine::tree", block=?block.num_hash(), "Validating block consensus"); + trace!(target: "engine::tree", block=?block_num_hash, "Validating block consensus"); + // validate block consensus rules self.validate_block(&block)?; - trace!(target: "engine::tree", block=?block.num_hash(), parent=?block.parent_hash(), "Fetching block state provider"); + trace!(target: "engine::tree", block=?block_num_hash, parent=?block.parent_hash(), "Fetching block state provider"); let Some(state_provider) = self.state_provider(block.parent_hash())? else { // we don't have the state required to execute this block, buffering it and find the // missing parent block @@ -2272,188 +2274,96 @@ where let state_provider = CachedStateProvider::new_with_caches(state_provider, caches, cache_metrics); - trace!(target: "engine::tree", block=?block.num_hash(), "Executing block"); - let executor = self.executor_provider.executor(StateProviderDatabase::new(&state_provider)); - - let block_number = block.number(); - let block_hash = block.hash(); let sealed_block = Arc::new(block.clone_sealed_block()); + trace!(target: "engine::tree", block=?block_num_hash, "Executing block"); + let executor = self.executor_provider.executor(StateProviderDatabase::new(&state_provider)); let persistence_not_in_progress = !self.persistence_state.in_progress(); - let state_root_result = std::thread::scope(|scope| { - let (state_root_handle, in_memory_trie_cursor, state_hook) = - if persistence_not_in_progress && self.config.use_state_root_task() { - let consistent_view = - ConsistentDbView::new_with_latest_tip(self.provider.clone())?; - - let state_root_config = StateRootConfig::new_from_input( - consistent_view.clone(), - self.compute_trie_input( - consistent_view.clone(), - block.header().parent_hash(), - ) - .map_err(|e| InsertBlockErrorKind::Other(Box::new(e)))?, - ); - - let provider_ro = consistent_view.provider_ro()?; - let nodes_sorted = state_root_config.nodes_sorted.clone(); - let state_sorted = state_root_config.state_sorted.clone(); - let prefix_sets = state_root_config.prefix_sets.clone(); - - // context will hold the values that need to be kept alive - let context = - StateHookContext { provider_ro, nodes_sorted, state_sorted, prefix_sets }; - - // it is ok to leak here because we are in a scoped thread, the - // memory will be freed when the thread completes - let context = Box::leak(Box::new(context)); - - let in_memory_trie_cursor = InMemoryTrieCursorFactory::new( - DatabaseTrieCursorFactory::new(context.provider_ro.tx_ref()), - &context.nodes_sorted, - ); - let blinded_provider_factory = ProofBlindedProviderFactory::new( - in_memory_trie_cursor.clone(), - HashedPostStateCursorFactory::new( - DatabaseHashedCursorFactory::new(context.provider_ro.tx_ref()), - &context.state_sorted, - ), - context.prefix_sets.clone(), - ); - - let state_root_task = StateRootTask::new( - state_root_config, - blinded_provider_factory, - self.state_root_task_pool.clone(), - ); - let state_hook = state_root_task.state_hook(); - ( - Some(state_root_task.spawn(scope)), - Some(in_memory_trie_cursor), - Box::new(state_hook) as Box, - ) - } else { - (None, None, Box::new(|_state: &EvmState| {}) as Box) - }; - - let execution_start = Instant::now(); - let output = self.metrics.executor.execute_metered(executor, &block, state_hook)?; - let execution_time = execution_start.elapsed(); - trace!(target: "engine::tree", elapsed = ?execution_time, ?block_number, "Executed block"); - - if let Err(err) = self.consensus.validate_block_post_execution( - &block, - PostExecutionInput::new(&output.receipts, &output.requests), - ) { - // call post-block hook - self.invalid_block_hook.on_invalid_block(&parent_block, &block, &output, None); - return Err(err.into()) - } + let (state_root_handle, state_root_task_config, state_hook) = if persistence_not_in_progress && + self.config.use_state_root_task() + { + let consistent_view = ConsistentDbView::new_with_latest_tip(self.provider.clone())?; + let state_root_config = StateRootConfig::new_from_input( + consistent_view.clone(), + self.compute_trie_input(consistent_view, block.header().parent_hash()) + .map_err(|e| InsertBlockErrorKind::Other(Box::new(e)))?, + ); - let hashed_state = self.provider.hashed_post_state(&output.state); - - trace!(target: "engine::tree", block=?sealed_block.num_hash(), "Calculating block state root"); - let root_time = Instant::now(); - - // We attempt to compute state root in parallel if we are currently not persisting - // anything to database. This is safe, because the database state cannot - // change until we finish parallel computation. It is important that nothing - // is being persisted as we are computing in parallel, because we initialize - // a different database transaction per thread and it might end up with a - // different view of the database. - let (state_root, trie_updates, root_elapsed) = if persistence_not_in_progress { - if self.config.use_state_root_task() { - match state_root_handle - .expect("state root handle must exist if use_state_root_task is true") - .wait_for_result() - { - Ok(StateRootComputeOutcome { - state_root: (task_state_root, task_trie_updates), - time_from_last_update, - .. - }) => { - info!( - target: "engine::tree", - block = ?sealed_block.num_hash(), - ?task_state_root, - task_elapsed = ?time_from_last_update, - "Task state root finished" - ); - - if task_state_root != block.header().state_root() || - self.config.always_compare_trie_updates() - { - if task_state_root != block.header().state_root() { - debug!(target: "engine::tree", "Task state root does not match block state root"); - } + let state_root_task = + StateRootTask::new(state_root_config.clone(), self.state_root_task_pool.clone()); + let state_hook = Box::new(state_root_task.state_hook()) as Box; + (Some(state_root_task.spawn()), Some(state_root_config), state_hook) + } else { + (None, None, Box::new(NoopHook::default()) as Box) + }; - let (regular_root, regular_updates) = - state_provider.state_root_with_updates(hashed_state.clone())?; - - if regular_root == block.header().state_root() { - compare_trie_updates( - in_memory_trie_cursor.expect("in memory trie cursor must exist if use_state_root_task is true"), - task_trie_updates.clone(), - regular_updates, - ) - .map_err(ProviderError::from)?; - } else { - debug!(target: "engine::tree", "Regular state root does not match block state root"); - } - } + let execution_start = Instant::now(); + let output = self.metrics.executor.execute_metered(executor, &block, state_hook)?; + let execution_time = execution_start.elapsed(); + trace!(target: "engine::tree", elapsed = ?execution_time, number=?block_num_hash.number, "Executed block"); - (task_state_root, task_trie_updates, time_from_last_update) - } - Err(error) => { - info!(target: "engine::tree", ?error, "Failed to wait for state root task result"); - // Fall back to sequential calculation - let (root, updates) = - state_provider.state_root_with_updates(hashed_state.clone())?; - (root, updates, root_time.elapsed()) - } + if let Err(err) = self.consensus.validate_block_post_execution( + &block, + PostExecutionInput::new(&output.receipts, &output.requests), + ) { + // call post-block hook + self.invalid_block_hook.on_invalid_block(&parent_block, &block, &output, None); + return Err(err.into()) + } + + let hashed_state = self.provider.hashed_post_state(&output.state); + + trace!(target: "engine::tree", block=?block_num_hash, "Calculating block state root"); + let root_time = Instant::now(); + + // We attempt to compute state root in parallel if we are currently not persisting + // anything to database. This is safe, because the database state cannot + // change until we finish parallel computation. It is important that nothing + // is being persisted as we are computing in parallel, because we initialize + // a different database transaction per thread and it might end up with a + // different view of the database. + let (state_root, trie_output, root_elapsed) = if persistence_not_in_progress { + if self.config.use_state_root_task() { + let state_root_handle = state_root_handle + .expect("state root handle must exist if use_state_root_task is true"); + let state_root_config = state_root_task_config.expect("task config is present"); + + // Handle state root result from task using handle + self.handle_state_root_result( + state_root_handle, + state_root_config, + sealed_block.as_ref(), + &hashed_state, + &state_provider, + root_time, + )? + } else { + match self.compute_state_root_parallel(block.header().parent_hash(), &hashed_state) + { + Ok(result) => { + info!( + target: "engine::tree", + block = ?block_num_hash, + regular_state_root = ?result.0, + "Regular root task finished" + ); + (result.0, result.1, root_time.elapsed()) } - } else { - match self - .compute_state_root_parallel(block.header().parent_hash(), &hashed_state) - { - Ok(result) => { - info!( - target: "engine::tree", - block = ?sealed_block.num_hash(), - regular_state_root = ?result.0, - "Regular root task finished" - ); - (result.0, result.1, root_time.elapsed()) - } - Err(ParallelStateRootError::Provider(ProviderError::ConsistentView( - error, - ))) => { - debug!(target: "engine", %error, "Parallel state root computation failed consistency check, falling back"); - let (root, updates) = - state_provider.state_root_with_updates(hashed_state.clone())?; - (root, updates, root_time.elapsed()) - } - Err(error) => return Err(InsertBlockErrorKind::Other(Box::new(error))), + Err(ParallelStateRootError::Provider(ProviderError::ConsistentView(error))) => { + debug!(target: "engine", %error, "Parallel state root computation failed consistency check, falling back"); + let (root, updates) = + state_provider.state_root_with_updates(hashed_state.clone())?; + (root, updates, root_time.elapsed()) } + Err(error) => return Err(InsertBlockErrorKind::Other(Box::new(error))), } - } else { - debug!(target: "engine::tree", block=?sealed_block.num_hash(), ?persistence_not_in_progress, "Failed to compute state root in parallel"); - let (root, updates) = - state_provider.state_root_with_updates(hashed_state.clone())?; - (root, updates, root_time.elapsed()) - }; - - Result::<_, InsertBlockErrorKind>::Ok(( - state_root, - trie_updates, - hashed_state, - output, - root_elapsed, - )) - })?; - - let (state_root, trie_output, hashed_state, output, root_elapsed) = state_root_result; + } + } else { + debug!(target: "engine::tree", block=?block_num_hash, ?persistence_not_in_progress, "Failed to compute state root in parallel"); + let (root, updates) = state_provider.state_root_with_updates(hashed_state.clone())?; + (root, updates, root_time.elapsed()) + }; if state_root != block.header().state_root() { // call post-block hook @@ -2470,21 +2380,23 @@ where } self.metrics.block_validation.record_state_root(&trie_output, root_elapsed.as_secs_f64()); - debug!(target: "engine::tree", ?root_elapsed, block=?sealed_block.num_hash(), "Calculated state root"); - - let executed: ExecutedBlock = ExecutedBlock { - recovered_block: Arc::new(RecoveredBlock::new_sealed( - sealed_block.as_ref().clone(), - block.senders().to_vec(), - )), - execution_output: Arc::new(ExecutionOutcome::from((output, block_number))), - hashed_state: Arc::new(hashed_state), + debug!(target: "engine::tree", ?root_elapsed, block=?block_num_hash, "Calculated state root"); + + let executed: ExecutedBlockWithTrieUpdates = ExecutedBlockWithTrieUpdates { + block: ExecutedBlock { + recovered_block: Arc::new(RecoveredBlock::new_sealed( + sealed_block.as_ref().clone(), + block.senders().to_vec(), + )), + execution_output: Arc::new(ExecutionOutcome::from((output, block_num_hash.number))), + hashed_state: Arc::new(hashed_state), + }, trie: Arc::new(trie_output), }; if self.state.tree_state.canonical_block_hash() == executed.recovered_block().parent_hash() { - debug!(target: "engine::tree", pending = ?executed.recovered_block().num_hash() ,"updating pending block"); + debug!(target: "engine::tree", pending=?block_num_hash, "updating pending block"); // if the parent is the canonical head, we can insert the block as the pending block self.canonical_in_memory_state.set_pending_block(executed.clone()); } @@ -2494,14 +2406,14 @@ where // emit insert event let elapsed = start.elapsed(); - let engine_event = if self.is_fork(block_hash)? { + let engine_event = if self.is_fork(block_num_hash.hash)? { BeaconConsensusEngineEvent::ForkBlockAdded(sealed_block, elapsed) } else { BeaconConsensusEngineEvent::CanonicalBlockAdded(sealed_block, elapsed) }; self.emit_event(EngineApiEvent::BeaconConsensus(engine_event)); - debug!(target: "engine::tree", block=?BlockNumHash::new(block_number, block_hash), "Finished inserting block"); + debug!(target: "engine::tree", block=?block_num_hash, "Finished inserting block"); Ok(InsertPayloadOk::Inserted(BlockStatus::Valid)) } @@ -2589,6 +2501,70 @@ where )) } + /// Waits for the result on the input [`StateRootHandle`], and handles it, falling back to + /// the hash builder-based state root calculation if it fails. + fn handle_state_root_result( + &self, + state_root_handle: StateRootHandle, + state_root_task_config: StateRootConfig

, + sealed_block: &SealedBlock, + hashed_state: &HashedPostState, + state_provider: impl StateRootProvider, + root_time: Instant, + ) -> Result<(B256, TrieUpdates, Duration), InsertBlockErrorKind> { + match state_root_handle.wait_for_result() { + Ok(StateRootComputeOutcome { + state_root: (task_state_root, task_trie_updates), + time_from_last_update, + .. + }) => { + info!( + target: "engine::tree", + block = ?sealed_block.num_hash(), + ?task_state_root, + task_elapsed = ?time_from_last_update, + "Task state root finished" + ); + + if task_state_root != sealed_block.header().state_root() || + self.config.always_compare_trie_updates() + { + if task_state_root != sealed_block.header().state_root() { + debug!(target: "engine::tree", "Task state root does not match block state root"); + } + + let (regular_root, regular_updates) = + state_provider.state_root_with_updates(hashed_state.clone())?; + + if regular_root == sealed_block.header().state_root() { + let provider_ro = state_root_task_config.consistent_view.provider_ro()?; + let in_memory_trie_cursor = InMemoryTrieCursorFactory::new( + DatabaseTrieCursorFactory::new(provider_ro.tx_ref()), + &state_root_task_config.nodes_sorted, + ); + compare_trie_updates( + in_memory_trie_cursor, + task_trie_updates.clone(), + regular_updates, + ) + .map_err(ProviderError::from)?; + } else { + debug!(target: "engine::tree", "Regular state root does not match block state root"); + } + } + + Ok((task_state_root, task_trie_updates, time_from_last_update)) + } + Err(error) => { + info!(target: "engine::tree", ?error, "Failed to wait for state root task result"); + // Fall back to sequential calculation + let (root, updates) = + state_provider.state_root_with_updates(hashed_state.clone())?; + Ok((root, updates, root_time.elapsed())) + } + } + } + /// Attempts to find the header for the given block hash if it is canonical. pub fn find_canonical_header( &self, @@ -2920,7 +2896,7 @@ mod tests { >, to_tree_tx: Sender, Block>>, from_tree_rx: UnboundedReceiver, - blocks: Vec, + blocks: Vec, action_rx: Receiver, executor_provider: MockExecutorProvider, block_builder: TestBlockBuilder, @@ -2991,7 +2967,7 @@ mod tests { } } - 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(); @@ -3736,20 +3712,24 @@ mod tests { let chain_b = test_block_builder.create_fork(&last_block, 10); for block in &chain_a { - 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()), + 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: 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(ExecutedBlock { - recovered_block: Arc::new(block.clone()), - execution_output: Arc::new(ExecutionOutcome::default()), - hashed_state: Arc::new(HashedPostState::default()), + 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: Arc::new(TrieUpdates::default()), }); } diff --git a/crates/engine/tree/src/tree/root.rs b/crates/engine/tree/src/tree/root.rs index 93cac7b435ed..dd96692a7b8e 100644 --- a/crates/engine/tree/src/tree/root.rs +++ b/crates/engine/tree/src/tree/root.rs @@ -6,28 +6,32 @@ use rayon::iter::{ParallelBridge, ParallelIterator}; use reth_errors::{ProviderError, ProviderResult}; use reth_evm::system_calls::OnStateHook; use reth_provider::{ - providers::ConsistentDbView, BlockReader, DatabaseProviderFactory, StateCommitmentProvider, + providers::ConsistentDbView, BlockReader, DBProvider, DatabaseProviderFactory, + StateCommitmentProvider, }; use reth_trie::{ + hashed_cursor::HashedPostStateCursorFactory, prefix_set::TriePrefixSetsMut, + proof::ProofBlindedProviderFactory, + trie_cursor::InMemoryTrieCursorFactory, updates::{TrieUpdates, TrieUpdatesSorted}, HashedPostState, HashedPostStateSorted, HashedStorage, MultiProof, MultiProofTargets, Nibbles, TrieInput, }; +use reth_trie_db::{DatabaseHashedCursorFactory, DatabaseTrieCursorFactory}; use reth_trie_parallel::{proof::ParallelProof, root::ParallelStateRootError}; use reth_trie_sparse::{ blinded::{BlindedProvider, BlindedProviderFactory}, - errors::{SparseStateTrieError, SparseStateTrieResult, SparseTrieErrorKind}, + errors::{SparseStateTrieResult, SparseTrieErrorKind}, SparseStateTrie, }; use revm_primitives::{keccak256, EvmState, B256}; use std::{ - collections::BTreeMap, + collections::{BTreeMap, VecDeque}, sync::{ mpsc::{self, channel, Receiver, Sender}, Arc, }, - thread::{self}, time::{Duration, Instant}, }; use tracing::{debug, error, trace}; @@ -35,6 +39,16 @@ use tracing::{debug, error, trace}; /// The level below which the sparse trie hashes are calculated in [`update_sparse_trie`]. const SPARSE_TRIE_INCREMENTAL_LEVEL: usize = 2; +/// Determines the size of the thread pool to be used in [`StateRootTask`]. +/// It should be at least three, one for multiproof calculations plus two to be +/// used internally in [`StateRootTask`]. +/// +/// NOTE: this value can be greater than the available cores in the host, it +/// represents the maximum number of threads that can be handled by the pool. +pub(crate) fn thread_pool_size() -> usize { + std::thread::available_parallelism().map_or(3, |num| (num.get() / 2).max(3)) +} + /// Outcome of the state root computation, including the state root itself with /// the trie updates and the total time spent. #[derive(Debug)] @@ -47,6 +61,32 @@ pub struct StateRootComputeOutcome { pub time_from_last_update: Duration, } +/// A trie update that can be applied to sparse trie alongside the proofs for touched parts of the +/// state. +#[derive(Default, Debug)] +pub struct SparseTrieUpdate { + /// The state update that was used to calculate the proof + state: HashedPostState, + /// The proof targets + targets: MultiProofTargets, + /// The calculated multiproof + multiproof: MultiProof, +} + +impl SparseTrieUpdate { + /// Construct update from multiproof. + pub fn from_multiproof(multiproof: MultiProof) -> Self { + Self { multiproof, ..Default::default() } + } + + /// Extend update with contents of the other. + pub fn extend(&mut self, other: Self) { + self.state.extend(other.state); + extend_multi_proof_targets(&mut self.targets, other.targets); + self.multiproof.extend(other.multiproof); + } +} + /// Result of the state root calculation pub(crate) type StateRootResult = Result; @@ -99,7 +139,7 @@ impl StateRootConfig { /// Messages used internally by the state root task #[derive(Debug)] -pub enum StateRootMessage { +pub enum StateRootMessage { /// Prefetch proof targets PrefetchProofs(MultiProofTargets), /// New state update from transaction execution @@ -110,13 +150,15 @@ pub enum StateRootMessage { ProofCalculationError(ProviderError), /// State root calculation completed RootCalculated { - /// The updated sparse trie - trie: Box>, - /// Time taken to calculate the root - elapsed: Duration, + /// Final state root. + state_root: B256, + /// Trie updates. + trie_updates: TrieUpdates, + /// The number of time sparse trie was updated. + iterations: u64, }, /// Error during state root calculation - RootCalculationError(SparseStateTrieError), + RootCalculationError(ParallelStateRootError), /// Signals state update stream end. FinishedStateUpdates, } @@ -124,14 +166,29 @@ pub enum StateRootMessage { /// Message about completion of proof calculation for a specific state update #[derive(Debug)] pub struct ProofCalculated { - /// The state update that was used to calculate the proof - state_update: HashedPostState, - /// The proof targets - targets: MultiProofTargets, - /// The calculated proof - proof: MultiProof, /// The index of this proof in the sequence of state updates sequence_number: u64, + /// Sparse trie update + update: SparseTrieUpdate, + /// The source of the proof fetch, whether it was requested as a prefetch or as a result of a + /// state update. + source: ProofFetchSource, +} + +impl ProofCalculated { + /// Returns true if the proof was calculated as a result of a state update. + pub(crate) const fn is_from_state_update(&self) -> bool { + matches!(self.source, ProofFetchSource::StateUpdate) + } +} + +/// Whether or not a proof was fetched due to a state update, or due to a prefetch command. +#[derive(Debug)] +pub enum ProofFetchSource { + /// The proof was fetched due to a prefetch command. + Prefetch, + /// The proof was fetched due to a state update. + StateUpdate, } /// Handle to track proof calculation ordering @@ -142,7 +199,7 @@ pub(crate) struct ProofSequencer { /// The next sequence number expected to be delivered. next_to_deliver: u64, /// Buffer for out-of-order proofs and corresponding state updates - pending_proofs: BTreeMap, + pending_proofs: BTreeMap, } impl ProofSequencer { @@ -163,12 +220,10 @@ impl ProofSequencer { pub(crate) fn add_proof( &mut self, sequence: u64, - state_update: HashedPostState, - targets: MultiProofTargets, - proof: MultiProof, - ) -> Vec<(HashedPostState, MultiProofTargets, MultiProof)> { + update: SparseTrieUpdate, + ) -> Vec { if sequence >= self.next_to_deliver { - self.pending_proofs.insert(sequence, (state_update, targets, proof)); + self.pending_proofs.insert(sequence, update); } // return early if we don't have the next expected proof @@ -202,16 +257,16 @@ impl ProofSequencer { } /// A wrapper for the sender that signals completion when dropped -#[derive(Deref)] -pub(crate) struct StateHookSender(Sender>); +#[derive(Deref, Debug)] +pub struct StateHookSender(Sender); -impl StateHookSender { - pub(crate) const fn new(inner: Sender>) -> Self { +impl StateHookSender { + pub(crate) const fn new(inner: Sender) -> Self { Self(inner) } } -impl Drop for StateHookSender { +impl Drop for StateHookSender { fn drop(&mut self) { // Send completion signal when the sender is dropped let _ = self.0.send(StateRootMessage::FinishedStateUpdates); @@ -251,6 +306,129 @@ fn evm_state_to_hashed_post_state(update: EvmState) -> HashedPostState { hashed_state } +/// Input parameters for spawning a multiproof calculation. +#[derive(Debug)] +struct MultiproofInput { + config: StateRootConfig, + hashed_state_update: HashedPostState, + proof_targets: MultiProofTargets, + proof_sequence_number: u64, + state_root_message_sender: Sender, + source: ProofFetchSource, +} + +/// Manages concurrent multiproof calculations. +/// Takes care of not having more calculations in flight than a given thread +/// pool size, further calculation requests are queued and spawn later, after +/// availability has been signaled. +#[derive(Debug)] +struct MultiproofManager { + /// Maximum number of concurrent calculations. + max_concurrent: usize, + /// Currently running calculations. + inflight: usize, + /// Queued calculations. + pending: VecDeque>, + /// Thread pool to spawn multiproof calculations. + thread_pool: Arc, +} + +impl MultiproofManager +where + Factory: DatabaseProviderFactory + + StateCommitmentProvider + + Clone + + Send + + Sync + + 'static, +{ + /// Creates a new [`MultiproofManager`]. + fn new(thread_pool: Arc, thread_pool_size: usize) -> Self { + // we keep 2 threads to be used internally by [`StateRootTask`] + let max_concurrent = thread_pool_size.saturating_sub(2); + debug_assert!(max_concurrent != 0); + Self { + thread_pool, + max_concurrent, + inflight: 0, + pending: VecDeque::with_capacity(max_concurrent), + } + } + + /// Spawns a new multiproof calculation or enqueues it for later if + /// `max_concurrent` are already inflight. + fn spawn_or_queue(&mut self, input: MultiproofInput) { + if self.inflight >= self.max_concurrent { + self.pending.push_back(input); + return; + } + + self.spawn_multiproof(input); + } + + /// Signals that a multiproof calculation has finished and there's room to + /// spawn a new calculation if needed. + fn on_calculation_complete(&mut self) { + self.inflight = self.inflight.saturating_sub(1); + + if let Some(input) = self.pending.pop_front() { + self.spawn_multiproof(input); + } + } + + /// Spawns a multiproof calculation. + fn spawn_multiproof(&mut self, input: MultiproofInput) { + let MultiproofInput { + config, + hashed_state_update, + proof_targets, + proof_sequence_number, + state_root_message_sender, + source, + } = input; + let thread_pool = self.thread_pool.clone(); + + self.thread_pool.spawn(move || { + trace!( + target: "engine::root", + proof_sequence_number, + ?proof_targets, + "Starting multiproof calculation", + ); + let start = Instant::now(); + let result = calculate_multiproof(thread_pool, config, proof_targets.clone()); + trace!( + target: "engine::root", + proof_sequence_number, + elapsed = ?start.elapsed(), + "Multiproof calculated", + ); + + match result { + Ok(proof) => { + let _ = state_root_message_sender.send(StateRootMessage::ProofCalculated( + Box::new(ProofCalculated { + sequence_number: proof_sequence_number, + update: SparseTrieUpdate { + state: hashed_state_update, + targets: proof_targets, + multiproof: proof, + }, + source, + }), + )); + } + Err(error) => { + let _ = state_root_message_sender + .send(StateRootMessage::ProofCalculationError(error)); + } + } + }); + + self.inflight += 1; + } +} + /// Standalone task that receives a transaction state stream and updates relevant /// data structures to calculate state root. /// @@ -260,25 +438,24 @@ fn evm_state_to_hashed_post_state(update: EvmState) -> HashedPostState { /// to the tree. /// Then it updates relevant leaves according to the result of the transaction. #[derive(Debug)] -pub struct StateRootTask { +pub struct StateRootTask { /// Task configuration. config: StateRootConfig, /// Receiver for state root related messages. - rx: Receiver>, + rx: Receiver, /// Sender for state root related messages. - tx: Sender>, + tx: Sender, /// Proof targets that have been already fetched. fetched_proof_targets: MultiProofTargets, /// Proof sequencing handler. proof_sequencer: ProofSequencer, - /// The sparse trie used for the state root calculation. If [`None`], then update is in - /// progress. - sparse_trie: Option>>, - /// Reference to the shared thread pool for parallel proof generation + /// Reference to the shared thread pool for parallel proof generation. thread_pool: Arc, + /// Manages calculation of multiproofs. + multiproof_manager: MultiproofManager, } -impl<'env, Factory, BPF> StateRootTask +impl StateRootTask where Factory: DatabaseProviderFactory + StateCommitmentProvider @@ -286,38 +463,48 @@ where + Send + Sync + 'static, - BPF: BlindedProviderFactory + Send + Sync + 'env, - BPF::AccountNodeProvider: BlindedProvider + Send + Sync + 'env, - BPF::StorageNodeProvider: BlindedProvider + Send + Sync + 'env, { /// Creates a new state root task with the unified message channel - pub fn new( - config: StateRootConfig, - blinded_provider: BPF, - thread_pool: Arc, - ) -> Self { + pub fn new(config: StateRootConfig, thread_pool: Arc) -> Self { let (tx, rx) = channel(); - Self { config, rx, tx, fetched_proof_targets: Default::default(), proof_sequencer: ProofSequencer::new(), - sparse_trie: Some(Box::new(SparseStateTrie::new(blinded_provider).with_updates(true))), - thread_pool, + thread_pool: thread_pool.clone(), + multiproof_manager: MultiproofManager::new(thread_pool, thread_pool_size()), + } + } + + /// Returns a [`StateHookSender`] that can be used to send state updates to this task. + pub fn state_hook_sender(&self) -> StateHookSender { + StateHookSender::new(self.tx.clone()) + } + + /// Returns a state hook to be used to send state updates to this task. + pub fn state_hook(&self) -> impl OnStateHook { + let state_hook = self.state_hook_sender(); + + move |state: &EvmState| { + if let Err(error) = state_hook.send(StateRootMessage::StateUpdate(state.clone())) { + error!(target: "engine::root", ?error, "Failed to send state update"); + } } } /// Spawns the state root task and returns a handle to await its result. - pub fn spawn<'scope>(self, scope: &'scope thread::Scope<'scope, 'env>) -> StateRootHandle { + pub fn spawn(self) -> StateRootHandle { + let sparse_trie_tx = + Self::spawn_sparse_trie(self.thread_pool.clone(), self.config.clone(), self.tx.clone()); let (tx, rx) = mpsc::sync_channel(1); std::thread::Builder::new() .name("State Root Task".to_string()) - .spawn_scoped(scope, move || { + .spawn(move || { debug!(target: "engine::tree", "Starting state root task"); - let result = rayon::scope(|scope| self.run(scope)); + let result = self.run(sparse_trie_tx); let _ = tx.send(result); }) .expect("failed to spawn state root thread"); @@ -325,110 +512,55 @@ where StateRootHandle::new(rx) } - /// Returns a state hook to be used to send state updates to this task. - pub fn state_hook(&self) -> impl OnStateHook { - let state_hook = StateHookSender::new(self.tx.clone()); - - move |state: &EvmState| { - if let Err(error) = state_hook.send(StateRootMessage::StateUpdate(state.clone())) { - error!(target: "engine::root", ?error, "Failed to send state update"); - } - } + /// Spawn long running sparse trie task that forwards the final result upon completion. + fn spawn_sparse_trie( + thread_pool: Arc, + config: StateRootConfig, + task_tx: Sender, + ) -> Sender { + let (tx, rx) = mpsc::channel(); + thread_pool.spawn(move || { + debug!(target: "engine::tree", "Starting sparse trie task"); + let result = match run_sparse_trie(config, rx) { + Ok((state_root, trie_updates, iterations)) => { + StateRootMessage::RootCalculated { state_root, trie_updates, iterations } + } + Err(error) => StateRootMessage::RootCalculationError(error), + }; + let _ = task_tx.send(result); + }); + tx } /// Handles request for proof prefetch. - fn on_prefetch_proof( - scope: &rayon::Scope<'env>, - config: StateRootConfig, - targets: MultiProofTargets, - fetched_proof_targets: &mut MultiProofTargets, - proof_sequence_number: u64, - state_root_message_sender: Sender>, - thread_pool: Arc, - ) { - extend_multi_proof_targets_ref(fetched_proof_targets, &targets); - - Self::spawn_multiproof( - scope, - config, - Default::default(), - targets, - proof_sequence_number, - state_root_message_sender, - thread_pool, - ); + fn on_prefetch_proof(&mut self, targets: MultiProofTargets) { + extend_multi_proof_targets_ref(&mut self.fetched_proof_targets, &targets); + + self.multiproof_manager.spawn_or_queue(MultiproofInput { + config: self.config.clone(), + hashed_state_update: Default::default(), + proof_targets: targets, + proof_sequence_number: self.proof_sequencer.next_sequence(), + state_root_message_sender: self.tx.clone(), + source: ProofFetchSource::Prefetch, + }); } /// Handles state updates. /// /// Returns proof targets derived from the state update. - fn on_state_update( - scope: &rayon::Scope<'env>, - config: StateRootConfig, - update: EvmState, - fetched_proof_targets: &mut MultiProofTargets, - proof_sequence_number: u64, - state_root_message_sender: Sender>, - thread_pool: Arc, - ) { + fn on_state_update(&mut self, update: EvmState, proof_sequence_number: u64) { let hashed_state_update = evm_state_to_hashed_post_state(update); + let proof_targets = get_proof_targets(&hashed_state_update, &self.fetched_proof_targets); + extend_multi_proof_targets_ref(&mut self.fetched_proof_targets, &proof_targets); - let proof_targets = get_proof_targets(&hashed_state_update, fetched_proof_targets); - extend_multi_proof_targets_ref(fetched_proof_targets, &proof_targets); - - Self::spawn_multiproof( - scope, - config, + self.multiproof_manager.spawn_or_queue(MultiproofInput { + config: self.config.clone(), hashed_state_update, proof_targets, proof_sequence_number, - state_root_message_sender, - thread_pool, - ); - } - - fn spawn_multiproof( - scope: &rayon::Scope<'env>, - config: StateRootConfig, - hashed_state_update: HashedPostState, - proof_targets: MultiProofTargets, - proof_sequence_number: u64, - state_root_message_sender: Sender>, - thread_pool: Arc, - ) { - // Dispatch proof gathering for this state update - scope.spawn(move |_| { - trace!( - target: "engine::root", - proof_sequence_number, - ?proof_targets, - "Starting multiproof calculation", - ); - let start = Instant::now(); - let result = calculate_multiproof(thread_pool, config, proof_targets.clone()); - trace!( - target: "engine::root", - proof_sequence_number, - elapsed = ?start.elapsed(), - "Multiproof calculated", - ); - - match result { - Ok(proof) => { - let _ = state_root_message_sender.send(StateRootMessage::ProofCalculated( - Box::new(ProofCalculated { - state_update: hashed_state_update, - targets: proof_targets, - proof, - sequence_number: proof_sequence_number, - }), - )); - } - Err(error) => { - let _ = state_root_message_sender - .send(StateRootMessage::ProofCalculationError(error)); - } - } + state_root_message_sender: self.tx.clone(), + source: ProofFetchSource::StateUpdate, }); } @@ -436,77 +568,26 @@ where fn on_proof( &mut self, sequence_number: u64, - state_update: HashedPostState, - targets: MultiProofTargets, - proof: MultiProof, - ) -> Option<(HashedPostState, MultiProofTargets, MultiProof)> { - let ready_proofs = - self.proof_sequencer.add_proof(sequence_number, state_update, targets, proof); + update: SparseTrieUpdate, + ) -> Option { + let ready_proofs = self.proof_sequencer.add_proof(sequence_number, update); if ready_proofs.is_empty() { None } else { // Merge all ready proofs and state updates - ready_proofs.into_iter().reduce( - |(mut acc_state_update, mut acc_targets, mut acc_proof), - (state_update, targets, proof)| { - acc_state_update.extend(state_update); - extend_multi_proof_targets(&mut acc_targets, targets); - acc_proof.extend(proof); - - (acc_state_update, acc_targets, acc_proof) - }, - ) + ready_proofs.into_iter().reduce(|mut acc_update, update| { + acc_update.extend(update); + acc_update + }) } } - /// Spawns root calculation with the current state and proofs. - fn spawn_root_calculation( - &mut self, - scope: &rayon::Scope<'env>, - state: HashedPostState, - targets: MultiProofTargets, - multiproof: MultiProof, - ) { - let Some(trie) = self.sparse_trie.take() else { return }; - - trace!( - target: "engine::root", - account_proofs = multiproof.account_subtree.len(), - storage_proofs = multiproof.storages.len(), - "Spawning root calculation" - ); - - // TODO(alexey): store proof targets in `ProofSequecner` to avoid recomputing them - let targets = get_proof_targets(&state, &targets); - - let tx = self.tx.clone(); - scope.spawn(move |_| { - let result = update_sparse_trie(trie, multiproof, targets, state); - match result { - Ok((trie, elapsed)) => { - trace!( - target: "engine::root", - ?elapsed, - "Root calculation completed, sending result" - ); - let _ = tx.send(StateRootMessage::RootCalculated { trie, elapsed }); - } - Err(e) => { - let _ = tx.send(StateRootMessage::RootCalculationError(e)); - } - } - }); - } - - fn run(mut self, scope: &rayon::Scope<'env>) -> StateRootResult { - let mut current_state_update = HashedPostState::default(); - let mut current_proof_targets = MultiProofTargets::default(); - let mut current_multiproof = MultiProof::default(); + fn run(mut self, sparse_trie_tx: Sender) -> StateRootResult { + let mut sparse_trie_tx = Some(sparse_trie_tx); let mut updates_received = 0; let mut proofs_processed = 0; - let mut roots_calculated = 0; let mut updates_finished = false; @@ -516,25 +597,20 @@ where let mut last_update_time = None; loop { + trace!(target: "engine::root", "entering main channel receiving loop"); match self.rx.recv() { Ok(message) => match message { StateRootMessage::PrefetchProofs(targets) => { + trace!(target: "engine::root", "processing StateRootMessage::PrefetchProofs"); debug!( target: "engine::root", len = targets.len(), "Prefetching proofs" ); - Self::on_prefetch_proof( - scope, - self.config.clone(), - targets, - &mut self.fetched_proof_targets, - self.proof_sequencer.next_sequence(), - self.tx.clone(), - self.thread_pool.clone(), - ); + self.on_prefetch_proof(targets); } StateRootMessage::StateUpdate(update) => { + trace!(target: "engine::root", "processing StateRootMessage::StateUpdate"); if updates_received == 0 { first_update_time = Some(Instant::now()); debug!(target: "engine::root", "Started state root calculation"); @@ -548,22 +624,32 @@ where total_updates = updates_received, "Received new state update" ); - Self::on_state_update( - scope, - self.config.clone(), - update, - &mut self.fetched_proof_targets, - self.proof_sequencer.next_sequence(), - self.tx.clone(), - self.thread_pool.clone(), - ); + let next_sequence = self.proof_sequencer.next_sequence(); + self.on_state_update(update, next_sequence); } StateRootMessage::FinishedStateUpdates => { - trace!(target: "engine::root", "Finished state updates"); + trace!(target: "engine::root", "processing StateRootMessage::FinishedStateUpdates"); updates_finished = true; + + let all_proofs_received = proofs_processed >= updates_received; + let no_pending = !self.proof_sequencer.has_pending(); + if all_proofs_received && no_pending { + // drop the sender + sparse_trie_tx.take(); + debug!( + target: "engine::root", + total_updates = updates_received, + total_proofs = proofs_processed, + "State updates finished and all proofs processed, ending calculation" + ); + } } StateRootMessage::ProofCalculated(proof_calculated) => { - proofs_processed += 1; + trace!(target: "engine::root", "processing StateRootMessage::ProofCalculated"); + if proof_calculated.is_from_state_update() { + proofs_processed += 1; + } + debug!( target: "engine::root", sequence = proof_calculated.sequence_number, @@ -571,105 +657,52 @@ where "Processing calculated proof" ); - if let Some(( - combined_state_update, - combined_proof_targets, - combined_proof, - )) = self.on_proof( - proof_calculated.sequence_number, - proof_calculated.state_update, - proof_calculated.targets, - proof_calculated.proof, - ) { - if self.sparse_trie.is_none() { - current_state_update.extend(combined_state_update); - extend_multi_proof_targets( - &mut current_proof_targets, - combined_proof_targets, - ); - current_multiproof.extend(combined_proof); - } else { - self.spawn_root_calculation( - scope, - combined_state_update, - combined_proof_targets, - combined_proof, - ); - } + self.multiproof_manager.on_calculation_complete(); + + if let Some(combined_update) = + self.on_proof(proof_calculated.sequence_number, proof_calculated.update) + { + let _ = sparse_trie_tx + .as_ref() + .expect("tx not dropped") + .send(combined_update); } - } - StateRootMessage::RootCalculated { trie, elapsed } => { - roots_calculated += 1; - debug!( - target: "engine::root", - ?elapsed, - roots_calculated, - proofs = proofs_processed, - updates = updates_received, - "Computed intermediate root" - ); - self.sparse_trie = Some(trie); - let has_new_proofs = !current_multiproof.account_subtree.is_empty() || - !current_multiproof.storages.is_empty(); let all_proofs_received = proofs_processed >= updates_received; let no_pending = !self.proof_sequencer.has_pending(); - - trace!( - target: "engine::root", - has_new_proofs, - all_proofs_received, - no_pending, - ?updates_finished, - "State check" - ); - - // only spawn new calculation if we have accumulated new proofs - if has_new_proofs { - debug!( - target: "engine::root", - account_proofs = current_multiproof.account_subtree.len(), - storage_proofs = current_multiproof.storages.len(), - "Spawning subsequent root calculation" - ); - self.spawn_root_calculation( - scope, - std::mem::take(&mut current_state_update), - std::mem::take(&mut current_proof_targets), - std::mem::take(&mut current_multiproof), - ); - } else if all_proofs_received && no_pending && updates_finished { - let total_time = first_update_time - .expect("first update time should be set") - .elapsed(); - let time_from_last_update = - last_update_time.expect("last update time should be set").elapsed(); + if all_proofs_received && no_pending && updates_finished { + // drop the sender + sparse_trie_tx.take(); debug!( target: "engine::root", total_updates = updates_received, total_proofs = proofs_processed, - roots_calculated, - ?total_time, - ?time_from_last_update, "All proofs processed, ending calculation" ); - - let mut trie = self - .sparse_trie - .take() - .expect("sparse trie update should not be in progress"); - let root = trie.root().expect("sparse trie should be revealed"); - let trie_updates = trie - .take_trie_updates() - .expect("sparse trie should have updates retention enabled"); - - return Ok(StateRootComputeOutcome { - state_root: (root, trie_updates), - total_time, - time_from_last_update, - }); } } + StateRootMessage::RootCalculated { state_root, trie_updates, iterations } => { + trace!(target: "engine::root", "processing StateRootMessage::RootCalculated"); + let total_time = + first_update_time.expect("first update time should be set").elapsed(); + let time_from_last_update = + last_update_time.expect("last update time should be set").elapsed(); + debug!( + target: "engine::root", + total_updates = updates_received, + total_proofs = proofs_processed, + roots_calculated = iterations, + ?total_time, + ?time_from_last_update, + "All proofs processed, ending calculation" + ); + return Ok(StateRootComputeOutcome { + state_root: (state_root, trie_updates), + total_time, + time_from_last_update, + }); + } + StateRootMessage::ProofCalculationError(e) => { return Err(ParallelStateRootError::Other(format!( "could not calculate multiproof: {e:?}" @@ -697,6 +730,63 @@ where } } +/// Listen to incoming sparse trie updates and update the sparse trie. +/// Returns final state root, trie updates and the number of update iterations. +fn run_sparse_trie( + config: StateRootConfig, + update_rx: mpsc::Receiver, +) -> Result<(B256, TrieUpdates, u64), ParallelStateRootError> +where + Factory: DatabaseProviderFactory + StateCommitmentProvider, +{ + let provider_ro = config.consistent_view.provider_ro()?; + let in_memory_trie_cursor = InMemoryTrieCursorFactory::new( + DatabaseTrieCursorFactory::new(provider_ro.tx_ref()), + &config.nodes_sorted, + ); + let blinded_provider_factory = ProofBlindedProviderFactory::new( + in_memory_trie_cursor.clone(), + HashedPostStateCursorFactory::new( + DatabaseHashedCursorFactory::new(provider_ro.tx_ref()), + &config.state_sorted, + ), + config.prefix_sets.clone(), + ); + + let mut num_iterations = 0; + let mut trie = SparseStateTrie::new(blinded_provider_factory).with_updates(true); + + while let Ok(mut update) = update_rx.recv() { + num_iterations += 1; + let mut num_updates = 1; + while let Ok(next) = update_rx.try_recv() { + update.extend(next); + num_updates += 1; + } + + debug!( + target: "engine::root", + num_updates, + account_proofs = update.multiproof.account_subtree.len(), + storage_proofs = update.multiproof.storages.len(), + "Updating sparse trie" + ); + + // TODO: alexey to remind me why we are doing this + update.targets = get_proof_targets(&update.state, &update.targets); + + let elapsed = update_sparse_trie(&mut trie, update).map_err(|e| { + ParallelStateRootError::Other(format!("could not calculate state root: {e:?}")) + })?; + trace!(target: "engine::root", ?elapsed, num_iterations, "Root calculation completed"); + } + + debug!(target: "engine::root", num_iterations, "All proofs processed, ending calculation"); + let root = trie.root().expect("sparse trie should be revealed"); + let trie_updates = trie.take_trie_updates().expect("retention must be enabled"); + Ok((root, trie_updates, num_iterations)) +} + /// Returns accounts only with those storages that were not already fetched, and /// if there are no such storages and the account itself was already fetched, the /// account shouldn't be included. @@ -752,14 +842,11 @@ where .multiproof(proof_targets)?) } -/// Updates the sparse trie with the given proofs and state, and returns the updated trie and the -/// time it took. +/// Updates the sparse trie with the given proofs and state, and returns the elapsed time. fn update_sparse_trie( - mut trie: Box>, - multiproof: MultiProof, - targets: MultiProofTargets, - state: HashedPostState, -) -> SparseStateTrieResult<(Box>, Duration)> + trie: &mut SparseStateTrie, + SparseTrieUpdate { state, targets, multiproof }: SparseTrieUpdate, +) -> SparseStateTrieResult where BPF: BlindedProviderFactory + Send + Sync, BPF::AccountNodeProvider: BlindedProvider + Send + Sync, @@ -820,7 +907,7 @@ where trie.calculate_below_level(SPARSE_TRIE_INCREMENTAL_LEVEL); let elapsed = started_at.elapsed(); - Ok((trie, elapsed)) + Ok(elapsed) } fn extend_multi_proof_targets(targets: &mut MultiProofTargets, other: MultiProofTargets) { @@ -843,11 +930,7 @@ mod tests { providers::ConsistentDbView, test_utils::create_test_provider_factory, HashingWriter, }; use reth_testing_utils::generators::{self, Rng}; - use reth_trie::{ - hashed_cursor::HashedPostStateCursorFactory, proof::ProofBlindedProviderFactory, - test_utils::state_root, trie_cursor::InMemoryTrieCursorFactory, TrieInput, - }; - use reth_trie_db::{DatabaseHashedCursorFactory, DatabaseTrieCursorFactory}; + use reth_trie::{test_utils::state_root, TrieInput}; use revm_primitives::{ Account as RevmAccount, AccountInfo, AccountStatus, Address, EvmState, EvmStorageSlot, HashMap, B256, KECCAK_EMPTY, U256, @@ -965,24 +1048,12 @@ mod tests { let state_sorted = Arc::new(input.state.clone().into_sorted()); let config = StateRootConfig { consistent_view: ConsistentDbView::new(factory, None), - nodes_sorted: nodes_sorted.clone(), - state_sorted: state_sorted.clone(), + nodes_sorted, + state_sorted, prefix_sets: Arc::new(input.prefix_sets), }; - let provider = config.consistent_view.provider_ro().unwrap(); - let blinded_provider_factory = ProofBlindedProviderFactory::new( - InMemoryTrieCursorFactory::new( - DatabaseTrieCursorFactory::new(provider.tx_ref()), - &nodes_sorted, - ), - HashedPostStateCursorFactory::new( - DatabaseHashedCursorFactory::new(provider.tx_ref()), - &state_sorted, - ), - config.prefix_sets.clone(), - ); - let num_threads = - std::thread::available_parallelism().map_or(1, |num| (num.get() / 2).max(1)); + + let num_threads = thread_pool_size(); let state_root_task_pool = rayon::ThreadPoolBuilder::new() .num_threads(num_threads) @@ -990,23 +1061,16 @@ mod tests { .build() .expect("Failed to create proof worker thread pool"); - let (root_from_task, _) = std::thread::scope(|std_scope| { - let task = StateRootTask::new( - config, - blinded_provider_factory, - Arc::new(state_root_task_pool), - ); - let mut state_hook = task.state_hook(); - let handle = task.spawn(std_scope); + let task = StateRootTask::new(config, Arc::new(state_root_task_pool)); + let mut state_hook = task.state_hook(); + let handle = task.spawn(); - for update in state_updates { - state_hook.on_state(&update); - } - drop(state_hook); + for update in state_updates { + state_hook.on_state(&update); + } + drop(state_hook); - handle.wait_for_result().expect("task failed") - }) - .state_root; + let (root_from_task, _) = handle.wait_for_result().expect("task failed").state_root; let root_from_base = state_root(accumulated_state); assert_eq!( @@ -1022,21 +1086,11 @@ mod tests { let proof2 = MultiProof::default(); sequencer.next_sequence = 2; - let ready = sequencer.add_proof( - 0, - HashedPostState::default(), - MultiProofTargets::default(), - proof1, - ); + let ready = sequencer.add_proof(0, SparseTrieUpdate::from_multiproof(proof1)); assert_eq!(ready.len(), 1); assert!(!sequencer.has_pending()); - let ready = sequencer.add_proof( - 1, - HashedPostState::default(), - MultiProofTargets::default(), - proof2, - ); + let ready = sequencer.add_proof(1, SparseTrieUpdate::from_multiproof(proof2)); assert_eq!(ready.len(), 1); assert!(!sequencer.has_pending()); } @@ -1049,30 +1103,15 @@ mod tests { let proof3 = MultiProof::default(); sequencer.next_sequence = 3; - let ready = sequencer.add_proof( - 2, - HashedPostState::default(), - MultiProofTargets::default(), - proof3, - ); + let ready = sequencer.add_proof(2, SparseTrieUpdate::from_multiproof(proof3)); assert_eq!(ready.len(), 0); assert!(sequencer.has_pending()); - let ready = sequencer.add_proof( - 0, - HashedPostState::default(), - MultiProofTargets::default(), - proof1, - ); + let ready = sequencer.add_proof(0, SparseTrieUpdate::from_multiproof(proof1)); assert_eq!(ready.len(), 1); assert!(sequencer.has_pending()); - let ready = sequencer.add_proof( - 1, - HashedPostState::default(), - MultiProofTargets::default(), - proof2, - ); + let ready = sequencer.add_proof(1, SparseTrieUpdate::from_multiproof(proof2)); assert_eq!(ready.len(), 2); assert!(!sequencer.has_pending()); } @@ -1084,20 +1123,10 @@ mod tests { let proof3 = MultiProof::default(); sequencer.next_sequence = 3; - let ready = sequencer.add_proof( - 0, - HashedPostState::default(), - MultiProofTargets::default(), - proof1, - ); + let ready = sequencer.add_proof(0, SparseTrieUpdate::from_multiproof(proof1)); assert_eq!(ready.len(), 1); - let ready = sequencer.add_proof( - 2, - HashedPostState::default(), - MultiProofTargets::default(), - proof3, - ); + let ready = sequencer.add_proof(2, SparseTrieUpdate::from_multiproof(proof3)); assert_eq!(ready.len(), 0); assert!(sequencer.has_pending()); } @@ -1108,20 +1137,10 @@ mod tests { let proof1 = MultiProof::default(); let proof2 = MultiProof::default(); - let ready = sequencer.add_proof( - 0, - HashedPostState::default(), - MultiProofTargets::default(), - proof1, - ); + let ready = sequencer.add_proof(0, SparseTrieUpdate::from_multiproof(proof1)); assert_eq!(ready.len(), 1); - let ready = sequencer.add_proof( - 0, - HashedPostState::default(), - MultiProofTargets::default(), - proof2, - ); + let ready = sequencer.add_proof(0, SparseTrieUpdate::from_multiproof(proof2)); assert_eq!(ready.len(), 0); assert!(!sequencer.has_pending()); } @@ -1132,37 +1151,12 @@ mod tests { let proofs: Vec<_> = (0..5).map(|_| MultiProof::default()).collect(); sequencer.next_sequence = 5; - sequencer.add_proof( - 4, - HashedPostState::default(), - MultiProofTargets::default(), - proofs[4].clone(), - ); - sequencer.add_proof( - 2, - HashedPostState::default(), - MultiProofTargets::default(), - proofs[2].clone(), - ); - sequencer.add_proof( - 1, - HashedPostState::default(), - MultiProofTargets::default(), - proofs[1].clone(), - ); - sequencer.add_proof( - 3, - HashedPostState::default(), - MultiProofTargets::default(), - proofs[3].clone(), - ); + sequencer.add_proof(4, SparseTrieUpdate::from_multiproof(proofs[4].clone())); + sequencer.add_proof(2, SparseTrieUpdate::from_multiproof(proofs[2].clone())); + sequencer.add_proof(1, SparseTrieUpdate::from_multiproof(proofs[1].clone())); + sequencer.add_proof(3, SparseTrieUpdate::from_multiproof(proofs[3].clone())); - let ready = sequencer.add_proof( - 0, - HashedPostState::default(), - MultiProofTargets::default(), - proofs[0].clone(), - ); + let ready = sequencer.add_proof(0, SparseTrieUpdate::from_multiproof(proofs[0].clone())); assert_eq!(ready.len(), 5); assert!(!sequencer.has_pending()); } diff --git a/crates/engine/util/Cargo.toml b/crates/engine/util/Cargo.toml index 3e5f333e8d1f..d98e51ace225 100644 --- a/crates/engine/util/Cargo.toml +++ b/crates/engine/util/Cargo.toml @@ -26,6 +26,7 @@ reth-provider.workspace = true reth-ethereum-forks.workspace = true revm-primitives.workspace = true reth-trie.workspace = true +reth-payload-primitives.workspace = true # alloy alloy-eips.workspace = true diff --git a/crates/engine/util/src/reorg.rs b/crates/engine/util/src/reorg.rs index 4f8ed0ce3315..16771731ad0f 100644 --- a/crates/engine/util/src/reorg.rs +++ b/crates/engine/util/src/reorg.rs @@ -8,20 +8,20 @@ use alloy_rpc_types_engine::{ use futures::{stream::FuturesUnordered, Stream, StreamExt, TryFutureExt}; use itertools::Either; use reth_engine_primitives::{ - BeaconEngineMessage, BeaconOnNewPayloadError, EngineApiMessageVersion, EngineTypes, - OnForkChoiceUpdated, + BeaconEngineMessage, BeaconOnNewPayloadError, EngineTypes, OnForkChoiceUpdated, }; use reth_errors::{BlockExecutionError, BlockValidationError, RethError, RethResult}; use reth_ethereum_forks::EthereumHardforks; use reth_evm::{ state_change::post_block_withdrawals_balance_increments, system_calls::SystemCaller, - ConfigureEvm, + ConfigureEvm, Evm, }; +use reth_payload_primitives::EngineApiMessageVersion; use reth_payload_validator::ExecutionPayloadValidator; use reth_primitives::{ - proofs, transaction::SignedTransactionIntoRecoveredExt, Block, BlockBody, Receipt, Receipts, + transaction::SignedTransactionIntoRecoveredExt, Block, BlockBody, Receipt, Receipts, }; -use reth_primitives_traits::{block::Block as _, SignedTransaction}; +use reth_primitives_traits::{block::Block as _, proofs, SignedTransaction}; use reth_provider::{BlockReader, ExecutionOutcome, ProviderError, StateProviderFactory}; use reth_revm::{ database::StateProviderDatabase, @@ -322,11 +322,11 @@ where } // Configure the environment for the block. - let tx_recovered = tx.clone().try_into_ecrecovered().map_err(|_| { + let tx_recovered = tx.try_clone_into_recovered().map_err(|_| { BlockExecutionError::Validation(BlockValidationError::SenderRecoveryError) })?; - evm_config.fill_tx_env(evm.tx_mut(), &tx_recovered, tx_recovered.signer()); - let exec_result = match evm.transact() { + let tx_env = evm_config.tx_env(&tx_recovered, tx_recovered.signer()); + let exec_result = match evm.transact(tx_env) { Ok(result) => result, error @ Err(EVMError::Transaction(_) | EVMError::Header(_)) => { trace!(target: "engine::stream::reorg", hash = %tx.tx_hash(), ?error, "Error executing transaction from next block"); @@ -348,13 +348,13 @@ where cumulative_gas_used += exec_result.result.gas_used(); #[allow(clippy::needless_update)] // side-effect of optimism fields - receipts.push(Some(Receipt { + receipts.push(Receipt { tx_type: tx.tx_type(), success: exec_result.result.is_success(), cumulative_gas_used, logs: exec_result.result.into_logs().into_iter().collect(), ..Default::default() - })); + }); // append transaction to the list of executed transactions transactions.push(tx); diff --git a/crates/ethereum-forks/src/hardfork/ethereum.rs b/crates/ethereum-forks/src/hardfork/ethereum.rs index d75c444ead7c..684f15992b8e 100644 --- a/crates/ethereum-forks/src/hardfork/ethereum.rs +++ b/crates/ethereum-forks/src/hardfork/ethereum.rs @@ -45,9 +45,9 @@ hardfork!( Paris, /// Shanghai: . Shanghai, - /// Cancun. + /// Cancun: Cancun, - /// Prague: + /// Prague. Prague, /// Osaka: Osaka, diff --git a/crates/ethereum/consensus/src/lib.rs b/crates/ethereum/consensus/src/lib.rs index 4d2daaaaeada..c0ca492d6bce 100644 --- a/crates/ethereum/consensus/src/lib.rs +++ b/crates/ethereum/consensus/src/lib.rs @@ -270,7 +270,7 @@ mod tests { use super::*; use alloy_primitives::B256; use reth_chainspec::{ChainSpec, ChainSpecBuilder}; - use reth_primitives::proofs; + use reth_primitives_traits::proofs; fn header_with_gas_limit(gas_limit: u64) -> SealedHeader { let header = reth_primitives::Header { gas_limit, ..Default::default() }; diff --git a/crates/ethereum/engine-primitives/Cargo.toml b/crates/ethereum/engine-primitives/Cargo.toml index f019f6e5f2a6..3a5aa3247577 100644 --- a/crates/ethereum/engine-primitives/Cargo.toml +++ b/crates/ethereum/engine-primitives/Cargo.toml @@ -18,13 +18,13 @@ reth-engine-primitives.workspace = true reth-payload-primitives.workspace = true reth-payload-validator.workspace = true reth-rpc-types-compat.workspace = true -alloy-rlp.workspace = true reth-chain-state.workspace = true # alloy alloy-primitives.workspace = true alloy-eips.workspace = true alloy-rpc-types-engine.workspace = true +alloy-rlp.workspace = true # misc serde.workspace = true @@ -32,3 +32,18 @@ sha2.workspace = true [dev-dependencies] serde_json.workspace = true + +[features] +default = ["std"] +std = [ + "reth-chainspec/std", + "reth-primitives/std", + "alloy-primitives/std", + "alloy-eips/std", + "alloy-rpc-types-engine/std", + "alloy-rlp/std", + "serde/std", + "sha2/std", + "serde_json/std", + "reth-engine-primitives/std" +] diff --git a/crates/ethereum/engine-primitives/src/lib.rs b/crates/ethereum/engine-primitives/src/lib.rs index f4723bf39f28..d58e7ff1ca96 100644 --- a/crates/ethereum/engine-primitives/src/lib.rs +++ b/crates/ethereum/engine-primitives/src/lib.rs @@ -7,8 +7,12 @@ )] #![cfg_attr(not(test), warn(unused_crate_dependencies))] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate alloc; mod payload; +use alloc::sync::Arc; use alloy_rpc_types_engine::{ExecutionPayload, ExecutionPayloadSidecar, PayloadError}; pub use alloy_rpc_types_engine::{ ExecutionPayloadEnvelopeV2, ExecutionPayloadEnvelopeV3, ExecutionPayloadEnvelopeV4, @@ -16,21 +20,20 @@ pub use alloy_rpc_types_engine::{ }; pub use payload::{EthBuiltPayload, EthPayloadBuilderAttributes}; use reth_chainspec::ChainSpec; -use reth_engine_primitives::{BuiltPayload, EngineTypes, EngineValidator, PayloadValidator}; +use reth_engine_primitives::{EngineTypes, EngineValidator, PayloadValidator}; use reth_payload_primitives::{ - validate_version_specific_fields, EngineApiMessageVersion, EngineObjectValidationError, - PayloadOrAttributes, PayloadTypes, + validate_version_specific_fields, BuiltPayload, EngineApiMessageVersion, + EngineObjectValidationError, PayloadOrAttributes, PayloadTypes, }; use reth_payload_validator::ExecutionPayloadValidator; use reth_primitives::{Block, NodePrimitives, SealedBlock}; use reth_rpc_types_compat::engine::payload::block_to_payload; -use std::sync::Arc; /// The types used in the default mainnet ethereum beacon consensus engine. #[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)] #[non_exhaustive] pub struct EthEngineTypes { - _marker: std::marker::PhantomData, + _marker: core::marker::PhantomData, } impl PayloadTypes for EthEngineTypes { diff --git a/crates/ethereum/engine-primitives/src/payload.rs b/crates/ethereum/engine-primitives/src/payload.rs index 450302598ecd..eabad4fb131b 100644 --- a/crates/ethereum/engine-primitives/src/payload.rs +++ b/crates/ethereum/engine-primitives/src/payload.rs @@ -1,5 +1,6 @@ //! Contains types required for building a payload. +use alloc::{sync::Arc, vec::Vec}; use alloy_eips::{eip4844::BlobTransactionSidecar, eip4895::Withdrawals, eip7685::Requests}; use alloy_primitives::{Address, B256, U256}; use alloy_rlp::Encodable; @@ -7,13 +8,13 @@ use alloy_rpc_types_engine::{ ExecutionPayloadEnvelopeV2, ExecutionPayloadEnvelopeV3, ExecutionPayloadEnvelopeV4, ExecutionPayloadV1, PayloadAttributes, PayloadId, }; -use reth_chain_state::ExecutedBlock; +use core::convert::Infallible; +use reth_chain_state::ExecutedBlockWithTrieUpdates; use reth_payload_primitives::{BuiltPayload, PayloadBuilderAttributes}; use reth_primitives::{EthPrimitives, SealedBlock}; use reth_rpc_types_compat::engine::payload::{ block_to_payload_v1, block_to_payload_v3, convert_block_to_payload_field_v2, }; -use std::{convert::Infallible, sync::Arc}; /// Contains the built payload. /// @@ -27,7 +28,7 @@ pub struct EthBuiltPayload { /// The built 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, /// The blobs, proofs, and commitments in the block. If the block is pre-cancun, this will be @@ -47,7 +48,7 @@ impl EthBuiltPayload { id: PayloadId, block: Arc, fees: U256, - executed_block: Option, + executed_block: Option, requests: Option, ) -> Self { Self { id, block, executed_block, fees, sidecars: Vec::new(), requests } @@ -99,7 +100,7 @@ impl BuiltPayload for EthBuiltPayload { self.fees } - fn executed_block(&self) -> Option { + fn executed_block(&self) -> Option { self.executed_block.clone() } @@ -119,7 +120,7 @@ impl BuiltPayload for &EthBuiltPayload { (**self).fees() } - fn executed_block(&self) -> Option { + fn executed_block(&self) -> Option { self.executed_block.clone() } @@ -297,7 +298,7 @@ mod tests { use super::*; use alloy_eips::eip4895::Withdrawal; use alloy_primitives::B64; - use std::str::FromStr; + use core::str::FromStr; #[test] fn attributes_serde() { diff --git a/crates/ethereum/evm/Cargo.toml b/crates/ethereum/evm/Cargo.toml index 7e6ed8052877..19a026517e00 100644 --- a/crates/ethereum/evm/Cargo.toml +++ b/crates/ethereum/evm/Cargo.toml @@ -30,6 +30,9 @@ alloy-eips.workspace = true alloy-sol-types.workspace = true alloy-consensus.workspace = true +# Misc +derive_more.workspace = true + [dev-dependencies] reth-testing-utils.workspace = true reth-evm = { workspace = true, features = ["test-utils"] } @@ -55,5 +58,6 @@ std = [ "reth-ethereum-forks/std", "serde_json/std", "reth-primitives-traits/std", - "reth-chainspec/std" + "reth-chainspec/std", + "derive_more/std" ] diff --git a/crates/ethereum/evm/src/execute.rs b/crates/ethereum/evm/src/execute.rs index 8bc3272272b2..e162c8557e49 100644 --- a/crates/ethereum/evm/src/execute.rs +++ b/crates/ethereum/evm/src/execute.rs @@ -19,7 +19,7 @@ use reth_evm::{ }, state_change::post_block_balance_increments, system_calls::{OnStateHook, SystemCaller}, - ConfigureEvm, TxEnvOverrides, + ConfigureEvm, Evm, }; use reth_primitives::{EthPrimitives, Receipt, RecoveredBlock}; use reth_primitives_traits::{BlockBody, SignedTransaction}; @@ -94,8 +94,6 @@ where chain_spec: Arc, /// How to create an EVM. evm_config: EvmConfig, - /// Optional overrides for the transactions environment. - tx_env_overrides: Option>, /// Current state for block execution. state: State, /// Utility to call system smart contracts. @@ -109,7 +107,7 @@ where /// Creates a new [`EthExecutionStrategy`] pub fn new(state: State, chain_spec: Arc, evm_config: EvmConfig) -> Self { let system_caller = SystemCaller::new(evm_config.clone(), chain_spec.clone()); - Self { state, chain_spec, evm_config, system_caller, tx_env_overrides: None } + Self { state, chain_spec, evm_config, system_caller } } } @@ -123,13 +121,8 @@ where { type DB = DB; type Error = BlockExecutionError; - type Primitives = EthPrimitives; - fn init(&mut self, tx_env_overrides: Box) { - self.tx_env_overrides = Some(tx_env_overrides); - } - fn apply_pre_execution_changes( &mut self, block: &RecoveredBlock, @@ -166,14 +159,10 @@ where .into()) } - self.evm_config.fill_tx_env(evm.tx_mut(), transaction, *sender); - - if let Some(tx_env_overrides) = &mut self.tx_env_overrides { - tx_env_overrides.apply(evm.tx_mut()); - } + let tx_env = self.evm_config.tx_env(transaction, *sender); // Execute transaction. - let result_and_state = evm.transact().map_err(move |err| { + let result_and_state = evm.transact(tx_env).map_err(move |err| { let new_err = err.map_db_err(|e| e.into()); // Ensure hash is calculated for error log, if not already done BlockValidationError::EVM { @@ -713,7 +702,8 @@ mod tests { let chain_spec = Arc::new( ChainSpecBuilder::from(&*MAINNET) .shanghai_activated() - .with_fork(EthereumHardfork::Prague, ForkCondition::Timestamp(0)) + .cancun_activated() + .prague_activated() .build(), ); @@ -751,6 +741,7 @@ mod tests { let chain_spec = Arc::new( ChainSpecBuilder::from(&*MAINNET) .shanghai_activated() + .cancun_activated() .with_fork(EthereumHardfork::Prague, ForkCondition::Timestamp(1)) .build(), ); @@ -760,6 +751,8 @@ mod tests { timestamp: 1, number: fork_activation_block, requests_hash: Some(EMPTY_REQUESTS_HASH), + excess_blob_gas: Some(0), + parent_beacon_block_root: Some(B256::random()), ..Header::default() }; let provider = executor_provider(chain_spec); @@ -801,6 +794,7 @@ mod tests { let chain_spec = Arc::new( ChainSpecBuilder::from(&*MAINNET) .shanghai_activated() + .cancun_activated() .with_fork(EthereumHardfork::Prague, ForkCondition::Timestamp(1)) .build(), ); @@ -813,6 +807,8 @@ mod tests { timestamp: 1, number: fork_activation_block, requests_hash: Some(EMPTY_REQUESTS_HASH), + excess_blob_gas: Some(0), + parent_beacon_block_root: Some(B256::random()), ..Header::default() }; @@ -838,12 +834,12 @@ mod tests { let chain_spec = Arc::new( ChainSpecBuilder::from(&*MAINNET) .shanghai_activated() - .with_fork(EthereumHardfork::Prague, ForkCondition::Timestamp(0)) + .cancun_activated() + .prague_activated() .build(), ); - let mut header = chain_spec.genesis_header().clone(); - header.requests_hash = Some(EMPTY_REQUESTS_HASH); + let header = chain_spec.genesis_header().clone(); let header_hash = header.hash_slow(); let provider = executor_provider(chain_spec); @@ -875,6 +871,8 @@ mod tests { timestamp: 1, number: 1, requests_hash: Some(EMPTY_REQUESTS_HASH), + excess_blob_gas: Some(0), + parent_beacon_block_root: Some(B256::random()), ..Header::default() }; let header_hash = header.hash_slow(); @@ -908,6 +906,8 @@ mod tests { timestamp: 1, number: 2, requests_hash: Some(EMPTY_REQUESTS_HASH), + excess_blob_gas: Some(0), + parent_beacon_block_root: Some(B256::random()), ..Header::default() }; @@ -946,7 +946,8 @@ mod tests { let chain_spec = Arc::new( ChainSpecBuilder::from(&*MAINNET) .shanghai_activated() - .with_fork(EthereumHardfork::Prague, ForkCondition::Timestamp(0)) + .cancun_activated() + .prague_activated() .build(), ); @@ -997,7 +998,7 @@ mod tests { let BlockExecutionOutput { receipts, requests, .. } = executor .execute( &Block { header, body: BlockBody { transactions: vec![tx], ..Default::default() } } - .with_recovered_senders() + .try_into_recovered() .unwrap(), ) .unwrap(); @@ -1073,7 +1074,7 @@ mod tests { // Execute the block and capture the result let exec_result = executor.execute( &Block { header, body: BlockBody { transactions: vec![tx], ..Default::default() } } - .with_recovered_senders() + .try_into_recovered() .unwrap(), ); @@ -1095,7 +1096,8 @@ mod tests { let chain_spec = Arc::new( ChainSpecBuilder::from(&*MAINNET) .shanghai_activated() - .with_fork(EthereumHardfork::Prague, ForkCondition::Timestamp(0)) + .cancun_activated() + .prague_activated() .build(), ); @@ -1113,7 +1115,13 @@ mod tests { let withdrawal = Withdrawal { index: 0, validator_index: 0, address: withdrawal_recipient, amount: 1 }; - let header = Header { timestamp: 1, number: 1, ..Header::default() }; + let header = Header { + timestamp: 1, + number: 1, + excess_blob_gas: Some(0), + parent_beacon_block_root: Some(B256::random()), + ..Header::default() + }; let block = &RecoveredBlock::new_unhashed( Block { diff --git a/crates/ethereum/evm/src/lib.rs b/crates/ethereum/evm/src/lib.rs index 50907223a1a2..408aa415c5eb 100644 --- a/crates/ethereum/evm/src/lib.rs +++ b/crates/ethereum/evm/src/lib.rs @@ -17,18 +17,18 @@ extern crate alloc; -use core::convert::Infallible; - use alloc::{sync::Arc, vec::Vec}; use alloy_consensus::{BlockHeader, Header}; -use alloy_primitives::{Address, Bytes, TxKind, U256}; +use alloy_primitives::{Address, U256}; +use core::{convert::Infallible, fmt::Debug}; use reth_chainspec::ChainSpec; -use reth_evm::{env::EvmEnv, ConfigureEvm, ConfigureEvmEnv, NextBlockEnvAttributes}; +use reth_evm::{env::EvmEnv, ConfigureEvm, ConfigureEvmEnv, Evm, NextBlockEnvAttributes}; use reth_primitives::TransactionSigned; use reth_primitives_traits::transaction::execute::FillTxEnv; -use reth_revm::{inspector_handle_register, EvmBuilder}; +use reth_revm::{inspector_handle_register, Database, EvmBuilder}; use revm_primitives::{ - AnalysisKind, BlobExcessGasAndPrice, BlockEnv, CfgEnv, CfgEnvWithHandlerCfg, Env, SpecId, TxEnv, + AnalysisKind, BlobExcessGasAndPrice, BlockEnv, Bytes, CfgEnv, CfgEnvWithHandlerCfg, EVMError, + HandlerCfg, ResultAndState, SpecId, TxEnv, TxKind, }; mod config; @@ -44,42 +44,33 @@ pub mod dao_fork; /// [EIP-6110](https://eips.ethereum.org/EIPS/eip-6110) handling. pub mod eip6110; -/// Ethereum-related EVM configuration. -#[derive(Debug, Clone)] -pub struct EthEvmConfig { - chain_spec: Arc, -} +/// Ethereum EVM implementation. +#[derive(derive_more::Debug, derive_more::Deref, derive_more::DerefMut, derive_more::From)] +#[debug(bound(DB::Error: Debug))] +pub struct EthEvm<'a, EXT, DB: Database>(reth_revm::Evm<'a, EXT, DB>); -impl EthEvmConfig { - /// Creates a new Ethereum EVM configuration with the given chain spec. - pub const fn new(chain_spec: Arc) -> Self { - Self { chain_spec } - } +impl Evm for EthEvm<'_, EXT, DB> { + type DB = DB; + type Tx = TxEnv; + type Error = EVMError; - /// Returns the chain spec associated with this configuration. - pub const fn chain_spec(&self) -> &Arc { - &self.chain_spec + fn block(&self) -> &BlockEnv { + self.0.block() } -} - -impl ConfigureEvmEnv for EthEvmConfig { - type Header = Header; - type Transaction = TransactionSigned; - type Error = Infallible; - fn fill_tx_env(&self, tx_env: &mut TxEnv, transaction: &TransactionSigned, sender: Address) { - transaction.fill_tx_env(tx_env, sender); + fn transact(&mut self, tx: Self::Tx) -> Result { + *self.tx_mut() = tx; + self.0.transact() } - fn fill_tx_env_system_contract_call( - &self, - env: &mut Env, + fn transact_system_call( + &mut self, caller: Address, contract: Address, data: Bytes, - ) { + ) -> Result { #[allow(clippy::needless_update)] // side-effect of optimism fields - let tx = TxEnv { + let tx_env = TxEnv { caller, transact_to: TxKind::Call(contract), // Explicitly set nonce to None so revm does not do any nonce checks @@ -102,25 +93,86 @@ impl ConfigureEvmEnv for EthEvmConfig { // TODO remove this once this crate is no longer built with optimism ..Default::default() }; - env.tx = tx; + + *self.tx_mut() = tx_env; + + let prev_block_env = self.block().clone(); // ensure the block gas limit is >= the tx - env.block.gas_limit = U256::from(env.tx.gas_limit); + self.block_mut().gas_limit = U256::from(self.tx().gas_limit); // disable the base fee check for this call by setting the base fee to zero - env.block.basefee = U256::ZERO; + self.block_mut().basefee = U256::ZERO; + + let res = self.0.transact(); + + // re-set the block env + *self.block_mut() = prev_block_env; + + res + } + + fn db_mut(&mut self) -> &mut Self::DB { + &mut self.context.evm.db + } +} + +/// Ethereum-related EVM configuration. +#[derive(Debug, Clone)] +pub struct EthEvmConfig { + chain_spec: Arc, +} + +impl EthEvmConfig { + /// Creates a new Ethereum EVM configuration with the given chain spec. + pub const fn new(chain_spec: Arc) -> Self { + Self { chain_spec } } - fn fill_cfg_env(&self, cfg_env: &mut CfgEnvWithHandlerCfg, header: &Header) { - let spec_id = config::revm_spec(self.chain_spec(), header); + /// Returns the chain spec associated with this configuration. + pub const fn chain_spec(&self) -> &Arc { + &self.chain_spec + } +} +impl ConfigureEvmEnv for EthEvmConfig { + type Header = Header; + type Transaction = TransactionSigned; + type Error = Infallible; + type TxEnv = TxEnv; + type Spec = revm_primitives::SpecId; + + fn tx_env(&self, transaction: &TransactionSigned, sender: Address) -> Self::TxEnv { + let mut tx_env = TxEnv::default(); + transaction.fill_tx_env(&mut tx_env, sender); + tx_env + } + + fn evm_env(&self, header: &Self::Header) -> EvmEnv { + let spec = config::revm_spec(self.chain_spec(), header); + + let mut cfg_env = CfgEnv::default(); cfg_env.chain_id = self.chain_spec.chain().id(); - cfg_env.perf_analyse_created_bytecodes = AnalysisKind::Analyse; + cfg_env.perf_analyse_created_bytecodes = AnalysisKind::default(); + + let block_env = BlockEnv { + number: U256::from(header.number()), + coinbase: header.beneficiary(), + timestamp: U256::from(header.timestamp()), + difficulty: if spec >= SpecId::MERGE { U256::ZERO } else { header.difficulty() }, + prevrandao: if spec >= SpecId::MERGE { header.mix_hash() } else { None }, + gas_limit: U256::from(header.gas_limit()), + basefee: U256::from(header.base_fee_per_gas().unwrap_or_default()), + // EIP-4844 excess blob gas of this block, introduced in Cancun + blob_excess_gas_and_price: header.excess_blob_gas.map(|excess_blob_gas| { + BlobExcessGasAndPrice::new(excess_blob_gas, spec >= SpecId::PRAGUE) + }), + }; - cfg_env.handler_cfg.spec_id = spec_id; + EvmEnv { cfg_env, spec, block_env } } - fn next_cfg_and_block_env( + fn next_evm_env( &self, parent: &Self::Header, attributes: NextBlockEnvAttributes, @@ -184,39 +236,45 @@ impl ConfigureEvmEnv for EthEvmConfig { } impl ConfigureEvm for EthEvmConfig { - fn evm_with_env( - &self, - db: DB, - evm_env: EvmEnv, - tx: TxEnv, - ) -> reth_revm::Evm<'_, (), DB> { - EvmBuilder::default() - .with_db(db) - .with_cfg_env_with_handler_cfg(evm_env.cfg_env_with_handler_cfg) - .with_block_env(evm_env.block_env) - .with_tx_env(tx) - .build() + type Evm<'a, DB: Database + 'a, I: 'a> = EthEvm<'a, I, DB>; + + fn evm_with_env(&self, db: DB, evm_env: EvmEnv) -> Self::Evm<'_, DB, ()> { + let cfg_env_with_handler_cfg = CfgEnvWithHandlerCfg { + cfg_env: evm_env.cfg_env, + handler_cfg: HandlerCfg::new(evm_env.spec), + }; + EthEvm( + EvmBuilder::default() + .with_db(db) + .with_cfg_env_with_handler_cfg(cfg_env_with_handler_cfg) + .with_block_env(evm_env.block_env) + .build(), + ) } fn evm_with_env_and_inspector( &self, db: DB, evm_env: EvmEnv, - tx: TxEnv, inspector: I, - ) -> reth_revm::Evm<'_, I, DB> + ) -> Self::Evm<'_, DB, I> where - DB: reth_revm::Database, + DB: Database, I: reth_revm::GetInspector, { - EvmBuilder::default() - .with_db(db) - .with_external_context(inspector) - .with_cfg_env_with_handler_cfg(evm_env.cfg_env_with_handler_cfg) - .with_block_env(evm_env.block_env) - .with_tx_env(tx) - .append_handler_register(inspector_handle_register) - .build() + let cfg_env_with_handler_cfg = CfgEnvWithHandlerCfg { + cfg_env: evm_env.cfg_env, + handler_cfg: HandlerCfg::new(evm_env.spec), + }; + EthEvm( + EvmBuilder::default() + .with_db(db) + .with_external_context(inspector) + .with_cfg_env_with_handler_cfg(cfg_env_with_handler_cfg) + .with_block_env(evm_env.block_env) + .append_handler_register(inspector_handle_register) + .build(), + ) } } @@ -252,12 +310,12 @@ mod tests { // Use the `EthEvmConfig` to fill the `cfg_env` and `block_env` based on the ChainSpec, // Header, and total difficulty - let EvmEnv { cfg_env_with_handler_cfg, .. } = - EthEvmConfig::new(Arc::new(chain_spec.clone())).cfg_and_block_env(&header); + let EvmEnv { cfg_env, .. } = + EthEvmConfig::new(Arc::new(chain_spec.clone())).evm_env(&header); // Assert that the chain ID in the `cfg_env` is correctly set to the chain ID of the // ChainSpec - assert_eq!(cfg_env_with_handler_cfg.chain_id, chain_spec.chain().id()); + assert_eq!(cfg_env.chain_id, chain_spec.chain().id()); } #[test] @@ -269,11 +327,11 @@ mod tests { let evm_env = EvmEnv::default(); - let evm = evm_config.evm_with_env(db, evm_env.clone(), Default::default()); + let evm = evm_config.evm_with_env(db, evm_env.clone()); // Check that the EVM environment assert_eq!(evm.context.evm.env.block, evm_env.block_env); - assert_eq!(evm.context.evm.env.cfg, evm_env.cfg_env_with_handler_cfg.cfg_env); + assert_eq!(evm.context.evm.env.cfg, evm_env.cfg_env); // Default spec ID assert_eq!(evm.handler.spec_id(), SpecId::LATEST); @@ -292,15 +350,9 @@ mod tests { // Create a custom configuration environment with a chain ID of 111 let cfg = CfgEnv::default().with_chain_id(111); - let evm_env = EvmEnv { - cfg_env_with_handler_cfg: CfgEnvWithHandlerCfg { - cfg_env: cfg.clone(), - handler_cfg: Default::default(), - }, - ..Default::default() - }; + let evm_env = EvmEnv { cfg_env: cfg.clone(), ..Default::default() }; - let evm = evm_config.evm_with_env(db, evm_env, Default::default()); + let evm = evm_config.evm_with_env(db, evm_env); // Check that the EVM environment is initialized with the custom environment assert_eq!(evm.context.evm.inner.env.cfg, cfg); @@ -326,21 +378,13 @@ mod tests { number: U256::from(42), ..Default::default() }; - let tx = TxEnv { gas_limit: 5_000_000, gas_price: U256::from(50), ..Default::default() }; - - let evm_env = EvmEnv { - cfg_env_with_handler_cfg: CfgEnvWithHandlerCfg { - cfg_env: CfgEnv::default(), - handler_cfg: Default::default(), - }, - block_env: block, - }; - let evm = evm_config.evm_with_env(db, evm_env.clone(), tx.clone()); + let evm_env = EvmEnv { block_env: block, ..Default::default() }; + + let evm = evm_config.evm_with_env(db, evm_env.clone()); // Verify that the block and transaction environments are set correctly assert_eq!(evm.context.evm.env.block, evm_env.block_env); - assert_eq!(evm.context.evm.env.tx, tx); // Default spec ID assert_eq!(evm.handler.spec_id(), SpecId::LATEST); @@ -356,17 +400,9 @@ mod tests { let db = CacheDB::>::default(); - let handler_cfg = HandlerCfg { spec_id: SpecId::CONSTANTINOPLE, ..Default::default() }; + let evm_env = EvmEnv { spec: SpecId::CONSTANTINOPLE, ..Default::default() }; - let evm_env = EvmEnv { - cfg_env_with_handler_cfg: CfgEnvWithHandlerCfg { - cfg_env: Default::default(), - handler_cfg, - }, - ..Default::default() - }; - - let evm = evm_config.evm_with_env(db, evm_env, Default::default()); + let evm = evm_config.evm_with_env(db, evm_env); // Check that the spec ID is setup properly assert_eq!(evm.handler.spec_id(), SpecId::PETERSBURG); @@ -386,16 +422,11 @@ mod tests { let evm_env = EvmEnv::default(); - let evm = evm_config.evm_with_env_and_inspector( - db, - evm_env.clone(), - Default::default(), - NoOpInspector, - ); + let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector); // Check that the EVM environment is set to default values assert_eq!(evm.context.evm.env.block, evm_env.block_env); - assert_eq!(evm.context.evm.env.cfg, evm_env.cfg_env_with_handler_cfg.cfg_env); + assert_eq!(evm.context.evm.env.cfg, evm_env.cfg_env); assert_eq!(evm.context.external, NoOpInspector); assert_eq!(evm.handler.spec_id(), SpecId::LATEST); @@ -411,16 +442,10 @@ mod tests { let cfg_env = CfgEnv::default().with_chain_id(111); let block = BlockEnv::default(); - let tx = TxEnv::default(); - let evm_env = EvmEnv { - cfg_env_with_handler_cfg: CfgEnvWithHandlerCfg { - cfg_env: cfg_env.clone(), - handler_cfg: Default::default(), - }, - block_env: block, - }; + let evm_env = + EvmEnv { cfg_env: cfg_env.clone(), block_env: block, spec: Default::default() }; - let evm = evm_config.evm_with_env_and_inspector(db, evm_env, tx, NoOpInspector); + let evm = evm_config.evm_with_env_and_inspector(db, evm_env, NoOpInspector); // Check that the EVM environment is set with custom configuration assert_eq!(evm.context.evm.env.cfg, cfg_env); @@ -444,15 +469,12 @@ mod tests { number: U256::from(42), ..Default::default() }; - let tx = TxEnv { gas_limit: 5_000_000, gas_price: U256::from(50), ..Default::default() }; let evm_env = EvmEnv { block_env: block, ..Default::default() }; - let evm = - evm_config.evm_with_env_and_inspector(db, evm_env.clone(), tx.clone(), NoOpInspector); + let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector); // Verify that the block and transaction environments are set correctly assert_eq!(evm.context.evm.env.block, evm_env.block_env); - assert_eq!(evm.context.evm.env.tx, tx); assert_eq!(evm.context.external, NoOpInspector); assert_eq!(evm.handler.spec_id(), SpecId::LATEST); @@ -466,26 +488,14 @@ mod tests { let evm_config = EthEvmConfig::new(MAINNET.clone()); let db = CacheDB::>::default(); - let handler_cfg = HandlerCfg { spec_id: SpecId::CONSTANTINOPLE, ..Default::default() }; - let evm_env = EvmEnv { - cfg_env_with_handler_cfg: CfgEnvWithHandlerCfg { - handler_cfg, - cfg_env: Default::default(), - }, - ..Default::default() - }; + let evm_env = EvmEnv { spec: SpecId::CONSTANTINOPLE, ..Default::default() }; - let evm = evm_config.evm_with_env_and_inspector( - db, - evm_env.clone(), - Default::default(), - NoOpInspector, - ); + let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector); // Check that the spec ID is set properly assert_eq!(evm.handler.spec_id(), SpecId::PETERSBURG); assert_eq!(evm.context.evm.env.block, evm_env.block_env); - assert_eq!(evm.context.evm.env.cfg, evm_env.cfg_env_with_handler_cfg.cfg_env); + assert_eq!(evm.context.evm.env.cfg, evm_env.cfg_env); assert_eq!(evm.context.evm.env.tx, Default::default()); assert_eq!(evm.context.external, NoOpInspector); diff --git a/crates/ethereum/node/src/evm.rs b/crates/ethereum/node/src/evm.rs index bcdcaac6bfa2..6af6f927162d 100644 --- a/crates/ethereum/node/src/evm.rs +++ b/crates/ethereum/node/src/evm.rs @@ -5,4 +5,4 @@ pub use reth_evm::execute::BasicBlockExecutorProvider; #[doc(inline)] pub use reth_evm_ethereum::execute::{EthExecutionStrategyFactory, EthExecutorProvider}; #[doc(inline)] -pub use reth_evm_ethereum::EthEvmConfig; +pub use reth_evm_ethereum::{EthEvm, EthEvmConfig}; diff --git a/crates/ethereum/node/src/payload.rs b/crates/ethereum/node/src/payload.rs index 84df93c0bab4..52d0fe01d1a0 100644 --- a/crates/ethereum/node/src/payload.rs +++ b/crates/ethereum/node/src/payload.rs @@ -6,9 +6,9 @@ use reth_ethereum_engine_primitives::{ EthBuiltPayload, EthPayloadAttributes, EthPayloadBuilderAttributes, }; use reth_ethereum_payload_builder::EthereumBuilderConfig; -use reth_evm::ConfigureEvm; +use reth_evm::ConfigureEvmFor; use reth_evm_ethereum::EthEvmConfig; -use reth_node_api::{FullNodeTypes, HeaderTy, NodeTypesWithEngine, TxTy}; +use reth_node_api::{FullNodeTypes, NodeTypesWithEngine, PrimitivesTy, TxTy}; use reth_node_builder::{ components::PayloadServiceBuilder, BuilderContext, PayloadBuilderConfig, PayloadTypes, }; @@ -33,7 +33,7 @@ impl EthereumPayloadBuilder { where Types: NodeTypesWithEngine, Node: FullNodeTypes, - Evm: ConfigureEvm

, Transaction = TxTy>, + Evm: ConfigureEvmFor>, Pool: TransactionPool>> + Unpin + 'static, diff --git a/crates/ethereum/payload/src/lib.rs b/crates/ethereum/payload/src/lib.rs index 7d172784e9d5..ca3821765ea4 100644 --- a/crates/ethereum/payload/src/lib.rs +++ b/crates/ethereum/payload/src/lib.rs @@ -19,21 +19,25 @@ use reth_basic_payload_builder::{ commit_withdrawals, is_better_payload, BuildArguments, BuildOutcome, PayloadBuilder, PayloadConfig, }; -use reth_chain_state::ExecutedBlock; +use reth_chain_state::{ExecutedBlock, ExecutedBlockWithTrieUpdates}; use reth_chainspec::{ChainSpec, ChainSpecProvider}; use reth_errors::RethError; -use reth_evm::{env::EvmEnv, system_calls::SystemCaller, ConfigureEvm, NextBlockEnvAttributes}; +use reth_evm::{ + env::EvmEnv, system_calls::SystemCaller, ConfigureEvm, Evm, NextBlockEnvAttributes, +}; use reth_evm_ethereum::{eip6110::parse_deposits_from_receipts, EthEvmConfig}; use reth_execution_types::ExecutionOutcome; use reth_payload_builder::{EthBuiltPayload, EthPayloadBuilderAttributes}; use reth_payload_builder_primitives::PayloadBuilderError; use reth_payload_primitives::PayloadBuilderAttributes; use reth_primitives::{ - proofs::{self}, Block, BlockBody, EthereumHardforks, InvalidTransactionError, Receipt, RecoveredBlock, TransactionSigned, }; -use reth_primitives_traits::{Block as _, SignedTransaction}; +use reth_primitives_traits::{ + proofs::{self}, + Block as _, SignedTransaction, +}; use reth_revm::database::StateProviderDatabase; use reth_storage_api::StateProviderFactory; use reth_transaction_pool::{ @@ -77,18 +81,18 @@ where { /// Returns the configured [`EvmEnv`] for the targeted payload /// (that has the `parent` as its parent). - fn cfg_and_block_env( + fn evm_env( &self, config: &PayloadConfig, parent: &Header, - ) -> Result { + ) -> Result, EvmConfig::Error> { let next_attributes = NextBlockEnvAttributes { timestamp: config.attributes.timestamp(), suggested_fee_recipient: config.attributes.suggested_fee_recipient(), prev_randao: config.attributes.prev_randao(), gas_limit: self.builder_config.gas_limit(parent.gas_limit), }; - self.evm_config.next_cfg_and_block_env(parent, next_attributes) + self.evm_config.next_evm_env(parent, next_attributes) } } @@ -107,7 +111,7 @@ where args: BuildArguments, ) -> Result, PayloadBuilderError> { let evm_env = self - .cfg_and_block_env(&args.config, &args.config.parent_header) + .evm_env(&args.config, &args.config.parent_header) .map_err(PayloadBuilderError::other)?; let pool = args.pool.clone(); @@ -136,7 +140,7 @@ where ); let evm_env = self - .cfg_and_block_env(&args.config, &args.config.parent_header) + .evm_env(&args.config, &args.config.parent_header) .map_err(PayloadBuilderError::other)?; let pool = args.pool.clone(); @@ -163,7 +167,7 @@ pub fn default_ethereum_payload( evm_config: EvmConfig, builder_config: EthereumBuilderConfig, args: BuildArguments, - evm_env: EvmEnv, + evm_env: EvmEnv, best_txs: F, ) -> Result, PayloadBuilderError> where @@ -224,7 +228,7 @@ where PayloadBuilderError::Internal(err.into()) })?; - let mut evm = evm_config.evm_with_env(&mut db, evm_env, Default::default()); + let mut evm = evm_config.evm_with_env(&mut db, evm_env); let mut receipts = Vec::new(); while let Some(pool_tx) = best_txs.next() { @@ -270,9 +274,9 @@ where } // Configure the environment for the tx. - *evm.tx_mut() = evm_config.tx_env(tx.tx(), tx.signer()); + let tx_env = evm_config.tx_env(tx.tx(), tx.signer()); - let ResultAndState { result, state } = match evm.transact() { + let ResultAndState { result, state } = match evm.transact(tx_env) { Ok(res) => res, Err(err) => { match err { @@ -323,13 +327,13 @@ where // Push transaction changeset and calculate header bloom filter for receipt. #[allow(clippy::needless_update)] // side-effect of optimism fields - receipts.push(Some(Receipt { + receipts.push(Receipt { tx_type: tx.tx_type(), success: result.is_success(), cumulative_gas_used, logs: result.into_logs().into_iter().collect(), ..Default::default() - })); + }); // update add to total fees let miner_fee = @@ -352,7 +356,7 @@ where // calculate the requests and the requests root let requests = if chain_spec.is_prague_active_at_timestamp(attributes.timestamp) { - let deposit_requests = parse_deposits_from_receipts(&chain_spec, receipts.iter().flatten()) + let deposit_requests = parse_deposits_from_receipts(&chain_spec, receipts.iter()) .map_err(|err| PayloadBuilderError::Internal(RethError::Execution(err.into())))?; let mut requests = Requests::default(); @@ -477,13 +481,15 @@ where debug!(target: "payload_builder", id=%attributes.id, sealed_block_header = ?sealed_block.sealed_header(), "sealed built block"); // create the executed block data - let executed = ExecutedBlock { - recovered_block: Arc::new(RecoveredBlock::new_sealed( - sealed_block.as_ref().clone(), - executed_senders, - )), - execution_output: Arc::new(execution_outcome), - hashed_state: Arc::new(hashed_state), + let executed = ExecutedBlockWithTrieUpdates { + block: ExecutedBlock { + recovered_block: Arc::new(RecoveredBlock::new_sealed( + sealed_block.as_ref().clone(), + executed_senders, + )), + execution_output: Arc::new(execution_outcome), + hashed_state: Arc::new(hashed_state), + }, trie: Arc::new(trie_output), }; diff --git a/crates/ethereum/primitives/src/alloy_compat.rs b/crates/ethereum/primitives/src/alloy_compat.rs index 6dba43025f9a..0abb521647ac 100644 --- a/crates/ethereum/primitives/src/alloy_compat.rs +++ b/crates/ethereum/primitives/src/alloy_compat.rs @@ -14,7 +14,6 @@ impl TryFrom for TransactionSigned { let WithOtherFields { inner: tx, other: _ } = tx; - #[allow(unreachable_patterns)] let (transaction, signature, hash) = match tx.inner { AnyTxEnvelope::Ethereum(TxEnvelope::Legacy(tx)) => { let (tx, signature, hash) = tx.into_parts(); @@ -39,6 +38,6 @@ impl TryFrom for TransactionSigned { _ => return Err(ConversionError::Custom("unknown transaction type".to_string())), }; - Ok(Self { transaction, signature, hash: hash.into() }) + Ok(Self::new(transaction, signature, hash)) } } diff --git a/crates/ethereum/primitives/src/lib.rs b/crates/ethereum/primitives/src/lib.rs index 79ccdf4e9d8a..f7fe5c36f4b5 100644 --- a/crates/ethereum/primitives/src/lib.rs +++ b/crates/ethereum/primitives/src/lib.rs @@ -19,3 +19,9 @@ pub use transaction::*; #[cfg(feature = "alloy-compat")] mod alloy_compat; + +/// Type alias for the ethereum block +pub type Block = alloy_consensus::Block; + +/// Type alias for the ethereum blockbody +pub type BlockBody = alloy_consensus::BlockBody; diff --git a/crates/ethereum/primitives/src/receipt.rs b/crates/ethereum/primitives/src/receipt.rs index 491a544ef5cf..23e80064e40c 100644 --- a/crates/ethereum/primitives/src/receipt.rs +++ b/crates/ethereum/primitives/src/receipt.rs @@ -3,9 +3,10 @@ use alloy_consensus::{ Eip2718EncodableReceipt, Eip658Value, ReceiptWithBloom, RlpDecodableReceipt, RlpEncodableReceipt, TxReceipt, TxType, Typed2718, }; -use alloy_primitives::{Bloom, Log}; +use alloy_eips::eip2718::Encodable2718; +use alloy_primitives::{Bloom, Log, B256}; use alloy_rlp::{BufMut, Decodable, Encodable, Header}; -use reth_primitives_traits::InMemorySize; +use reth_primitives_traits::{proofs::ordered_trie_root_with_encoder, InMemorySize}; use serde::{Deserialize, Serialize}; /// Typed ethereum transaction receipt. @@ -80,6 +81,13 @@ impl Receipt { logs_bloom, }) } + + /// Calculates the receipt root for a header for the reference type of [Receipt]. + /// + /// NOTE: Prefer `proofs::calculate_receipt_root` if you have log blooms memoized. + pub fn calculate_receipt_root_no_memo(receipts: &[&Self]) -> B256 { + ordered_trie_root_with_encoder(receipts, |r, buf| r.with_bloom_ref().encode_2718(buf)) + } } impl Eip2718EncodableReceipt for Receipt { @@ -188,9 +196,21 @@ impl reth_primitives_traits::Receipt for Receipt {} #[cfg(test)] mod tests { use super::*; + use crate::TransactionSigned; use alloy_eips::eip2718::Encodable2718; - use alloy_primitives::{address, b256, bytes, hex_literal::hex, Bytes}; + use alloy_primitives::{ + address, b256, bloom, bytes, hex_literal::hex, Address, Bytes, Log, LogData, + }; + use alloy_rlp::Decodable; use reth_codecs::Compact; + use reth_primitives_traits::proofs::{ + calculate_receipt_root, calculate_transaction_root, calculate_withdrawals_root, + }; + + /// Ethereum full block. + /// + /// Withdrawals can be optionally included at the end of the RLP encoded message. + pub(crate) type Block = alloy_consensus::Block; #[test] fn test_decode_receipt() { @@ -319,4 +339,59 @@ mod tests { "Encoded length for legacy receipt should match the actual encoded data length" ); } + + #[test] + fn check_transaction_root() { + let data = &hex!("f90262f901f9a092230ce5476ae868e98c7979cfc165a93f8b6ad1922acf2df62e340916efd49da01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa02307107a867056ca33b5087e77c4174f47625e48fb49f1c70ced34890ddd88f3a08151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcba0c598f69a5674cae9337261b669970e24abc0b46e6d284372a239ec8ccbf20b0ab901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018502540be40082a8618203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f863f861800a8405f5e10094100000000000000000000000000000000000000080801ba07e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37a05f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509bc0"); + let block_rlp = &mut data.as_slice(); + let block: Block = Block::decode(block_rlp).unwrap(); + + let tx_root = calculate_transaction_root(&block.body.transactions); + assert_eq!(block.transactions_root, tx_root, "Must be the same"); + } + + #[test] + fn check_withdrawals_root() { + // Single withdrawal, amount 0 + // https://github.com/ethereum/tests/blob/9760400e667eba241265016b02644ef62ab55de2/BlockchainTests/EIPTests/bc4895-withdrawals/amountIs0.json + let data = &hex!("f90238f90219a0151934ad9b654c50197f37018ee5ee9bb922dec0a1b5e24a6d679cb111cdb107a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa0046119afb1ab36aaa8f66088677ed96cd62762f6d3e65642898e189fbe702d51a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008001887fffffffffffffff8082079e42a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b42188000000000000000009a048a703da164234812273ea083e4ec3d09d028300cd325b46a6a75402e5a7ab95c0c0d9d8808094c94f5374fce5edbc8e2a8697c15331677e6ebf0b80"); + let block: Block = Block::decode(&mut data.as_slice()).unwrap(); + assert!(block.body.withdrawals.is_some()); + let withdrawals = block.body.withdrawals.as_ref().unwrap(); + assert_eq!(withdrawals.len(), 1); + let withdrawals_root = calculate_withdrawals_root(withdrawals); + assert_eq!(block.withdrawals_root, Some(withdrawals_root)); + + // 4 withdrawals, identical indices + // https://github.com/ethereum/tests/blob/9760400e667eba241265016b02644ef62ab55de2/BlockchainTests/EIPTests/bc4895-withdrawals/twoIdenticalIndex.json + let data = &hex!("f9028cf90219a0151934ad9b654c50197f37018ee5ee9bb922dec0a1b5e24a6d679cb111cdb107a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa0ccf7b62d616c2ad7af862d67b9dcd2119a90cebbff8c3cd1e5d7fc99f8755774a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008001887fffffffffffffff8082079e42a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b42188000000000000000009a0a95b9a7b58a6b3cb4001eb0be67951c5517141cb0183a255b5cae027a7b10b36c0c0f86cda808094c94f5374fce5edbc8e2a8697c15331677e6ebf0b822710da028094c94f5374fce5edbc8e2a8697c15331677e6ebf0b822710da018094c94f5374fce5edbc8e2a8697c15331677e6ebf0b822710da028094c94f5374fce5edbc8e2a8697c15331677e6ebf0b822710"); + let block: Block = Block::decode(&mut data.as_slice()).unwrap(); + assert!(block.body.withdrawals.is_some()); + let withdrawals = block.body.withdrawals.as_ref().unwrap(); + assert_eq!(withdrawals.len(), 4); + let withdrawals_root = calculate_withdrawals_root(withdrawals); + assert_eq!(block.withdrawals_root, Some(withdrawals_root)); + } + #[test] + fn check_receipt_root_optimism() { + use alloy_consensus::ReceiptWithBloom; + + let logs = vec![Log { + address: Address::ZERO, + data: LogData::new_unchecked(vec![], Default::default()), + }]; + let bloom = bloom!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"); + let receipt = ReceiptWithBloom { + receipt: Receipt { + tx_type: TxType::Eip2930, + success: true, + cumulative_gas_used: 102068, + logs, + }, + logs_bloom: bloom, + }; + let receipt = vec![receipt]; + let root = calculate_receipt_root(&receipt); + assert_eq!(root, b256!("fe70ae4a136d98944951b2123859698d59ad251a381abc9960fa81cae3d0d4a0")); + } } diff --git a/crates/ethereum/primitives/src/transaction.rs b/crates/ethereum/primitives/src/transaction.rs index 65cc894fcf86..2619504293e0 100644 --- a/crates/ethereum/primitives/src/transaction.rs +++ b/crates/ethereum/primitives/src/transaction.rs @@ -1,8 +1,8 @@ use alloc::vec::Vec; +pub use alloy_consensus::{transaction::PooledTransaction, TxType}; use alloy_consensus::{ - transaction::{PooledTransaction, RlpEcdsaTx}, - BlobTransactionSidecar, SignableTransaction, Signed, TxEip1559, TxEip2930, TxEip4844, - TxEip4844WithSidecar, TxEip7702, TxLegacy, TxType, Typed2718, TypedTransaction, + transaction::RlpEcdsaTx, BlobTransactionSidecar, SignableTransaction, Signed, TxEip1559, + TxEip2930, TxEip4844, TxEip4844WithSidecar, TxEip7702, TxLegacy, Typed2718, TypedTransaction, }; use alloy_eips::{ eip2718::{Decodable2718, Eip2718Error, Eip2718Result, Encodable2718}, @@ -19,7 +19,7 @@ use once_cell as _; use once_cell::sync::OnceCell as OnceLock; use reth_primitives_traits::{ crypto::secp256k1::{recover_signer, recover_signer_unchecked}, - transaction::error::TransactionConversionError, + transaction::{error::TransactionConversionError, signed::RecoveryError}, FillTxEnv, InMemorySize, SignedTransaction, }; use revm_primitives::{AuthorizationList, TxEnv}; @@ -719,12 +719,15 @@ impl SignedTransaction for TransactionSigned { &self.signature } - fn recover_signer(&self) -> Option
{ + fn recover_signer(&self) -> Result { let signature_hash = self.signature_hash(); recover_signer(&self.signature, signature_hash) } - fn recover_signer_unchecked_with_buf(&self, buf: &mut Vec) -> Option
{ + fn recover_signer_unchecked_with_buf( + &self, + buf: &mut Vec, + ) -> Result { self.encode_for_signing(buf); let signature_hash = keccak256(buf); recover_signer_unchecked(&self.signature, signature_hash) @@ -908,8 +911,21 @@ pub mod serde_bincode_compat { #[cfg(test)] mod tests { use super::*; - use alloy_eips::eip7702::constants::SECP256K1N_HALF; - use alloy_primitives::hex; + use alloy_consensus::{ + constants::LEGACY_TX_TYPE_ID, Block, Transaction as _, TxEip1559, TxLegacy, + }; + use alloy_eips::{ + eip2718::{Decodable2718, Encodable2718}, + eip7702::constants::SECP256K1N_HALF, + }; + use alloy_primitives::{ + address, b256, bytes, hex, Address, Bytes, PrimitiveSignature as Signature, TxKind, B256, + U256, + }; + use alloy_rlp::{Decodable, Encodable, Error as RlpError}; + use reth_codecs::Compact; + use reth_primitives_traits::SignedTransaction; + use std::str::FromStr; #[test] fn eip_2_reject_high_s_value() { @@ -927,9 +943,381 @@ mod tests { // recover signer, expect failure let hash = *tx.tx_hash(); - assert!(recover_signer(signature, hash).is_none()); + assert!(recover_signer(signature, hash).is_err()); // use unchecked, ensure it succeeds (the signature is valid if not for EIP-2) - assert!(recover_signer_unchecked(signature, hash).is_some()); + assert!(recover_signer_unchecked(signature, hash).is_ok()); + } + + #[test] + fn encode_decode_raw_block() { + let bytes = hex!("f90288f90218a0fe21bb173f43067a9f90cfc59bbb6830a7a2929b5de4a61f372a9db28e87f9aea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a061effbbcca94f0d3e02e5bd22e986ad57142acabf0cb3d129a6ad8d0f8752e94a0d911c25e97e27898680d242b7780b6faef30995c355a2d5de92e6b9a7212ad3aa0056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008003834c4b408252081e80a00000000000000000000000000000000000000000000000000000000000000000880000000000000000842806be9da056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421f869f86702842806be9e82520894658bdf435d810c91414ec09147daa6db624063798203e880820a95a040ce7918eeb045ebf8c8b1887ca139d076bda00fa828a07881d442a72626c42da0156576a68e456e295e4c9cf67cf9f53151f329438916e0f24fc69d6bbb7fbacfc0c0"); + let bytes_buf = &mut bytes.as_ref(); + let block = Block::::decode(bytes_buf).unwrap(); + let mut encoded_buf = Vec::with_capacity(bytes.len()); + block.encode(&mut encoded_buf); + assert_eq!(bytes[..], encoded_buf); + } + + #[test] + fn empty_block_rlp() { + let body = alloy_consensus::BlockBody::::default(); + let mut buf = Vec::new(); + body.encode(&mut buf); + let decoded = alloy_consensus::BlockBody::decode(&mut buf.as_slice()).unwrap(); + assert_eq!(body, decoded); + } + + #[test] + fn test_decode_empty_typed_tx() { + let input = [0x80u8]; + let res = TransactionSigned::decode(&mut &input[..]).unwrap_err(); + assert_eq!(RlpError::InputTooShort, res); + } + + #[test] + fn raw_kind_encoding_sanity() { + // check the 0x80 encoding for Create + let mut buf = Vec::new(); + TxKind::Create.encode(&mut buf); + assert_eq!(buf, vec![0x80]); + + // check decoding + let buf = [0x80]; + let decoded = TxKind::decode(&mut &buf[..]).unwrap(); + assert_eq!(decoded, TxKind::Create); + } + + #[test] + fn test_decode_create_goerli() { + // test that an example create tx from goerli decodes properly + let tx_bytes = hex!("b901f202f901ee05228459682f008459682f11830209bf8080b90195608060405234801561001057600080fd5b50610175806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80630c49c36c14610030575b600080fd5b61003861004e565b604051610045919061011d565b60405180910390f35b60606020600052600f6020527f68656c6c6f2073746174656d696e64000000000000000000000000000000000060405260406000f35b600081519050919050565b600082825260208201905092915050565b60005b838110156100be5780820151818401526020810190506100a3565b838111156100cd576000848401525b50505050565b6000601f19601f8301169050919050565b60006100ef82610084565b6100f9818561008f565b93506101098185602086016100a0565b610112816100d3565b840191505092915050565b6000602082019050818103600083015261013781846100e4565b90509291505056fea264697066735822122051449585839a4ea5ac23cae4552ef8a96b64ff59d0668f76bfac3796b2bdbb3664736f6c63430008090033c080a0136ebffaa8fc8b9fda9124de9ccb0b1f64e90fbd44251b4c4ac2501e60b104f9a07eb2999eec6d185ef57e91ed099afb0a926c5b536f0155dd67e537c7476e1471"); + + let decoded = TransactionSigned::decode(&mut &tx_bytes[..]).unwrap(); + assert_eq!(tx_bytes.len(), decoded.length()); + assert_eq!(tx_bytes, &alloy_rlp::encode(decoded)[..]); + } + + #[test] + fn test_decode_recover_mainnet_tx() { + // random mainnet tx + let tx_bytes = hex!("02f872018307910d808507204d2cb1827d0094388c818ca8b9251b393131c08a736a67ccb19297880320d04823e2701c80c001a0cf024f4815304df2867a1a74e9d2707b6abda0337d2d54a4438d453f4160f190a07ac0e6b3bc9395b5b9c8b9e6d77204a236577a5b18467b9175c01de4faa208d9"); + + let decoded = TransactionSigned::decode_2718(&mut &tx_bytes[..]).unwrap(); + assert_eq!( + decoded.recover_signer().unwrap(), + Address::from_str("0x95222290DD7278Aa3Ddd389Cc1E1d165CC4BAfe5").unwrap() + ); + } + + #[test] + // Test vector from https://sepolia.etherscan.io/tx/0x9a22ccb0029bc8b0ddd073be1a1d923b7ae2b2ea52100bae0db4424f9107e9c0 + // Blobscan: https://sepolia.blobscan.com/tx/0x9a22ccb0029bc8b0ddd073be1a1d923b7ae2b2ea52100bae0db4424f9107e9c0 + fn test_decode_recover_sepolia_4844_tx() { + use alloy_primitives::{address, b256}; + + // https://sepolia.etherscan.io/getRawTx?tx=0x9a22ccb0029bc8b0ddd073be1a1d923b7ae2b2ea52100bae0db4424f9107e9c0 + let raw_tx = alloy_primitives::hex::decode("0x03f9011d83aa36a7820fa28477359400852e90edd0008252089411e9ca82a3a762b4b5bd264d4173a242e7a770648080c08504a817c800f8a5a0012ec3d6f66766bedb002a190126b3549fce0047de0d4c25cffce0dc1c57921aa00152d8e24762ff22b1cfd9f8c0683786a7ca63ba49973818b3d1e9512cd2cec4a0013b98c6c83e066d5b14af2b85199e3d4fc7d1e778dd53130d180f5077e2d1c7a001148b495d6e859114e670ca54fb6e2657f0cbae5b08063605093a4b3dc9f8f1a0011ac212f13c5dff2b2c6b600a79635103d6f580a4221079951181b25c7e654901a0c8de4cced43169f9aa3d36506363b2d2c44f6c49fc1fd91ea114c86f3757077ea01e11fdd0d1934eda0492606ee0bb80a7bf8f35cc5f86ec60fe5031ba48bfd544").unwrap(); + let decoded = TransactionSigned::decode_2718(&mut raw_tx.as_slice()).unwrap(); + assert!(alloy_consensus::Typed2718::is_eip4844(&decoded)); + + assert_eq!( + decoded.recover_signer().ok(), + Some(address!("A83C816D4f9b2783761a22BA6FADB0eB0606D7B2")) + ); + + let tx = decoded.transaction; + + assert_eq!(tx.to(), Some(address!("11E9CA82A3a762b4B5bd264d4173a242e7a77064"))); + + assert_eq!( + tx.blob_versioned_hashes(), + Some( + &[ + b256!("012ec3d6f66766bedb002a190126b3549fce0047de0d4c25cffce0dc1c57921a"), + b256!("0152d8e24762ff22b1cfd9f8c0683786a7ca63ba49973818b3d1e9512cd2cec4"), + b256!("013b98c6c83e066d5b14af2b85199e3d4fc7d1e778dd53130d180f5077e2d1c7"), + b256!("01148b495d6e859114e670ca54fb6e2657f0cbae5b08063605093a4b3dc9f8f1"), + b256!("011ac212f13c5dff2b2c6b600a79635103d6f580a4221079951181b25c7e6549"), + ][..] + ) + ); + } + + #[test] + fn decode_transaction_consumes_buffer() { + let bytes = &mut &hex!("b87502f872041a8459682f008459682f0d8252089461815774383099e24810ab832a5b2a5425c154d58829a2241af62c000080c001a059e6b67f48fb32e7e570dfb11e042b5ad2e55e3ce3ce9cd989c7e06e07feeafda0016b83f4f980694ed2eee4d10667242b1f40dc406901b34125b008d334d47469")[..]; + let _transaction_res = TransactionSigned::decode(bytes).unwrap(); + assert_eq!( + bytes.len(), + 0, + "did not consume all bytes in the buffer, {:?} remaining", + bytes.len() + ); + } + + #[test] + fn decode_multiple_network_txs() { + let bytes = hex!("f86b02843b9aca00830186a094d3e8763675e4c425df46cc3b5c0f6cbdac39604687038d7ea4c68000802ba00eb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5aea03a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca18"); + let transaction = Transaction::Legacy(TxLegacy { + chain_id: Some(4u64), + nonce: 2, + gas_price: 1000000000, + gas_limit: 100000, + to: Address::from_str("d3e8763675e4c425df46cc3b5c0f6cbdac396046").unwrap().into(), + value: U256::from(1000000000000000u64), + input: Bytes::default(), + }); + let signature = Signature::new( + U256::from_str("0xeb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5ae") + .unwrap(), + U256::from_str("0x3a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca18") + .unwrap(), + false, + ); + let hash = b256!("a517b206d2223278f860ea017d3626cacad4f52ff51030dc9a96b432f17f8d34"); + test_decode_and_encode(&bytes, transaction, signature, Some(hash)); + + let bytes = hex!("f86b01843b9aca00830186a094d3e8763675e4c425df46cc3b5c0f6cbdac3960468702769bb01b2a00802ba0e24d8bd32ad906d6f8b8d7741e08d1959df021698b19ee232feba15361587d0aa05406ad177223213df262cb66ccbb2f46bfdccfdfbbb5ffdda9e2c02d977631da"); + let transaction = Transaction::Legacy(TxLegacy { + chain_id: Some(4), + nonce: 1u64, + gas_price: 1000000000, + gas_limit: 100000, + to: Address::from_slice(&hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046")[..]).into(), + value: U256::from(693361000000000u64), + input: Default::default(), + }); + let signature = Signature::new( + U256::from_str("0xe24d8bd32ad906d6f8b8d7741e08d1959df021698b19ee232feba15361587d0a") + .unwrap(), + U256::from_str("0x5406ad177223213df262cb66ccbb2f46bfdccfdfbbb5ffdda9e2c02d977631da") + .unwrap(), + false, + ); + test_decode_and_encode(&bytes, transaction, signature, None); + + let bytes = hex!("f86b0384773594008398968094d3e8763675e4c425df46cc3b5c0f6cbdac39604687038d7ea4c68000802ba0ce6834447c0a4193c40382e6c57ae33b241379c5418caac9cdc18d786fd12071a03ca3ae86580e94550d7c071e3a02eadb5a77830947c9225165cf9100901bee88"); + let transaction = Transaction::Legacy(TxLegacy { + chain_id: Some(4), + nonce: 3, + gas_price: 2000000000, + gas_limit: 10000000, + to: Address::from_slice(&hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046")[..]).into(), + value: U256::from(1000000000000000u64), + input: Bytes::default(), + }); + let signature = Signature::new( + U256::from_str("0xce6834447c0a4193c40382e6c57ae33b241379c5418caac9cdc18d786fd12071") + .unwrap(), + U256::from_str("0x3ca3ae86580e94550d7c071e3a02eadb5a77830947c9225165cf9100901bee88") + .unwrap(), + false, + ); + test_decode_and_encode(&bytes, transaction, signature, None); + + let bytes = hex!("b87502f872041a8459682f008459682f0d8252089461815774383099e24810ab832a5b2a5425c154d58829a2241af62c000080c001a059e6b67f48fb32e7e570dfb11e042b5ad2e55e3ce3ce9cd989c7e06e07feeafda0016b83f4f980694ed2eee4d10667242b1f40dc406901b34125b008d334d47469"); + let transaction = Transaction::Eip1559(TxEip1559 { + chain_id: 4, + nonce: 26, + max_priority_fee_per_gas: 1500000000, + max_fee_per_gas: 1500000013, + gas_limit: 21_000, + to: Address::from_slice(&hex!("61815774383099e24810ab832a5b2a5425c154d5")[..]).into(), + value: U256::from(3000000000000000000u64), + input: Default::default(), + access_list: Default::default(), + }); + let signature = Signature::new( + U256::from_str("0x59e6b67f48fb32e7e570dfb11e042b5ad2e55e3ce3ce9cd989c7e06e07feeafd") + .unwrap(), + U256::from_str("0x016b83f4f980694ed2eee4d10667242b1f40dc406901b34125b008d334d47469") + .unwrap(), + true, + ); + test_decode_and_encode(&bytes, transaction, signature, None); + + let bytes = hex!("f8650f84832156008287fb94cf7f9e66af820a19257a2108375b180b0ec491678204d2802ca035b7bfeb9ad9ece2cbafaaf8e202e706b4cfaeb233f46198f00b44d4a566a981a0612638fb29427ca33b9a3be2a0a561beecfe0269655be160d35e72d366a6a860"); + let transaction = Transaction::Legacy(TxLegacy { + chain_id: Some(4), + nonce: 15, + gas_price: 2200000000, + gas_limit: 34811, + to: Address::from_slice(&hex!("cf7f9e66af820a19257a2108375b180b0ec49167")[..]).into(), + value: U256::from(1234), + input: Bytes::default(), + }); + let signature = Signature::new( + U256::from_str("0x35b7bfeb9ad9ece2cbafaaf8e202e706b4cfaeb233f46198f00b44d4a566a981") + .unwrap(), + U256::from_str("0x612638fb29427ca33b9a3be2a0a561beecfe0269655be160d35e72d366a6a860") + .unwrap(), + true, + ); + test_decode_and_encode(&bytes, transaction, signature, None); + } + + fn test_decode_and_encode( + bytes: &[u8], + transaction: Transaction, + signature: Signature, + hash: Option, + ) { + let expected = TransactionSigned::new_unhashed(transaction, signature); + if let Some(hash) = hash { + assert_eq!(hash, *expected.tx_hash()); + } + assert_eq!(bytes.len(), expected.length()); + + let decoded = TransactionSigned::decode(&mut &bytes[..]).unwrap(); + assert_eq!(expected, decoded); + assert_eq!(bytes, &alloy_rlp::encode(expected)); + } + + #[test] + fn decode_raw_tx_and_recover_signer() { + use alloy_primitives::hex_literal::hex; + // transaction is from ropsten + + let hash: B256 = + hex!("559fb34c4a7f115db26cbf8505389475caaab3df45f5c7a0faa4abfa3835306c").into(); + let signer: Address = hex!("641c5d790f862a58ec7abcfd644c0442e9c201b3").into(); + let raw = hex!("f88b8212b085028fa6ae00830f424094aad593da0c8116ef7d2d594dd6a63241bccfc26c80a48318b64b000000000000000000000000641c5d790f862a58ec7abcfd644c0442e9c201b32aa0a6ef9e170bca5ffb7ac05433b13b7043de667fbb0b4a5e45d3b54fb2d6efcc63a0037ec2c05c3d60c5f5f78244ce0a3859e3a18a36c61efb061b383507d3ce19d2"); + + let mut pointer = raw.as_ref(); + let tx = TransactionSigned::decode(&mut pointer).unwrap(); + assert_eq!(*tx.tx_hash(), hash, "Expected same hash"); + let recovered = tx.recover_signer().expect("Recovering signer should pass"); + assert_eq!(recovered, signer); + } + + #[test] + fn test_envelop_encode() { + // random tx: + let input = hex!("02f871018302a90f808504890aef60826b6c94ddf4c5025d1a5742cf12f74eec246d4432c295e487e09c3bbcc12b2b80c080a0f21a4eacd0bf8fea9c5105c543be5a1d8c796516875710fafafdf16d16d8ee23a001280915021bb446d1973501a67f93d2b38894a514b976e7b46dc2fe54598d76"); + let decoded = TransactionSigned::decode(&mut &input[..]).unwrap(); + + let encoded = decoded.encoded_2718(); + assert_eq!(encoded[..], input); + } + + #[test] + fn test_envelop_decode() { + // random tx: + let input = bytes!("02f871018302a90f808504890aef60826b6c94ddf4c5025d1a5742cf12f74eec246d4432c295e487e09c3bbcc12b2b80c080a0f21a4eacd0bf8fea9c5105c543be5a1d8c796516875710fafafdf16d16d8ee23a001280915021bb446d1973501a67f93d2b38894a514b976e7b46dc2fe54598d76"); + let decoded = TransactionSigned::decode_2718(&mut input.as_ref()).unwrap(); + + let encoded = decoded.encoded_2718(); + assert_eq!(encoded, input); + } + + #[test] + fn test_decode_tx() { + // some random transactions pulled from hive tests + let data = hex!("b86f02f86c0705843b9aca008506fc23ac00830124f89400000000000000000000000000000000000003160180c001a00293c713e2f1eab91c366621ff2f867e05ad7e99d4aa5d069aafeb9e1e8c9b6aa05ec6c0605ff20b57c90a6484ec3b0509e5923733d06f9b69bee9a2dabe4f1352"); + let tx = TransactionSigned::decode(&mut data.as_slice()).unwrap(); + let mut b = Vec::with_capacity(data.len()); + tx.encode(&mut b); + assert_eq!(data.as_slice(), b.as_slice()); + + let data = hex!("f865048506fc23ac00830124f8940000000000000000000000000000000000000316018032a06b8fdfdcb84790816b7af85b19305f493665fe8b4e7c51ffdd7cc144cd776a60a028a09ab55def7b8d6602ba1c97a0ebbafe64ffc9c8e89520cec97a8edfb2ebe9"); + let tx = TransactionSigned::decode(&mut data.as_slice()).unwrap(); + let mut b = Vec::with_capacity(data.len()); + tx.encode(&mut b); + assert_eq!(data.as_slice(), b.as_slice()); + } + + // + #[test] + fn recover_legacy_singer() { + let data = hex!("f9015482078b8505d21dba0083022ef1947a250d5630b4cf539739df2c5dacb4c659f2488d880c46549a521b13d8b8e47ff36ab50000000000000000000000000000000000000000000066ab5a608bd00a23f2fe000000000000000000000000000000000000000000000000000000000000008000000000000000000000000048c04ed5691981c42154c6167398f95e8f38a7ff00000000000000000000000000000000000000000000000000000000632ceac70000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006c6ee5e31d828de241282b9606c8e98ea48526e225a0c9077369501641a92ef7399ff81c21639ed4fd8fc69cb793cfa1dbfab342e10aa0615facb2f1bcf3274a354cfe384a38d0cc008a11c2dd23a69111bc6930ba27a8"); + let tx = TransactionSigned::fallback_decode(&mut data.as_slice()).unwrap(); + assert_eq!(tx.ty(), LEGACY_TX_TYPE_ID); + let sender = tx.recover_signer().unwrap(); + assert_eq!(sender, address!("a12e1462d0ceD572f396F58B6E2D03894cD7C8a4")); + } + + // + // + #[test] + fn recover_enveloped() { + let data = hex!("02f86f0102843b9aca0085029e7822d68298f094d9e1459a7a482635700cbc20bbaf52d495ab9c9680841b55ba3ac080a0c199674fcb29f353693dd779c017823b954b3c69dffa3cd6b2a6ff7888798039a028ca912de909e7e6cdef9cdcaf24c54dd8c1032946dfa1d85c206b32a9064fe8"); + let tx = TransactionSigned::decode_2718(&mut data.as_slice()).unwrap(); + let sender = tx.recover_signer().unwrap(); + assert_eq!(sender, address!("001e2b7dE757bA469a57bF6b23d982458a07eFcE")); + assert_eq!(tx.to(), Some(address!("D9e1459A7A482635700cBc20BBAF52D495Ab9C96"))); + assert_eq!(tx.input().as_ref(), hex!("1b55ba3a")); + let encoded = tx.encoded_2718(); + assert_eq!(encoded.as_ref(), data.to_vec()); + } + + // + // + #[test] + fn recover_pre_eip2() { + let data = hex!("f8ea0c850ba43b7400832dc6c0942935aa0a2d2fbb791622c29eb1c117b65b7a908580b884590528a9000000000000000000000001878ace42092b7f1ae1f28d16c1272b1aa80ca4670000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000d02ab486cedc0000000000000000000000000000000000000000000000000000557fe293cabc08cf1ca05bfaf3fda0a56b49cc78b22125feb5ae6a99d2b4781f00507d8b02c173771c85a0b5da0dbe6c5bc53740d0071fc83eb17ba0f709e49e9ae7df60dee625ef51afc5"); + let tx = TransactionSigned::decode_2718(&mut data.as_slice()).unwrap(); + let sender = tx.recover_signer(); + assert!(sender.is_err()); + let sender = tx.recover_signer_unchecked().unwrap(); + + assert_eq!(sender, address!("7e9e359edf0dbacf96a9952fa63092d919b0842b")); + } + + #[test] + fn transaction_signed_no_hash_zstd_codec() { + // will use same signature everywhere. + // We don't need signature to match tx, just decoded to the same signature + let signature = Signature::new( + U256::from_str("0xeb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5ae") + .unwrap(), + U256::from_str("0x3a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca18") + .unwrap(), + false, + ); + + let inputs: Vec> = vec![ + vec![], + vec![0], + vec![255], + vec![1u8; 31], + vec![255u8; 31], + vec![1u8; 32], + vec![255u8; 32], + vec![1u8; 64], + vec![255u8; 64], + ]; + + for input in inputs { + let transaction = Transaction::Legacy(TxLegacy { + chain_id: Some(4u64), + nonce: 2, + gas_price: 1000000000, + gas_limit: 100000, + to: Address::from_str("d3e8763675e4c425df46cc3b5c0f6cbdac396046").unwrap().into(), + value: U256::from(1000000000000000u64), + input: Bytes::from(input), + }); + + let tx = TransactionSigned::new_unhashed(transaction, signature); + test_transaction_signed_to_from_compact(tx); + } + } + + fn test_transaction_signed_to_from_compact(tx: TransactionSigned) { + // zstd aware `to_compact` + let mut buff: Vec = Vec::new(); + let written_bytes = tx.to_compact(&mut buff); + let (decoded, _) = TransactionSigned::from_compact(&buff, written_bytes); + assert_eq!(tx, decoded); + } + + #[test] + fn create_txs_disallowed_for_eip4844() { + let data = + [3, 208, 128, 128, 123, 128, 120, 128, 129, 129, 128, 192, 129, 129, 192, 128, 128, 9]; + let res = TransactionSigned::decode_2718(&mut &data[..]); + + assert!(res.is_err()); } } diff --git a/crates/evm/execution-types/src/chain.rs b/crates/evm/execution-types/src/chain.rs index 67ed25133efd..c8e66dff79d6 100644 --- a/crates/evm/execution-types/src/chain.rs +++ b/crates/evm/execution-types/src/chain.rs @@ -1,14 +1,14 @@ //! Contains [Chain], a chain of blocks and their final state. use crate::ExecutionOutcome; -use alloc::{borrow::Cow, collections::BTreeMap}; +use alloc::{borrow::Cow, boxed::Box, collections::BTreeMap, vec::Vec}; use alloy_consensus::BlockHeader; use alloy_eips::{eip1898::ForkBlock, eip2718::Encodable2718, BlockNumHash}; use alloy_primitives::{Address, BlockHash, BlockNumber, TxHash}; use core::{fmt, ops::RangeInclusive}; use reth_execution_errors::{BlockExecutionError, InternalBlockExecutionError}; use reth_primitives::{ - transaction::SignedTransactionIntoRecoveredExt, RecoveredBlock, RecoveredTx, SealedHeader, + transaction::SignedTransactionIntoRecoveredExt, Recovered, RecoveredBlock, SealedHeader, }; use reth_primitives_traits::{Block, BlockBody, NodePrimitives, SignedTransaction}; use reth_trie::updates::TrieUpdates; @@ -170,7 +170,7 @@ impl Chain { } /// Returns an iterator over all the receipts of the blocks in the chain. - pub fn block_receipts_iter(&self) -> impl Iterator>> + '_ { + pub fn block_receipts_iter(&self) -> impl Iterator> + '_ { self.execution_outcome.receipts().iter() } @@ -182,7 +182,7 @@ impl Chain { /// Returns an iterator over all blocks and their receipts in the chain. pub fn blocks_and_receipts( &self, - ) -> impl Iterator, &Vec>)> + '_ { + ) -> impl Iterator, &Vec)> + '_ { self.blocks_iter().zip(self.block_receipts_iter()) } @@ -233,7 +233,7 @@ impl Chain { /// Get all receipts for the given block. pub fn receipts_by_block_hash(&self, block_hash: BlockHash) -> Option> { let num = self.block_number(block_hash)?; - self.execution_outcome.receipts_by_block(num).iter().map(Option::as_ref).collect() + Some(self.execution_outcome.receipts_by_block(num).iter().collect()) } /// Get all receipts with attachment. @@ -249,10 +249,7 @@ impl Chain { { let mut tx_receipts = Vec::with_capacity(receipts.len()); for (tx, receipt) in block.body().transactions().iter().zip(receipts.iter()) { - tx_receipts.push(( - tx.trie_hash(), - receipt.as_ref().expect("receipts have not been pruned").clone(), - )); + tx_receipts.push((tx.trie_hash(), receipt.clone())); } let block_num_hash = BlockNumHash::new(*block_num, block.hash()); receipt_attach.push(BlockReceipts { block: block_num_hash, tx_receipts }); @@ -345,7 +342,7 @@ impl Chain { let split_at = block_number + 1; let higher_number_blocks = self.blocks.split_off(&split_at); - let execution_outcome = std::mem::take(&mut self.execution_outcome); + let execution_outcome = core::mem::take(&mut self.execution_outcome); let (canonical_block_exec_outcome, pending_block_exec_outcome) = execution_outcome.split_at(split_at); @@ -431,7 +428,7 @@ impl>> ChainBlocks<'_, /// Returns an iterator over all transactions in the chain. #[inline] pub fn transactions(&self) -> impl Iterator::Transaction> + '_ { - self.blocks.values().flat_map(|block| block.body().transactions().iter()) + self.blocks.values().flat_map(|block| block.body().transactions_iter()) } /// Returns an iterator over all transactions and their senders. @@ -442,13 +439,13 @@ impl>> ChainBlocks<'_, self.blocks.values().flat_map(|block| block.transactions_with_sender()) } - /// Returns an iterator over all [`RecoveredTx`] in the blocks + /// Returns an iterator over all [`Recovered`] in the blocks /// /// Note: This clones the transactions since it is assumed this is part of a shared [Chain]. #[inline] pub fn transactions_ecrecovered( &self, - ) -> impl Iterator::Transaction>> + '_ { + ) -> impl Iterator::Transaction>> + '_ { self.transactions_with_sender().map(|(signer, tx)| tx.clone().with_signer(*signer)) } @@ -457,13 +454,13 @@ impl>> ChainBlocks<'_, pub fn transaction_hashes(&self) -> impl Iterator + '_ { self.blocks .values() - .flat_map(|block| block.body().transactions().iter().map(|tx| tx.trie_hash())) + .flat_map(|block| block.body().transactions_iter().map(|tx| tx.trie_hash())) } } impl IntoIterator for ChainBlocks<'_, B> { type Item = (BlockNumber, RecoveredBlock); - type IntoIter = std::collections::btree_map::IntoIter>; + type IntoIter = alloc::collections::btree_map::IntoIter>; fn into_iter(self) -> Self::IntoIter { #[allow(clippy::unnecessary_to_owned)] @@ -887,8 +884,7 @@ mod tests { }; // Create a Receipts object with a vector of receipt vectors - let receipts = - Receipts { receipt_vec: vec![vec![Some(receipt1.clone())], vec![Some(receipt2)]] }; + let receipts = Receipts { receipt_vec: vec![vec![receipt1.clone()], vec![receipt2]] }; // Create an ExecutionOutcome object with the created bundle, receipts, an empty requests // vector, and first_block set to 10 @@ -913,7 +909,7 @@ mod tests { // Create an ExecutionOutcome object with a single receipt vector containing receipt1 let execution_outcome1 = ExecutionOutcome { bundle: Default::default(), - receipts: Receipts { receipt_vec: vec![vec![Some(receipt1)]] }, + receipts: Receipts { receipt_vec: vec![vec![receipt1]] }, requests: vec![], first_block: 10, }; diff --git a/crates/evm/execution-types/src/execute.rs b/crates/evm/execution-types/src/execute.rs index 6d2a4c035ca9..6928cc993642 100644 --- a/crates/evm/execution-types/src/execute.rs +++ b/crates/evm/execution-types/src/execute.rs @@ -1,3 +1,4 @@ +use alloc::vec::Vec; use alloy_eips::eip7685::Requests; use revm::db::BundleState; diff --git a/crates/evm/execution-types/src/execution_outcome.rs b/crates/evm/execution-types/src/execution_outcome.rs index 1a0f8a5c3902..64d128c9665f 100644 --- a/crates/evm/execution-types/src/execution_outcome.rs +++ b/crates/evm/execution-types/src/execution_outcome.rs @@ -1,4 +1,5 @@ use crate::BlockExecutionOutput; +use alloc::{vec, vec::Vec}; use alloy_eips::eip7685::Requests; use alloy_primitives::{logs_bloom, map::HashMap, Address, BlockNumber, Bloom, Log, B256, U256}; use reth_primitives::Receipts; @@ -198,7 +199,7 @@ impl ExecutionOutcome { block_number: BlockNumber, f: impl FnOnce(&[&T]) -> B256, ) -> Option { - self.receipts.root_slow(self.block_number_to_index(block_number)?, f) + Some(self.receipts.root_slow(self.block_number_to_index(block_number)?, f)) } /// Returns reference to receipts. @@ -212,7 +213,7 @@ impl ExecutionOutcome { } /// Return all block receipts - pub fn receipts_by_block(&self, block_number: BlockNumber) -> &[Option] { + pub fn receipts_by_block(&self, block_number: BlockNumber) -> &[T] { let Some(index) = self.block_number_to_index(block_number) else { return &[] }; &self.receipts[index] } @@ -232,6 +233,11 @@ impl ExecutionOutcome { self.first_block } + /// Return last block of the execution outcome + pub fn last_block(&self) -> BlockNumber { + (self.first_block + self.len() as u64).saturating_sub(1) + } + /// Revert the state to the given block number. /// /// Returns false if the block number is not in the bundle state. @@ -309,13 +315,13 @@ impl ExecutionOutcome { pub fn prepend_state(&mut self, mut other: BundleState) { let other_len = other.reverts.len(); // take this bundle - let this_bundle = std::mem::take(&mut self.bundle); + let this_bundle = core::mem::take(&mut self.bundle); // extend other bundle with this other.extend(this_bundle); // discard other reverts other.take_n_reverts(other_len); // swap bundles - std::mem::swap(&mut self.bundle, &mut other) + core::mem::swap(&mut self.bundle, &mut other) } /// Create a new instance with updated receipts. @@ -346,7 +352,7 @@ impl> ExecutionOutcome { /// Returns an iterator over all block logs. pub fn logs(&self, block_number: BlockNumber) -> Option> { let index = self.block_number_to_index(block_number)?; - Some(self.receipts[index].iter().filter_map(|r| Some(r.as_ref()?.logs().iter())).flatten()) + Some(self.receipts[index].iter().flat_map(|r| r.logs())) } /// Return blocks logs bloom @@ -361,9 +367,9 @@ impl ExecutionOutcome { /// Note: this function calculated Bloom filters for every receipt and created merkle trees /// of receipt. This is a expensive operation. pub fn ethereum_receipts_root(&self, _block_number: BlockNumber) -> Option { - self.receipts.root_slow(self.block_number_to_index(_block_number)?, |receipts| { - reth_primitives::proofs::calculate_receipt_root_no_memo(receipts) - }) + Some(self.receipts.root_slow(self.block_number_to_index(_block_number)?, |receipts| { + reth_primitives::Receipt::calculate_receipt_root_no_memo(receipts) + })) } } @@ -491,12 +497,12 @@ mod tests { fn test_get_logs() { // Create a Receipts object with a vector of receipt vectors let receipts = Receipts { - receipt_vec: vec![vec![Some(reth_ethereum_primitives::Receipt { + receipt_vec: vec![vec![reth_ethereum_primitives::Receipt { tx_type: TxType::Legacy, cumulative_gas_used: 46913, logs: vec![Log::::default()], success: true, - })]], + }]], }; // Define the first block number diff --git a/crates/evm/execution-types/src/lib.rs b/crates/evm/execution-types/src/lib.rs index fb872cd596e4..15e6d3097e03 100644 --- a/crates/evm/execution-types/src/lib.rs +++ b/crates/evm/execution-types/src/lib.rs @@ -7,6 +7,7 @@ )] #![cfg_attr(not(test), warn(unused_crate_dependencies))] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +#![cfg_attr(not(feature = "std"), no_std)] extern crate alloc; diff --git a/crates/evm/src/aliases.rs b/crates/evm/src/aliases.rs new file mode 100644 index 000000000000..ef15aaf745b3 --- /dev/null +++ b/crates/evm/src/aliases.rs @@ -0,0 +1,31 @@ +//! Helper aliases when working with [`NodePrimitives`] and the traits in this crate. +use crate::{ConfigureEvm, ConfigureEvmEnv}; +use reth_primitives_traits::NodePrimitives; + +/// This is a type alias to make type bounds simpler when we have a [`NodePrimitives`] and need a +/// [`ConfigureEvmEnv`] whose associated types match the [`NodePrimitives`] associated types. +pub trait ConfigureEvmEnvFor: + ConfigureEvmEnv
+{ +} + +impl ConfigureEvmEnvFor for C +where + N: NodePrimitives, + C: ConfigureEvmEnv
, +{ +} + +/// This is a type alias to make type bounds simpler when we have a [`NodePrimitives`] and need a +/// [`ConfigureEvm`] whose associated types match the [`NodePrimitives`] associated types. +pub trait ConfigureEvmFor: + ConfigureEvm
+{ +} + +impl ConfigureEvmFor for C +where + N: NodePrimitives, + C: ConfigureEvm
, +{ +} diff --git a/crates/evm/src/either.rs b/crates/evm/src/either.rs index 4faeb1a72030..f4552715a507 100644 --- a/crates/evm/src/either.rs +++ b/crates/evm/src/either.rs @@ -6,7 +6,6 @@ use crate::{ execute::{BatchExecutor, BlockExecutorProvider, Executor}, system_calls::OnStateHook, }; -use alloc::boxed::Box; use alloy_primitives::BlockNumber; use reth_prune_types::PruneModes; use reth_storage_errors::provider::ProviderError; @@ -60,13 +59,6 @@ where type Output = A::Output; type Error = A::Error; - fn init(&mut self, tx_env_overrides: Box) { - match self { - Self::Left(a) => a.init(tx_env_overrides), - Self::Right(b) => b.init(tx_env_overrides), - } - } - fn execute(self, input: Self::Input<'_>) -> Result { match self { Self::Left(a) => a.execute(input), diff --git a/crates/evm/src/env.rs b/crates/evm/src/env.rs index df69d7bdd83b..8b10c48f4b6b 100644 --- a/crates/evm/src/env.rs +++ b/crates/evm/src/env.rs @@ -1,36 +1,27 @@ use revm::primitives::{BlockEnv, CfgEnvWithHandlerCfg}; +use revm_primitives::{CfgEnv, SpecId}; /// Container type that holds both the configuration and block environment for EVM execution. -#[derive(Debug, Clone)] -pub struct EvmEnv { +#[derive(Debug, Clone, Default)] +pub struct EvmEnv { /// The configuration environment with handler settings - pub cfg_env_with_handler_cfg: CfgEnvWithHandlerCfg, + pub cfg_env: CfgEnv, /// The block environment containing block-specific data pub block_env: BlockEnv, + /// The spec id of the chain. Specifies which hardfork is currently active, `Spec` type will + /// most likely be an enum over hardforks. + pub spec: Spec, } -impl Default for EvmEnv { - fn default() -> Self { - Self { - cfg_env_with_handler_cfg: CfgEnvWithHandlerCfg { - cfg_env: Default::default(), - // Will set `is_optimism` if `revm/optimism-default-handler` is enabled. - handler_cfg: Default::default(), - }, - block_env: BlockEnv::default(), - } - } -} - -impl EvmEnv { +impl EvmEnv { /// Create a new `EvmEnv` from its components. /// /// # Arguments /// /// * `cfg_env_with_handler_cfg` - The configuration environment with handler settings /// * `block` - The block environment containing block-specific data - pub const fn new(cfg_env_with_handler_cfg: CfgEnvWithHandlerCfg, block_env: BlockEnv) -> Self { - Self { cfg_env_with_handler_cfg, block_env } + pub const fn new(cfg_env: CfgEnv, block_env: BlockEnv, spec: Spec) -> Self { + Self { cfg_env, spec, block_env } } /// Returns a reference to the block environment. @@ -39,19 +30,19 @@ impl EvmEnv { } /// Returns a reference to the configuration environment. - pub const fn cfg_env_with_handler_cfg(&self) -> &CfgEnvWithHandlerCfg { - &self.cfg_env_with_handler_cfg + pub const fn cfg_env(&self) -> &CfgEnv { + &self.cfg_env } -} -impl From<(CfgEnvWithHandlerCfg, BlockEnv)> for EvmEnv { - fn from((cfg_env_with_handler_cfg, block_env): (CfgEnvWithHandlerCfg, BlockEnv)) -> Self { - Self { cfg_env_with_handler_cfg, block_env } + /// Returns the spec id of the chain + pub const fn spec_id(&self) -> &Spec { + &self.spec } } -impl From for (CfgEnvWithHandlerCfg, BlockEnv) { - fn from(env: EvmEnv) -> Self { - (env.cfg_env_with_handler_cfg, env.block_env) +impl From<(CfgEnvWithHandlerCfg, BlockEnv)> for EvmEnv { + fn from((cfg_env_with_handler_cfg, block_env): (CfgEnvWithHandlerCfg, BlockEnv)) -> Self { + let CfgEnvWithHandlerCfg { cfg_env, handler_cfg } = cfg_env_with_handler_cfg; + Self { cfg_env, spec: handler_cfg.spec_id, block_env } } } diff --git a/crates/evm/src/execute.rs b/crates/evm/src/execute.rs index 8ebba4c71236..404352dc3faf 100644 --- a/crates/evm/src/execute.rs +++ b/crates/evm/src/execute.rs @@ -8,7 +8,7 @@ pub use reth_execution_errors::{ pub use reth_execution_types::{BlockExecutionOutput, ExecutionOutcome}; pub use reth_storage_errors::provider::ProviderError; -use crate::{system_calls::OnStateHook, TxEnvOverrides}; +use crate::system_calls::OnStateHook; use alloc::{boxed::Box, vec::Vec}; use alloy_eips::eip7685::Requests; use alloy_primitives::{ @@ -38,9 +38,6 @@ pub trait Executor { /// The error type returned by the executor. type Error; - /// Initialize the executor with the given transaction environment overrides. - fn init(&mut self, _tx_env_overrides: Box) {} - /// Consumes the type and executes the block. /// /// # Note @@ -199,9 +196,6 @@ pub trait BlockExecutionStrategy { /// The error type returned by this strategy's methods. type Error: From + core::error::Error; - /// Initialize the strategy with the given transaction environment overrides. - fn init(&mut self, _tx_env_overrides: Box) {} - /// Applies any necessary changes before executing the block's transactions. fn apply_pre_execution_changes( &mut self, @@ -341,10 +335,6 @@ where type Output = BlockExecutionOutput<::Receipt>; type Error = S::Error; - fn init(&mut self, env_overrides: Box) { - self.strategy.init(env_overrides); - } - fn execute(mut self, block: Self::Input<'_>) -> Result { self.strategy.apply_pre_execution_changes(block)?; let ExecuteOutput { receipts, gas_used } = self.strategy.execute_transactions(block)?; @@ -444,7 +434,7 @@ where self.strategy.state_mut().merge_transitions(retention); // store receipts in the set - self.batch_record.save_receipts(receipts)?; + self.batch_record.save_receipts(receipts); // store requests in the set self.batch_record.save_requests(requests); @@ -518,7 +508,7 @@ mod tests { use reth_chainspec::{ChainSpec, MAINNET}; use reth_primitives::EthPrimitives; use revm::db::{CacheDB, EmptyDBTyped}; - use revm_primitives::{address, bytes, AccountInfo, TxEnv, KECCAK_EMPTY}; + use revm_primitives::{address, bytes, AccountInfo, KECCAK_EMPTY}; use std::sync::Arc; #[derive(Clone, Default)] @@ -734,29 +724,6 @@ mod tests { assert_eq!(block_execution_output.state, expected_finish_result); } - #[test] - fn test_tx_env_overrider() { - let strategy_factory = TestExecutorStrategyFactory { - execute_transactions_result: ExecuteOutput { - receipts: vec![Receipt::default()], - gas_used: 10, - }, - apply_post_execution_changes_result: Requests::new(vec![bytes!("deadbeef")]), - finish_result: BundleState::default(), - }; - let provider = BasicBlockExecutorProvider::new(strategy_factory); - let db = CacheDB::>::default(); - - // if we want to apply tx env overrides the executor must be mut. - let mut executor = provider.executor(db); - // execute consumes the executor, so we can only call it once. - executor.init(Box::new(|tx_env: &mut TxEnv| { - tx_env.nonce.take(); - })); - let result = executor.execute(&Default::default()); - assert!(result.is_ok()); - } - fn setup_state_with_account( addr: Address, balance: u128, diff --git a/crates/evm/src/lib.rs b/crates/evm/src/lib.rs index 1271bb14b77b..47a00252f54a 100644 --- a/crates/evm/src/lib.rs +++ b/crates/evm/src/lib.rs @@ -17,11 +17,13 @@ extern crate alloc; -use alloy_consensus::BlockHeader as _; +use alloy_consensus::transaction::Recovered; +use alloy_eips::eip2930::AccessList; use alloy_primitives::{Address, Bytes, B256, U256}; +use core::fmt::Debug; use reth_primitives_traits::{BlockHeader, SignedTransaction}; -use revm::{Database, Evm, GetInspector}; -use revm_primitives::{BlockEnv, CfgEnvWithHandlerCfg, Env, SpecId, TxEnv}; +use revm::{Database, DatabaseCommit, GetInspector}; +use revm_primitives::{BlockEnv, EVMError, ResultAndState, TxEnv, TxKind}; pub mod either; /// EVM environment configuration. @@ -29,6 +31,9 @@ pub mod env; pub mod execute; pub use env::EvmEnv; +mod aliases; +pub use aliases::*; + #[cfg(feature = "std")] pub mod metrics; pub mod noop; @@ -38,24 +43,75 @@ pub mod system_calls; /// test helpers for mocking executor pub mod test_utils; +/// An abstraction over EVM. +/// +/// At this point, assumed to be implemented on wrappers around [`revm::Evm`]. +pub trait Evm { + /// Database type held by the EVM. + type DB; + /// Transaction environment + type Tx; + /// Error type. + type Error; + + /// Reference to [`BlockEnv`]. + fn block(&self) -> &BlockEnv; + + /// Executes the given transaction. + fn transact(&mut self, tx: Self::Tx) -> Result; + + /// Executes a system call. + fn transact_system_call( + &mut self, + caller: Address, + contract: Address, + data: Bytes, + ) -> Result; + + /// Returns a mutable reference to the underlying database. + fn db_mut(&mut self) -> &mut Self::DB; + + /// Executes a transaction and commits the state changes to the underlying database. + fn transact_commit(&mut self, tx_env: Self::Tx) -> Result + where + Self::DB: DatabaseCommit, + { + let result = self.transact(tx_env)?; + self.db_mut().commit(result.state.clone()); + + Ok(result) + } +} + /// Trait for configuring the EVM for executing full blocks. pub trait ConfigureEvm: ConfigureEvmEnv { + /// The EVM implementation. + type Evm<'a, DB: Database + 'a, I: 'a>: Evm< + Tx = Self::TxEnv, + DB = DB, + Error = EVMError, + >; + /// Returns a new EVM with the given database configured with the given environment settings, /// including the spec id and transaction environment. /// /// This will preserve any handler modifications - fn evm_with_env(&self, db: DB, evm_env: EvmEnv, tx: TxEnv) -> Evm<'_, (), DB>; + fn evm_with_env( + &self, + db: DB, + evm_env: EvmEnv, + ) -> Self::Evm<'_, DB, ()>; /// Returns a new EVM with the given database configured with `cfg` and `block_env` /// configuration derived from the given header. Relies on - /// [`ConfigureEvmEnv::cfg_and_block_env`]. + /// [`ConfigureEvmEnv::evm_env`]. /// /// # Caution /// /// This does not initialize the tx environment. - fn evm_for_block(&self, db: DB, header: &Self::Header) -> Evm<'_, (), DB> { - let evm_env = self.cfg_and_block_env(header); - self.evm_with_env(db, evm_env, Default::default()) + fn evm_for_block(&self, db: DB, header: &Self::Header) -> Self::Evm<'_, DB, ()> { + let evm_env = self.evm_env(header); + self.evm_with_env(db, evm_env) } /// Returns a new EVM with the given database configured with the given environment settings, @@ -67,10 +123,9 @@ pub trait ConfigureEvm: ConfigureEvmEnv { fn evm_with_env_and_inspector( &self, db: DB, - evm_env: EvmEnv, - tx: TxEnv, + evm_env: EvmEnv, inspector: I, - ) -> Evm<'_, I, DB> + ) -> Self::Evm<'_, DB, I> where DB: Database, I: GetInspector; @@ -79,28 +134,33 @@ pub trait ConfigureEvm: ConfigureEvmEnv { impl<'b, T> ConfigureEvm for &'b T where T: ConfigureEvm, - &'b T: ConfigureEvmEnv
, + &'b T: ConfigureEvmEnv
, { - fn evm_for_block(&self, db: DB, header: &Self::Header) -> Evm<'_, (), DB> { + type Evm<'a, DB: Database + 'a, I: 'a> = T::Evm<'a, DB, I>; + + fn evm_for_block(&self, db: DB, header: &Self::Header) -> Self::Evm<'_, DB, ()> { (*self).evm_for_block(db, header) } - fn evm_with_env(&self, db: DB, evm_env: EvmEnv, tx: TxEnv) -> Evm<'_, (), DB> { - (*self).evm_with_env(db, evm_env, tx) + fn evm_with_env( + &self, + db: DB, + evm_env: EvmEnv, + ) -> Self::Evm<'_, DB, ()> { + (*self).evm_with_env(db, evm_env) } fn evm_with_env_and_inspector( &self, db: DB, - evm_env: EvmEnv, - tx_env: TxEnv, + evm_env: EvmEnv, inspector: I, - ) -> Evm<'_, I, DB> + ) -> Self::Evm<'_, DB, I> where DB: Database, I: GetInspector, { - (*self).evm_with_env_and_inspector(db, evm_env, tx_env, inspector) + (*self).evm_with_env_and_inspector(db, evm_env, inspector) } } @@ -116,99 +176,42 @@ pub trait ConfigureEvmEnv: Send + Sync + Unpin + Clone + 'static { /// The transaction type. type Transaction: SignedTransaction; - /// The error type that is returned by [`Self::next_cfg_and_block_env`]. - type Error: core::error::Error + Send + Sync; + /// Transaction environment used by EVM. + type TxEnv: TransactionEnv; - /// Returns a [`TxEnv`] from a transaction and [`Address`]. - fn tx_env(&self, transaction: &Self::Transaction, signer: Address) -> TxEnv { - let mut tx_env = TxEnv::default(); - self.fill_tx_env(&mut tx_env, transaction, signer); - tx_env - } + /// The error type that is returned by [`Self::next_evm_env`]. + type Error: core::error::Error + Send + Sync; - /// Fill transaction environment from a transaction and the given sender address. - fn fill_tx_env(&self, tx_env: &mut TxEnv, transaction: &Self::Transaction, sender: Address); + /// Identifier of the EVM specification. + type Spec: Into + Debug + Copy + Send + Sync; - /// Fill transaction environment with a system contract call. - fn fill_tx_env_system_contract_call( - &self, - env: &mut Env, - caller: Address, - contract: Address, - data: Bytes, - ); - - /// Returns a [`CfgEnvWithHandlerCfg`] for the given header. - fn cfg_env(&self, header: &Self::Header) -> CfgEnvWithHandlerCfg { - let mut cfg = CfgEnvWithHandlerCfg::new(Default::default(), Default::default()); - self.fill_cfg_env(&mut cfg, header); - cfg - } + /// Returns a [`TxEnv`] from a transaction and [`Address`]. + fn tx_env(&self, transaction: &Self::Transaction, signer: Address) -> Self::TxEnv; - /// Fill [`CfgEnvWithHandlerCfg`] fields according to the chain spec and given header. - /// - /// This __must__ set the corresponding spec id in the handler cfg, based on timestamp or total - /// difficulty - fn fill_cfg_env(&self, cfg_env: &mut CfgEnvWithHandlerCfg, header: &Self::Header); - - /// Fill [`BlockEnv`] field according to the chain spec and given header - fn fill_block_env(&self, block_env: &mut BlockEnv, header: &Self::Header, spec_id: SpecId) { - block_env.number = U256::from(header.number()); - block_env.coinbase = header.beneficiary(); - block_env.timestamp = U256::from(header.timestamp()); - if spec_id >= SpecId::MERGE { - block_env.prevrandao = header.mix_hash(); - block_env.difficulty = U256::ZERO; - } else { - block_env.difficulty = header.difficulty(); - block_env.prevrandao = None; - } - block_env.basefee = U256::from(header.base_fee_per_gas().unwrap_or_default()); - block_env.gas_limit = U256::from(header.gas_limit()); - - // EIP-4844 excess blob gas of this block, introduced in Cancun - if let Some(excess_blob_gas) = header.excess_blob_gas() { - block_env.set_blob_excess_gas_and_price(excess_blob_gas, spec_id >= SpecId::PRAGUE); - } + /// Returns a [`TxEnv`] from a [`Recovered`] transaction. + fn tx_env_from_recovered(&self, tx: Recovered<&Self::Transaction>) -> Self::TxEnv { + let (tx, address) = tx.into_parts(); + self.tx_env(tx, address) } /// Creates a new [`EvmEnv`] for the given header. - fn cfg_and_block_env(&self, header: &Self::Header) -> EvmEnv { - let mut cfg = CfgEnvWithHandlerCfg::new(Default::default(), Default::default()); - let mut block_env = BlockEnv::default(); - self.fill_cfg_and_block_env(&mut cfg, &mut block_env, header); - EvmEnv::new(cfg, block_env) - } - - /// Convenience function to call both [`fill_cfg_env`](ConfigureEvmEnv::fill_cfg_env) and - /// [`ConfigureEvmEnv::fill_block_env`]. - /// - /// Note: Implementers should ensure that all fields are required fields are filled. - fn fill_cfg_and_block_env( - &self, - cfg: &mut CfgEnvWithHandlerCfg, - block_env: &mut BlockEnv, - header: &Self::Header, - ) { - self.fill_cfg_env(cfg, header); - self.fill_block_env(block_env, header, cfg.handler_cfg.spec_id); - } + fn evm_env(&self, header: &Self::Header) -> EvmEnv; /// Returns the configured [`EvmEnv`] for `parent + 1` block. /// /// This is intended for usage in block building after the merge and requires additional /// attributes that can't be derived from the parent block: attributes that are determined by /// the CL, such as the timestamp, suggested fee recipient, and randomness value. - fn next_cfg_and_block_env( + fn next_evm_env( &self, parent: &Self::Header, attributes: NextBlockEnvAttributes, - ) -> Result; + ) -> Result, Self::Error>; } /// Represents additional attributes required to configure the next block. /// This is used to configure the next block's environment -/// [`ConfigureEvmEnv::next_cfg_and_block_env`] and contains fields that can't be derived from the +/// [`ConfigureEvmEnv::next_evm_env`] and contains fields that can't be derived from the /// parent header alone (attributes that are determined by the CL.) #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct NextBlockEnvAttributes { @@ -222,17 +225,119 @@ pub struct NextBlockEnvAttributes { pub gas_limit: u64, } -/// Function hook that allows to modify a transaction environment. -pub trait TxEnvOverrides { - /// Apply the overrides by modifying the given `TxEnv`. - fn apply(&mut self, env: &mut TxEnv); +/// Abstraction over transaction environment. +pub trait TransactionEnv: + Into + Debug + Default + Clone + Send + Sync + 'static +{ + /// Returns configured gas limit. + fn gas_limit(&self) -> u64; + + /// Set the gas limit. + fn set_gas_limit(&mut self, gas_limit: u64); + + /// Set the gas limit. + fn with_gas_limit(mut self, gas_limit: u64) -> Self { + self.set_gas_limit(gas_limit); + self + } + + /// Returns the configured nonce. + /// + /// This may return `None`, if the nonce has been intentionally unset in the environment. This + /// is useful in optimizations like transaction prewarming, where nonce checks should be + /// ignored. + fn nonce(&self) -> Option; + + /// Sets the nonce. + fn set_nonce(&mut self, nonce: u64); + + /// Sets the nonce. + fn with_nonce(mut self, nonce: u64) -> Self { + self.set_nonce(nonce); + self + } + + /// Unsets the nonce. This should be used when nonce checks for the transaction should be + /// ignored. + /// + /// See [`TransactionEnv::nonce`] for applications where this may be desired. + fn unset_nonce(&mut self); + + /// Constructs a version of this [`TransactionEnv`] that has the nonce unset. + /// + /// See [`TransactionEnv::nonce`] for applications where this may be desired. + fn without_nonce(mut self) -> Self { + self.unset_nonce(); + self + } + + /// Returns configured gas price. + fn gas_price(&self) -> U256; + + /// Returns configured value. + fn value(&self) -> U256; + + /// Caller of the transaction. + fn caller(&self) -> Address; + + /// Set access list. + fn set_access_list(&mut self, access_list: AccessList); + + /// Set access list. + fn with_access_list(mut self, access_list: AccessList) -> Self { + self.set_access_list(access_list); + self + } + + /// Returns calldata for the transaction. + fn input(&self) -> &Bytes; + + /// Returns [`TxKind`] of the transaction. + fn kind(&self) -> TxKind; } -impl TxEnvOverrides for F -where - F: FnMut(&mut TxEnv), -{ - fn apply(&mut self, env: &mut TxEnv) { - self(env) +impl TransactionEnv for TxEnv { + fn gas_limit(&self) -> u64 { + self.gas_limit + } + + fn set_gas_limit(&mut self, gas_limit: u64) { + self.gas_limit = gas_limit; + } + + fn gas_price(&self) -> U256 { + self.gas_price.to() + } + + fn nonce(&self) -> Option { + self.nonce + } + + fn set_nonce(&mut self, nonce: u64) { + self.nonce = Some(nonce); + } + + fn unset_nonce(&mut self) { + self.nonce = None; + } + + fn value(&self) -> U256 { + self.value + } + + fn caller(&self) -> Address { + self.caller + } + + fn set_access_list(&mut self, access_list: AccessList) { + self.access_list = access_list.to_vec(); + } + + fn input(&self) -> &Bytes { + &self.data + } + + fn kind(&self) -> TxKind { + self.transact_to } } diff --git a/crates/evm/src/system_calls/eip2935.rs b/crates/evm/src/system_calls/eip2935.rs index 05637d4e1d6c..6b85866d3926 100644 --- a/crates/evm/src/system_calls/eip2935.rs +++ b/crates/evm/src/system_calls/eip2935.rs @@ -1,13 +1,14 @@ //! [EIP-2935](https://eips.ethereum.org/EIPS/eip-2935) system call implementation. -use alloc::{boxed::Box, string::ToString}; +use core::fmt::Display; + +use alloc::string::ToString; use alloy_eips::eip2935::HISTORY_STORAGE_ADDRESS; -use crate::ConfigureEvm; +use crate::Evm; use alloy_primitives::B256; use reth_chainspec::EthereumHardforks; use reth_execution_errors::{BlockExecutionError, BlockValidationError}; -use revm::{interpreter::Host, Database, Evm}; use revm_primitives::ResultAndState; /// Applies the pre-block call to the [EIP-2935] blockhashes contract, using the given block, @@ -23,19 +24,13 @@ use revm_primitives::ResultAndState; /// /// [EIP-2935]: https://eips.ethereum.org/EIPS/eip-2935 #[inline] -pub(crate) fn transact_blockhashes_contract_call( - evm_config: &EvmConfig, +pub(crate) fn transact_blockhashes_contract_call( chain_spec: impl EthereumHardforks, block_timestamp: u64, block_number: u64, parent_block_hash: B256, - evm: &mut Evm<'_, EXT, DB>, -) -> Result, BlockExecutionError> -where - DB: Database, - DB::Error: core::fmt::Display, - EvmConfig: ConfigureEvm, -{ + evm: &mut impl Evm, +) -> Result, BlockExecutionError> { if !chain_spec.is_prague_active_at_timestamp(block_timestamp) { return Ok(None) } @@ -46,21 +41,13 @@ where return Ok(None) } - // get previous env - let previous_env = Box::new(evm.context.env().clone()); - - // modify env for pre block call - evm_config.fill_tx_env_system_contract_call( - &mut evm.context.evm.env, + let mut res = match evm.transact_system_call( alloy_eips::eip4788::SYSTEM_ADDRESS, HISTORY_STORAGE_ADDRESS, parent_block_hash.0.into(), - ); - - let mut res = match evm.transact() { + ) { Ok(res) => res, Err(e) => { - evm.context.evm.env = previous_env; return Err(BlockValidationError::BlockHashContractCall { message: e.to_string() }.into()) } }; @@ -73,8 +60,5 @@ where res.state.remove(&alloy_eips::eip4788::SYSTEM_ADDRESS); res.state.remove(&evm.block().coinbase); - // re-set the previous env - evm.context.evm.env = previous_env; - Ok(Some(res)) } diff --git a/crates/evm/src/system_calls/eip4788.rs b/crates/evm/src/system_calls/eip4788.rs index 4c78ff059c19..549c1cdf613f 100644 --- a/crates/evm/src/system_calls/eip4788.rs +++ b/crates/evm/src/system_calls/eip4788.rs @@ -1,12 +1,11 @@ //! [EIP-4788](https://eips.ethereum.org/EIPS/eip-4788) system call implementation. +use crate::Evm; use alloc::{boxed::Box, string::ToString}; - -use crate::ConfigureEvm; use alloy_eips::eip4788::BEACON_ROOTS_ADDRESS; use alloy_primitives::B256; +use core::fmt::Display; use reth_chainspec::EthereumHardforks; use reth_execution_errors::{BlockExecutionError, BlockValidationError}; -use revm::{interpreter::Host, Database, Evm}; use revm_primitives::ResultAndState; /// Applies the pre-block call to the [EIP-4788] beacon block root contract, using the given block, @@ -19,20 +18,13 @@ use revm_primitives::ResultAndState; /// /// [EIP-4788]: https://eips.ethereum.org/EIPS/eip-4788 #[inline] -pub(crate) fn transact_beacon_root_contract_call( - evm_config: &EvmConfig, - chain_spec: &Spec, +pub(crate) fn transact_beacon_root_contract_call( + chain_spec: impl EthereumHardforks, block_timestamp: u64, block_number: u64, parent_beacon_block_root: Option, - evm: &mut Evm<'_, EXT, DB>, -) -> Result, BlockExecutionError> -where - DB: Database, - DB::Error: core::fmt::Display, - EvmConfig: ConfigureEvm, - Spec: EthereumHardforks, -{ + evm: &mut impl Evm, +) -> Result, BlockExecutionError> { if !chain_spec.is_cancun_active_at_timestamp(block_timestamp) { return Ok(None) } @@ -52,21 +44,13 @@ where return Ok(None) } - // get previous env - let previous_env = Box::new(evm.context.env().clone()); - - // modify env for pre block call - evm_config.fill_tx_env_system_contract_call( - &mut evm.context.evm.env, + let mut res = match evm.transact_system_call( alloy_eips::eip4788::SYSTEM_ADDRESS, BEACON_ROOTS_ADDRESS, parent_beacon_block_root.0.into(), - ); - - let mut res = match evm.transact() { + ) { Ok(res) => res, Err(e) => { - evm.context.evm.env = previous_env; return Err(BlockValidationError::BeaconRootContractCall { parent_beacon_block_root: Box::new(parent_beacon_block_root), message: e.to_string(), @@ -83,8 +67,5 @@ where res.state.remove(&alloy_eips::eip4788::SYSTEM_ADDRESS); res.state.remove(&evm.block().coinbase); - // re-set the previous env - evm.context.evm.env = previous_env; - Ok(Some(res)) } diff --git a/crates/evm/src/system_calls/eip7002.rs b/crates/evm/src/system_calls/eip7002.rs index d949dd5a54a0..51db7c084a78 100644 --- a/crates/evm/src/system_calls/eip7002.rs +++ b/crates/evm/src/system_calls/eip7002.rs @@ -1,10 +1,10 @@ //! [EIP-7002](https://eips.ethereum.org/EIPS/eip-7002) system call implementation. -use crate::ConfigureEvm; -use alloc::{boxed::Box, format}; +use crate::Evm; +use alloc::format; use alloy_eips::eip7002::WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS; use alloy_primitives::Bytes; +use core::fmt::Display; use reth_execution_errors::{BlockExecutionError, BlockValidationError}; -use revm::{interpreter::Host, Database, Evm}; use revm_primitives::{ExecutionResult, ResultAndState}; /// Applies the post-block call to the EIP-7002 withdrawal requests contract. @@ -13,19 +13,10 @@ use revm_primitives::{ExecutionResult, ResultAndState}; /// /// Note: this does not commit the state changes to the database, it only transact the call. #[inline] -pub(crate) fn transact_withdrawal_requests_contract_call( - evm_config: &EvmConfig, - evm: &mut Evm<'_, EXT, DB>, -) -> Result -where - DB: Database, - DB::Error: core::fmt::Display, - EvmConfig: ConfigureEvm, -{ - // get previous env - let previous_env = Box::new(evm.context.env().clone()); - - // Fill transaction environment with the EIP-7002 withdrawal requests contract message data. +pub(crate) fn transact_withdrawal_requests_contract_call( + evm: &mut impl Evm, +) -> Result { + // Execute EIP-7002 withdrawal requests contract message data. // // This requirement for the withdrawal requests contract call defined by // [EIP-7002](https://eips.ethereum.org/EIPS/eip-7002) is: @@ -33,17 +24,13 @@ where // At the end of processing any execution block where `block.timestamp >= FORK_TIMESTAMP` (i.e. // after processing all transactions and after performing the block body withdrawal requests // validations), call the contract as `SYSTEM_ADDRESS`. - evm_config.fill_tx_env_system_contract_call( - &mut evm.context.evm.env, + let mut res = match evm.transact_system_call( alloy_eips::eip7002::SYSTEM_ADDRESS, WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS, Bytes::new(), - ); - - let mut res = match evm.transact() { + ) { Ok(res) => res, Err(e) => { - evm.context.evm.env = previous_env; return Err(BlockValidationError::WithdrawalRequestsContractCall { message: format!("execution failed: {e}"), } @@ -59,9 +46,6 @@ where res.state.remove(&alloy_eips::eip7002::SYSTEM_ADDRESS); res.state.remove(&evm.block().coinbase); - // re-set the previous env - evm.context.evm.env = previous_env; - Ok(res) } diff --git a/crates/evm/src/system_calls/eip7251.rs b/crates/evm/src/system_calls/eip7251.rs index 86a1bee53e61..3f9431b814fa 100644 --- a/crates/evm/src/system_calls/eip7251.rs +++ b/crates/evm/src/system_calls/eip7251.rs @@ -1,10 +1,10 @@ //! [EIP-7251](https://eips.ethereum.org/EIPS/eip-7251) system call implementation. -use crate::ConfigureEvm; -use alloc::{boxed::Box, format}; +use crate::Evm; +use alloc::format; use alloy_eips::eip7251::CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS; use alloy_primitives::Bytes; +use core::fmt::Display; use reth_execution_errors::{BlockExecutionError, BlockValidationError}; -use revm::{interpreter::Host, Database, Evm}; use revm_primitives::{ExecutionResult, ResultAndState}; /// Applies the post-block call to the EIP-7251 consolidation requests contract. @@ -14,19 +14,10 @@ use revm_primitives::{ExecutionResult, ResultAndState}; /// /// Note: this does not commit the state changes to the database, it only transact the call. #[inline] -pub(crate) fn transact_consolidation_requests_contract_call( - evm_config: &EvmConfig, - evm: &mut Evm<'_, EXT, DB>, -) -> Result -where - DB: Database, - DB::Error: core::fmt::Display, - EvmConfig: ConfigureEvm, -{ - // get previous env - let previous_env = Box::new(evm.context.env().clone()); - - // Fill transaction environment with the EIP-7251 consolidation requests contract message data. +pub(crate) fn transact_consolidation_requests_contract_call( + evm: &mut impl Evm, +) -> Result { + // Execute EIP-7251 consolidation requests contract message data. // // This requirement for the consolidation requests contract call defined by // [EIP-7251](https://eips.ethereum.org/EIPS/eip-7251) is: @@ -35,17 +26,13 @@ where // after processing all transactions and after performing the block body requests validations) // clienst software MUST [..] call the contract as `SYSTEM_ADDRESS` and empty input data to // trigger the system subroutine execute. - evm_config.fill_tx_env_system_contract_call( - &mut evm.context.evm.env, + let mut res = match evm.transact_system_call( alloy_eips::eip7002::SYSTEM_ADDRESS, CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS, Bytes::new(), - ); - - let mut res = match evm.transact() { + ) { Ok(res) => res, Err(e) => { - evm.context.evm.env = previous_env; return Err(BlockValidationError::ConsolidationRequestsContractCall { message: format!("execution failed: {e}"), } @@ -61,9 +48,6 @@ where res.state.remove(&alloy_eips::eip7002::SYSTEM_ADDRESS); res.state.remove(&evm.block().coinbase); - // re-set the previous env - evm.context.evm.env = previous_env; - Ok(res) } diff --git a/crates/evm/src/system_calls/mod.rs b/crates/evm/src/system_calls/mod.rs index 63527b29b219..4fa63dbdac9c 100644 --- a/crates/evm/src/system_calls/mod.rs +++ b/crates/evm/src/system_calls/mod.rs @@ -1,6 +1,6 @@ //! System contract call functions. -use crate::{ConfigureEvm, EvmEnv}; +use crate::{ConfigureEvm, Evm, EvmEnv}; use alloc::{boxed::Box, sync::Arc}; use alloy_consensus::BlockHeader; use alloy_eips::{ @@ -10,7 +10,7 @@ use alloy_primitives::Bytes; use core::fmt::Display; use reth_chainspec::EthereumHardforks; use reth_execution_errors::BlockExecutionError; -use revm::{Database, DatabaseCommit, Evm}; +use revm::{Database, DatabaseCommit}; use revm_primitives::{EvmState, B256}; mod eip2935; @@ -76,15 +76,11 @@ where Chainspec: EthereumHardforks, { /// Apply pre execution changes. - pub fn apply_pre_execution_changes( + pub fn apply_pre_execution_changes( &mut self, header: &EvmConfig::Header, - evm: &mut Evm<'_, Ext, DB>, - ) -> Result<(), BlockExecutionError> - where - DB: Database + DatabaseCommit, - DB::Error: Display, - { + evm: &mut impl Evm, + ) -> Result<(), BlockExecutionError> { self.apply_blockhashes_contract_call( header.timestamp(), header.number(), @@ -102,14 +98,10 @@ where } /// Apply post execution changes. - pub fn apply_post_execution_changes( + pub fn apply_post_execution_changes( &mut self, - evm: &mut Evm<'_, Ext, DB>, - ) -> Result - where - DB: Database + DatabaseCommit, - DB::Error: Display, - { + evm: &mut impl Evm, + ) -> Result { let mut requests = Requests::default(); // Collect all EIP-7685 requests @@ -131,7 +123,7 @@ where pub fn pre_block_blockhashes_contract_call( &mut self, db: &mut DB, - evm_env: &EvmEnv, + evm_env: &EvmEnv, parent_block_hash: B256, ) -> Result<(), BlockExecutionError> where @@ -139,7 +131,7 @@ where DB::Error: Display, { let evm_config = self.evm_config.clone(); - let mut evm = evm_config.evm_with_env(db, evm_env.clone(), Default::default()); + let mut evm = evm_config.evm_with_env(db, evm_env.clone()); self.apply_blockhashes_contract_call( evm_env.block_env.timestamp.to(), @@ -152,19 +144,14 @@ where } /// Applies the pre-block call to the EIP-2935 blockhashes contract. - pub fn apply_blockhashes_contract_call( + pub fn apply_blockhashes_contract_call( &mut self, timestamp: u64, block_number: u64, parent_block_hash: B256, - evm: &mut Evm<'_, Ext, DB>, - ) -> Result<(), BlockExecutionError> - where - DB: Database + DatabaseCommit, - DB::Error: Display, - { + evm: &mut impl Evm, + ) -> Result<(), BlockExecutionError> { let result_and_state = eip2935::transact_blockhashes_contract_call( - &self.evm_config, &self.chain_spec, timestamp, block_number, @@ -176,7 +163,7 @@ where if let Some(ref mut hook) = self.hook { hook.on_state(&res.state); } - evm.context.evm.db.commit(res.state); + evm.db_mut().commit(res.state); } Ok(()) @@ -186,7 +173,7 @@ where pub fn pre_block_beacon_root_contract_call( &mut self, db: &mut DB, - evm_env: &EvmEnv, + evm_env: &EvmEnv, parent_beacon_block_root: Option, ) -> Result<(), BlockExecutionError> where @@ -194,7 +181,7 @@ where DB::Error: Display, { let evm_config = self.evm_config.clone(); - let mut evm = evm_config.evm_with_env(db, evm_env.clone(), Default::default()); + let mut evm = evm_config.evm_with_env(db, evm_env.clone()); self.apply_beacon_root_contract_call( evm_env.block_env.timestamp.to(), @@ -207,19 +194,14 @@ where } /// Applies the pre-block call to the EIP-4788 beacon root contract. - pub fn apply_beacon_root_contract_call( + pub fn apply_beacon_root_contract_call( &mut self, timestamp: u64, block_number: u64, parent_block_hash: Option, - evm: &mut Evm<'_, Ext, DB>, - ) -> Result<(), BlockExecutionError> - where - DB: Database + DatabaseCommit, - DB::Error: Display, - { + evm: &mut impl Evm, + ) -> Result<(), BlockExecutionError> { let result_and_state = eip4788::transact_beacon_root_contract_call( - &self.evm_config, &self.chain_spec, timestamp, block_number, @@ -231,7 +213,7 @@ where if let Some(ref mut hook) = self.hook { hook.on_state(&res.state); } - evm.context.evm.db.commit(res.state); + evm.db_mut().commit(res.state); } Ok(()) @@ -241,14 +223,14 @@ where pub fn post_block_withdrawal_requests_contract_call( &mut self, db: &mut DB, - evm_env: &EvmEnv, + evm_env: &EvmEnv, ) -> Result where DB: Database + DatabaseCommit, DB::Error: Display, { let evm_config = self.evm_config.clone(); - let mut evm = evm_config.evm_with_env(db, evm_env.clone(), Default::default()); + let mut evm = evm_config.evm_with_env(db, evm_env.clone()); let result = self.apply_withdrawal_requests_contract_call(&mut evm)?; @@ -256,21 +238,16 @@ where } /// Applies the post-block call to the EIP-7002 withdrawal request contract. - pub fn apply_withdrawal_requests_contract_call( + pub fn apply_withdrawal_requests_contract_call( &mut self, - evm: &mut Evm<'_, Ext, DB>, - ) -> Result - where - DB: Database + DatabaseCommit, - DB::Error: Display, - { - let result_and_state = - eip7002::transact_withdrawal_requests_contract_call(&self.evm_config.clone(), evm)?; + evm: &mut impl Evm, + ) -> Result { + let result_and_state = eip7002::transact_withdrawal_requests_contract_call(evm)?; if let Some(ref mut hook) = self.hook { hook.on_state(&result_and_state.state); } - evm.context.evm.db.commit(result_and_state.state); + evm.db_mut().commit(result_and_state.state); eip7002::post_commit(result_and_state.result) } @@ -279,14 +256,14 @@ where pub fn post_block_consolidation_requests_contract_call( &mut self, db: &mut DB, - evm_env: &EvmEnv, + evm_env: &EvmEnv, ) -> Result where DB: Database + DatabaseCommit, DB::Error: Display, { let evm_config = self.evm_config.clone(); - let mut evm = evm_config.evm_with_env(db, evm_env.clone(), Default::default()); + let mut evm = evm_config.evm_with_env(db, evm_env.clone()); let res = self.apply_consolidation_requests_contract_call(&mut evm)?; @@ -294,21 +271,16 @@ where } /// Applies the post-block call to the EIP-7251 consolidation requests contract. - pub fn apply_consolidation_requests_contract_call( + pub fn apply_consolidation_requests_contract_call( &mut self, - evm: &mut Evm<'_, Ext, DB>, - ) -> Result - where - DB: Database + DatabaseCommit, - DB::Error: Display, - { - let result_and_state = - eip7251::transact_consolidation_requests_contract_call(&self.evm_config.clone(), evm)?; + evm: &mut impl Evm, + ) -> Result { + let result_and_state = eip7251::transact_consolidation_requests_contract_call(evm)?; if let Some(ref mut hook) = self.hook { hook.on_state(&result_and_state.state); } - evm.context.evm.db.commit(result_and_state.state); + evm.db_mut().commit(result_and_state.state); eip7251::post_commit(result_and_state.result) } diff --git a/crates/evm/src/test_utils.rs b/crates/evm/src/test_utils.rs index 2eaf7fdc5aa1..8326ac5bd51e 100644 --- a/crates/evm/src/test_utils.rs +++ b/crates/evm/src/test_utils.rs @@ -64,7 +64,7 @@ impl Executor for MockExecutorProvider { self.exec_results.lock().pop().unwrap(); Ok(BlockExecutionOutput { state: bundle, - receipts: receipts.into_iter().flatten().flatten().collect(), + receipts: receipts.into_iter().flatten().collect(), requests: requests.into_iter().fold(Requests::default(), |mut reqs, req| { reqs.extend(req); reqs diff --git a/crates/exex/exex/src/backfill/test_utils.rs b/crates/exex/exex/src/backfill/test_utils.rs index f64a09ab752e..19527fdd6454 100644 --- a/crates/exex/exex/src/backfill/test_utils.rs +++ b/crates/exex/exex/src/backfill/test_utils.rs @@ -3,7 +3,6 @@ use std::sync::Arc; use alloy_consensus::{constants::ETH_TO_WEI, BlockHeader, Header, TxEip2930}; use alloy_genesis::{Genesis, GenesisAccount}; use alloy_primitives::{b256, Address, TxKind, U256}; -use eyre::OptionExt; use reth_chainspec::{ChainSpec, ChainSpecBuilder, EthereumHardfork, MAINNET, MIN_TRANSACTION_GAS}; use reth_evm::execute::{BatchExecutor, BlockExecutionOutput, BlockExecutorProvider, Executor}; use reth_evm_ethereum::execute::EthExecutorProvider; @@ -121,8 +120,7 @@ fn blocks( ..Default::default() }, } - .with_recovered_senders() - .ok_or_eyre("failed to recover senders")?; + .try_into_recovered()?; // Second block resends the same transaction with increased nonce let block2 = Block { @@ -153,8 +151,7 @@ fn blocks( ..Default::default() }, } - .with_recovered_senders() - .ok_or_eyre("failed to recover senders")?; + .try_into_recovered()?; Ok((block1, block2)) } diff --git a/crates/exex/exex/src/context.rs b/crates/exex/exex/src/context.rs index deb6fa2bd8d2..9d003dec7678 100644 --- a/crates/exex/exex/src/context.rs +++ b/crates/exex/exex/src/context.rs @@ -1,6 +1,6 @@ use crate::{ExExContextDyn, ExExEvent, ExExNotifications, ExExNotificationsStream}; use reth_exex_types::ExExHead; -use reth_node_api::{FullNodeComponents, NodePrimitives, NodeTypes}; +use reth_node_api::{FullNodeComponents, NodePrimitives, NodeTypes, PrimitivesTy}; use reth_node_core::node_config::NodeConfig; use reth_primitives::Head; use reth_provider::BlockReader; @@ -62,7 +62,7 @@ where Node::Types: NodeTypes, { /// Returns dynamic version of the context - pub fn into_dyn(self) -> ExExContextDyn<::Primitives> { + pub fn into_dyn(self) -> ExExContextDyn> { ExExContextDyn::from(self) } } diff --git a/crates/exex/exex/src/dyn_context.rs b/crates/exex/exex/src/dyn_context.rs index 94f6ae81f8f7..0578fbf63441 100644 --- a/crates/exex/exex/src/dyn_context.rs +++ b/crates/exex/exex/src/dyn_context.rs @@ -4,7 +4,7 @@ use std::fmt::Debug; use reth_chainspec::{EthChainSpec, Head}; -use reth_node_api::{FullNodeComponents, HeaderTy, NodePrimitives, NodeTypes}; +use reth_node_api::{FullNodeComponents, HeaderTy, NodePrimitives, NodeTypes, PrimitivesTy}; use reth_node_core::node_config::NodeConfig; use reth_primitives::EthPrimitives; use reth_provider::BlockReader; @@ -50,7 +50,7 @@ impl Debug for ExExContextDyn { } } -impl From> for ExExContextDyn<::Primitives> +impl From> for ExExContextDyn> where Node: FullNodeComponents>, Node::Provider: Debug + BlockReader, diff --git a/crates/net/discv4/src/lib.rs b/crates/net/discv4/src/lib.rs index 9ffe8451f0e2..46627688370c 100644 --- a/crates/net/discv4/src/lib.rs +++ b/crates/net/discv4/src/lib.rs @@ -157,9 +157,10 @@ pub(crate) type IngressReceiver = mpsc::Receiver; type NodeRecordSender = OneshotSender>; -/// The Discv4 frontend +/// The Discv4 frontend. /// -/// This communicates with the [`Discv4Service`] by sending commands over a channel. +/// This is a cloneable type that communicates with the [`Discv4Service`] by sending commands over a +/// shared channel. /// /// See also [`Discv4::spawn`] #[derive(Debug, Clone)] @@ -174,11 +175,10 @@ pub struct Discv4 { node_record: Arc>, } -// === impl Discv4 === - impl Discv4 { - /// Same as [`Self::bind`] but also spawns the service onto a new task, - /// [`Discv4Service::spawn()`] + /// Same as [`Self::bind`] but also spawns the service onto a new task. + /// + /// See also: [`Discv4Service::spawn()`] pub async fn spawn( local_address: SocketAddr, local_enr: NodeRecord, @@ -421,6 +421,15 @@ impl Discv4 { /// /// This is a [Stream] to handles incoming and outgoing discv4 messages and emits updates via: /// [`Discv4Service::update_stream`]. +/// +/// This type maintains the discv Kademlia routing table and is responsible for performing lookups. +/// +/// ## Lookups +/// +/// See also [Recursive Lookups](https://github.com/ethereum/devp2p/blob/master/discv4.md#recursive-lookup). +/// Lookups are either triggered periodically or performaned on demand: [`Discv4::lookup`] +/// Newly discovered nodes are emitted as [`DiscoveryUpdate::Added`] event to all subscribers: +/// [`Discv4Service::update_stream`]. #[must_use = "Stream does nothing unless polled"] pub struct Discv4Service { /// Local address of the UDP socket. @@ -694,7 +703,7 @@ impl Discv4Service { /// Spawns this services onto a new task /// - /// Note: requires a running runtime + /// Note: requires a running tokio runtime pub fn spawn(mut self) -> JoinHandle<()> { tokio::task::spawn(async move { self.bootstrap(); diff --git a/crates/net/downloaders/src/bodies/request.rs b/crates/net/downloaders/src/bodies/request.rs index aa455f57900b..149d476b9e31 100644 --- a/crates/net/downloaders/src/bodies/request.rs +++ b/crates/net/downloaders/src/bodies/request.rs @@ -25,7 +25,7 @@ use std::{ /// If the response arrived with insufficient number of bodies, the future /// will issue another request until all bodies are collected. /// -/// It then proceeds to verify the downloaded bodies. In case of an validation error, +/// It then proceeds to verify the downloaded bodies. In case of a validation error, /// the future will start over. /// /// The future will filter out any empty headers (see [`alloy_consensus::Header::is_empty`]) from diff --git a/crates/net/downloaders/src/headers/task.rs b/crates/net/downloaders/src/headers/task.rs index f2084de87282..5253ca565062 100644 --- a/crates/net/downloaders/src/headers/task.rs +++ b/crates/net/downloaders/src/headers/task.rs @@ -175,7 +175,7 @@ impl Future for SpawnedDownloader { } } -/// Commands delegated tot the spawned [`HeaderDownloader`] +/// Commands delegated to the spawned [`HeaderDownloader`] enum DownloaderUpdates { UpdateSyncGap(SealedHeader, SyncTarget), UpdateLocalHead(SealedHeader), diff --git a/crates/net/downloaders/src/metrics.rs b/crates/net/downloaders/src/metrics.rs index 5f705ea48e63..fb990d5b569d 100644 --- a/crates/net/downloaders/src/metrics.rs +++ b/crates/net/downloaders/src/metrics.rs @@ -30,7 +30,7 @@ pub struct BodyDownloaderMetrics { pub buffered_responses: Gauge, /// The number of blocks the internal buffer of the /// downloader. - /// These are bodies that have been received, but not cannot be committed yet because they're + /// These are bodies that have been received, but cannot be committed yet because they're /// not contiguous pub buffered_blocks: Gauge, /// Total amount of memory used by the buffered blocks in bytes @@ -101,7 +101,7 @@ pub struct HeaderDownloaderMetrics { pub buffered_responses: Gauge, /// The number of blocks the internal buffer of the /// downloader. - /// These are bodies that have been received, but not cannot be committed yet because they're + /// These are bodies that have been received, but cannot be committed yet because they're /// not contiguous pub buffered_blocks: Gauge, /// Total amount of memory used by the buffered blocks in bytes diff --git a/crates/net/downloaders/src/receipt_file_client.rs b/crates/net/downloaders/src/receipt_file_client.rs index 6f53a79cbe5b..8aace21efc80 100644 --- a/crates/net/downloaders/src/receipt_file_client.rs +++ b/crates/net/downloaders/src/receipt_file_client.rs @@ -121,13 +121,13 @@ where } if block_number == number { - receipts_for_block.push(Some(receipt)); + receipts_for_block.push(receipt); } else { receipts.push(receipts_for_block); // next block block_number = number; - receipts_for_block = vec![Some(receipt)]; + receipts_for_block = vec![receipt]; } } None => { @@ -585,8 +585,8 @@ mod test { assert_eq!(2, total_receipts); assert_eq!(0, first_block); assert!(receipts[0].is_empty()); - assert_eq!(receipt_block_1().receipt, receipts[1][0].clone().unwrap()); - assert_eq!(receipt_block_2().receipt, receipts[2][0].clone().unwrap()); + assert_eq!(receipt_block_1().receipt, receipts[1][0].clone()); + assert_eq!(receipt_block_2().receipt, receipts[2][0].clone()); assert!(receipts[3].is_empty()); } @@ -621,9 +621,9 @@ mod test { assert_eq!(2, total_receipts); assert_eq!(0, first_block); assert!(receipts[0].is_empty()); - assert_eq!(receipt_block_1().receipt, receipts[1][0].clone().unwrap()); + assert_eq!(receipt_block_1().receipt, receipts[1][0].clone()); assert!(receipts[2].is_empty()); - assert_eq!(receipt_block_3().receipt, receipts[3][0].clone().unwrap()); + assert_eq!(receipt_block_3().receipt, receipts[3][0].clone()); } #[tokio::test] @@ -658,9 +658,9 @@ mod test { assert_eq!(4, total_receipts); assert_eq!(0, first_block); assert!(receipts[0].is_empty()); - assert_eq!(receipt_block_1().receipt, receipts[1][0].clone().unwrap()); - assert_eq!(receipt_block_2().receipt, receipts[2][0].clone().unwrap()); - assert_eq!(receipt_block_2().receipt, receipts[2][1].clone().unwrap()); - assert_eq!(receipt_block_3().receipt, receipts[3][0].clone().unwrap()); + assert_eq!(receipt_block_1().receipt, receipts[1][0].clone()); + assert_eq!(receipt_block_2().receipt, receipts[2][0].clone()); + assert_eq!(receipt_block_2().receipt, receipts[2][1].clone()); + assert_eq!(receipt_block_3().receipt, receipts[3][0].clone()); } } diff --git a/crates/net/eth-wire-types/Cargo.toml b/crates/net/eth-wire-types/Cargo.toml index c983d911661b..5bb6f315e927 100644 --- a/crates/net/eth-wire-types/Cargo.toml +++ b/crates/net/eth-wire-types/Cargo.toml @@ -15,7 +15,7 @@ workspace = true # reth reth-chainspec.workspace = true reth-codecs-derive.workspace = true -reth-primitives.workspace = true +reth-ethereum-primitives.workspace = true reth-primitives-traits.workspace = true reth-ethereum-forks.workspace = true @@ -37,7 +37,10 @@ proptest = { workspace = true, optional = true } proptest-arbitrary-interop = { workspace = true, optional = true } [dev-dependencies] -reth-primitives = { workspace = true, features = ["arbitrary"] } +reth-ethereum-primitives = { workspace = true, features = ["arbitrary"] } +alloy-primitives = { workspace = true, features = ["arbitrary", "rand"] } +alloy-consensus = { workspace = true, features = ["arbitrary"] } +alloy-eips = { workspace = true, features = ["arbitrary"] } alloy-genesis.workspace = true alloy-chains = { workspace = true, features = ["arbitrary"] } arbitrary = { workspace = true, features = ["derive"] } @@ -57,14 +60,14 @@ std = [ "bytes/std", "derive_more/std", "reth-ethereum-forks/std", - "reth-primitives/std", + "reth-ethereum-primitives/std", "reth-primitives-traits/std", "serde?/std", "thiserror/std", "reth-chainspec/std" ] arbitrary = [ - "reth-primitives/arbitrary", + "reth-ethereum-primitives/arbitrary", "alloy-chains/arbitrary", "dep:arbitrary", "dep:proptest", diff --git a/crates/net/eth-wire-types/src/blocks.rs b/crates/net/eth-wire-types/src/blocks.rs index 764603f33387..ce8d9aedcc87 100644 --- a/crates/net/eth-wire-types/src/blocks.rs +++ b/crates/net/eth-wire-types/src/blocks.rs @@ -76,12 +76,12 @@ impl From> for GetBlockBodies { #[derive(Clone, Debug, PartialEq, Eq, RlpEncodableWrapper, RlpDecodableWrapper, Default)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))] -pub struct BlockBodies( +pub struct BlockBodies( /// The requested block bodies, each of which should correspond to a hash in the request. pub Vec, ); -generate_tests!(#[rlp, 16] BlockBodies, EthBlockBodiesTests); +generate_tests!(#[rlp, 16] BlockBodies, EthBlockBodiesTests); impl From> for BlockBodies { fn from(bodies: Vec) -> Self { @@ -99,7 +99,7 @@ mod tests { use alloy_eips::BlockHashOrNumber; use alloy_primitives::{hex, PrimitiveSignature as Signature, TxKind, U256}; use alloy_rlp::{Decodable, Encodable}; - use reth_primitives::{BlockBody, Transaction, TransactionSigned}; + use reth_ethereum_primitives::{BlockBody, Transaction, TransactionSigned}; use std::str::FromStr; #[test] diff --git a/crates/net/eth-wire-types/src/broadcast.rs b/crates/net/eth-wire-types/src/broadcast.rs index b868070982f1..ede52681f92a 100644 --- a/crates/net/eth-wire-types/src/broadcast.rs +++ b/crates/net/eth-wire-types/src/broadcast.rs @@ -12,7 +12,7 @@ use alloy_rlp::{ use core::mem; use derive_more::{Constructor, Deref, DerefMut, From, IntoIterator}; use reth_codecs_derive::{add_arbitrary_tests, generate_tests}; -use reth_primitives::TransactionSigned; +use reth_ethereum_primitives::TransactionSigned; use reth_primitives_traits::SignedTransaction; /// This informs peers of new blocks that have appeared on the network. @@ -69,14 +69,14 @@ impl From for Vec { #[derive(Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable, Default)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))] -pub struct NewBlock { +pub struct NewBlock { /// A new block. pub block: B, /// The current total difficulty. pub td: U128, } -generate_tests!(#[rlp, 25] NewBlock, EthNewBlockTests); +generate_tests!(#[rlp, 25] NewBlock, EthNewBlockTests); /// This informs peers of transactions that have appeared on the network and are not yet included /// in a block. @@ -351,7 +351,7 @@ impl proptest::prelude::Arbitrary for NewPooledTransactionHashes68 { .prop_flat_map(|len| { // Use the generated length to create vectors of TxType, usize, and B256 let types_vec = vec( - proptest_arbitrary_interop::arb::() + proptest_arbitrary_interop::arb::() .prop_map(|ty| ty as u8), len..=len, ); diff --git a/crates/net/eth-wire-types/src/capability.rs b/crates/net/eth-wire-types/src/capability.rs index 2002a03aea62..3b4ca4fc5fbd 100644 --- a/crates/net/eth-wire-types/src/capability.rs +++ b/crates/net/eth-wire-types/src/capability.rs @@ -90,9 +90,9 @@ impl From for Capability { #[cfg(any(test, feature = "arbitrary"))] impl<'a> arbitrary::Arbitrary<'a> for Capability { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { - let version = u.int_in_range(0..=32)?; // TODO: What's the max? - let name = String::arbitrary(u)?; // TODO: what possible values? - Ok(Self::new(name, version)) + let version = u.int_in_range(66..=69)?; // Valid eth protocol versions are 66-69 + // Only generate valid eth protocol name for now since it's the only supported protocol + Ok(Self::new_static("eth", version)) } } diff --git a/crates/net/eth-wire-types/src/message.rs b/crates/net/eth-wire-types/src/message.rs index d3c6e5d064c6..9dfbadce1a38 100644 --- a/crates/net/eth-wire-types/src/message.rs +++ b/crates/net/eth-wire-types/src/message.rs @@ -254,6 +254,30 @@ impl EthMessage { Self::Receipts(_) => EthMessageID::Receipts, } } + + /// Returns true if the message variant is a request. + pub const fn is_request(&self) -> bool { + matches!( + self, + Self::GetBlockBodies(_) | + Self::GetBlockHeaders(_) | + Self::GetReceipts(_) | + Self::GetPooledTransactions(_) | + Self::GetNodeData(_) + ) + } + + /// Returns true if the message variant is a response to a request. + pub const fn is_response(&self) -> bool { + matches!( + self, + Self::PooledTransactions(_) | + Self::Receipts(_) | + Self::BlockHeaders(_) | + Self::BlockBodies(_) | + Self::NodeData(_) + ) + } } impl Encodable for EthMessage { @@ -450,6 +474,7 @@ impl TryFrom for EthMessageID { /// request id. #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))] pub struct RequestPair { /// id for the contained request or response message pub request_id: u64, @@ -513,6 +538,7 @@ mod tests { }; use alloy_primitives::hex; use alloy_rlp::{Decodable, Encodable, Error}; + use reth_ethereum_primitives::BlockBody; fn encode(value: T) -> Vec { let mut buf = vec![]; @@ -605,4 +631,34 @@ mod tests { ProtocolMessage::decode_message(EthVersion::Eth68, &mut buf.as_slice()).unwrap(); assert_eq!(empty_block_bodies, decoded); } + + #[test] + fn empty_block_body_protocol() { + let empty_block_bodies = + ProtocolMessage::from(EthMessage::::BlockBodies(RequestPair { + request_id: 0, + message: vec![BlockBody { + transactions: vec![], + ommers: vec![], + withdrawals: Some(Default::default()), + }] + .into(), + })); + let mut buf = Vec::new(); + empty_block_bodies.encode(&mut buf); + let decoded = + ProtocolMessage::decode_message(EthVersion::Eth68, &mut buf.as_slice()).unwrap(); + assert_eq!(empty_block_bodies, decoded); + } + + #[test] + fn decode_block_bodies_message() { + let buf = hex!("06c48199c1c0"); + let msg = ProtocolMessage::::decode_message( + EthVersion::Eth68, + &mut &buf[..], + ) + .unwrap_err(); + assert!(matches!(msg, MessageError::RlpError(alloy_rlp::Error::InputTooShort))); + } } diff --git a/crates/net/eth-wire-types/src/primitives.rs b/crates/net/eth-wire-types/src/primitives.rs index 6bd6d17531f2..4531e176caac 100644 --- a/crates/net/eth-wire-types/src/primitives.rs +++ b/crates/net/eth-wire-types/src/primitives.rs @@ -3,8 +3,7 @@ use alloy_consensus::{RlpDecodableReceipt, RlpEncodableReceipt, TxReceipt}; use alloy_rlp::{Decodable, Encodable}; use core::fmt::Debug; -use reth_primitives::NodePrimitives; -use reth_primitives_traits::{Block, BlockBody, BlockHeader, SignedTransaction}; +use reth_primitives_traits::{Block, BlockBody, BlockHeader, NodePrimitives, SignedTransaction}; /// Abstraction over primitive types which might appear in network messages. See /// [`crate::EthMessage`] for more context. @@ -66,9 +65,9 @@ pub struct EthNetworkPrimitives; impl NetworkPrimitives for EthNetworkPrimitives { type BlockHeader = alloy_consensus::Header; - type BlockBody = reth_primitives::BlockBody; - type Block = reth_primitives::Block; - type BroadcastedTransaction = reth_primitives::TransactionSigned; - type PooledTransaction = reth_primitives::PooledTransaction; - type Receipt = reth_primitives::Receipt; + type BlockBody = reth_ethereum_primitives::BlockBody; + type Block = reth_ethereum_primitives::Block; + type BroadcastedTransaction = reth_ethereum_primitives::TransactionSigned; + type PooledTransaction = reth_ethereum_primitives::PooledTransaction; + type Receipt = reth_ethereum_primitives::Receipt; } diff --git a/crates/net/eth-wire-types/src/receipts.rs b/crates/net/eth-wire-types/src/receipts.rs index c20c237811d3..7e45c0d496d9 100644 --- a/crates/net/eth-wire-types/src/receipts.rs +++ b/crates/net/eth-wire-types/src/receipts.rs @@ -1,11 +1,11 @@ //! Implements the `GetReceipts` and `Receipts` message types. use alloc::vec::Vec; -use alloy_consensus::{RlpDecodableReceipt, RlpEncodableReceipt}; +use alloy_consensus::{ReceiptWithBloom, RlpDecodableReceipt, RlpEncodableReceipt}; use alloy_primitives::B256; use alloy_rlp::{RlpDecodableWrapper, RlpEncodableWrapper}; use reth_codecs_derive::add_arbitrary_tests; -use reth_primitives::{Receipt, ReceiptWithBloom}; +use reth_ethereum_primitives::Receipt; /// A request for transaction receipts from the given block hashes. #[derive(Clone, Debug, PartialEq, Eq, RlpEncodableWrapper, RlpDecodableWrapper, Default)] @@ -48,10 +48,11 @@ impl alloy_rlp::Decodable for Receipts { #[cfg(test)] mod tests { + use super::*; use crate::{message::RequestPair, GetReceipts, Receipts}; + use alloy_consensus::TxType; use alloy_primitives::{hex, Log}; use alloy_rlp::{Decodable, Encodable}; - use reth_primitives::{Receipt, ReceiptWithBloom, TxType}; #[test] fn roundtrip_eip1559() { diff --git a/crates/net/eth-wire-types/src/status.rs b/crates/net/eth-wire-types/src/status.rs index 157d0ce53702..d0259d2fe023 100644 --- a/crates/net/eth-wire-types/src/status.rs +++ b/crates/net/eth-wire-types/src/status.rs @@ -222,7 +222,7 @@ mod tests { use alloy_rlp::{Decodable, Encodable}; use rand::Rng; use reth_chainspec::{Chain, ChainSpec, ForkCondition, NamedChain}; - use reth_primitives::{EthereumHardfork, ForkHash, ForkId, Head}; + use reth_ethereum_forks::{EthereumHardfork, ForkHash, ForkId, Head}; use std::str::FromStr; #[test] diff --git a/crates/net/eth-wire-types/src/transactions.rs b/crates/net/eth-wire-types/src/transactions.rs index 788136791e37..1fa77df4da0a 100644 --- a/crates/net/eth-wire-types/src/transactions.rs +++ b/crates/net/eth-wire-types/src/transactions.rs @@ -1,12 +1,12 @@ //! Implements the `GetPooledTransactions` and `PooledTransactions` message types. use alloc::vec::Vec; +use alloy_consensus::transaction::PooledTransaction; use alloy_eips::eip2718::Encodable2718; use alloy_primitives::B256; use alloy_rlp::{RlpDecodableWrapper, RlpEncodableWrapper}; use derive_more::{Constructor, Deref, IntoIterator}; use reth_codecs_derive::add_arbitrary_tests; -use reth_primitives::PooledTransaction; /// A list of transaction hashes that the peer would like transaction bodies for. #[derive(Clone, Debug, PartialEq, Eq, RlpEncodableWrapper, RlpDecodableWrapper, Default)] @@ -85,11 +85,11 @@ impl Default for PooledTransactions { #[cfg(test)] mod tests { use crate::{message::RequestPair, GetPooledTransactions, PooledTransactions}; - use alloy_consensus::{TxEip1559, TxLegacy}; + use alloy_consensus::{transaction::PooledTransaction, TxEip1559, TxLegacy}; use alloy_primitives::{hex, PrimitiveSignature as Signature, TxKind, U256}; use alloy_rlp::{Decodable, Encodable}; use reth_chainspec::MIN_TRANSACTION_GAS; - use reth_primitives::{PooledTransaction, Transaction, TransactionSigned}; + use reth_ethereum_primitives::{Transaction, TransactionSigned}; use std::str::FromStr; #[test] diff --git a/crates/net/eth-wire/src/capability.rs b/crates/net/eth-wire/src/capability.rs index 0dc9119ce88d..327292b2d9d6 100644 --- a/crates/net/eth-wire/src/capability.rs +++ b/crates/net/eth-wire/src/capability.rs @@ -9,17 +9,14 @@ use crate::{ }; use alloy_primitives::bytes::Bytes; use derive_more::{Deref, DerefMut}; -use reth_eth_wire_types::{EthMessage, EthNetworkPrimitives, NetworkPrimitives}; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; use std::{ borrow::Cow, collections::{BTreeSet, HashMap}, }; -/// A Capability message consisting of the message-id and the payload +/// A Capability message consisting of the message-id and the payload. #[derive(Debug, Clone, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct RawCapabilityMessage { /// Identifier of the message. pub id: usize, @@ -37,27 +34,12 @@ impl RawCapabilityMessage { /// /// Caller must ensure that the rlp encoded `payload` matches the given `id`. /// - /// See also [`EthMessage`] + /// See also [`EthMessage`](crate::EthMessage) pub const fn eth(id: EthMessageID, payload: Bytes) -> Self { Self::new(id as usize, payload) } } -/// Various protocol related event types bubbled up from a session that need to be handled by the -/// network. -#[derive(Debug)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum CapabilityMessage { - /// Eth sub-protocol message. - #[cfg_attr( - feature = "serde", - serde(bound = "EthMessage: Serialize + serde::de::DeserializeOwned") - )] - Eth(EthMessage), - /// Any other or manually crafted eth message. - Other(RawCapabilityMessage), -} - /// This represents a shared capability, its version, and its message id offset. /// /// The [offset](SharedCapability::message_id_offset) is the message ID offset for this shared diff --git a/crates/net/eth-wire/src/protocol.rs b/crates/net/eth-wire/src/protocol.rs index b9d2eed613d8..13c39d46e1f3 100644 --- a/crates/net/eth-wire/src/protocol.rs +++ b/crates/net/eth-wire/src/protocol.rs @@ -1,4 +1,4 @@ -//! A Protocol defines a P2P subprotocol in a `RLPx` connection +//! A Protocol defines a P2P subprotocol in an `RLPx` connection use crate::{Capability, EthMessageID, EthVersion}; diff --git a/crates/net/eth-wire/src/test_utils.rs b/crates/net/eth-wire/src/test_utils.rs index 56656d60e94a..7a2b934b4776 100644 --- a/crates/net/eth-wire/src/test_utils.rs +++ b/crates/net/eth-wire/src/test_utils.rs @@ -60,7 +60,7 @@ pub async fn connect_passthrough( p2p_stream } -/// A Rplx subprotocol for testing +/// An Rplx subprotocol for testing pub mod proto { use super::*; use crate::{protocol::Protocol, Capability}; diff --git a/crates/net/network-types/Cargo.toml b/crates/net/network-types/Cargo.toml index cd73106ad23c..7068371fb807 100644 --- a/crates/net/network-types/Cargo.toml +++ b/crates/net/network-types/Cargo.toml @@ -15,7 +15,8 @@ workspace = true # reth reth-network-peers.workspace = true reth-net-banlist.workspace = true -reth-ethereum-forks.workspace = true + +alloy-eip2124.workspace = true # misc serde = { workspace = true, optional = true } @@ -29,6 +30,6 @@ tracing.workspace = true serde = [ "dep:serde", "dep:humantime-serde", - "reth-ethereum-forks/serde" + "alloy-eip2124/serde" ] test-utils = [] diff --git a/crates/net/network-types/src/peers/mod.rs b/crates/net/network-types/src/peers/mod.rs index bcfddbca3703..be4847a2c734 100644 --- a/crates/net/network-types/src/peers/mod.rs +++ b/crates/net/network-types/src/peers/mod.rs @@ -7,7 +7,7 @@ pub mod state; pub use config::{ConnectionsConfig, PeersConfig}; pub use reputation::{Reputation, ReputationChange, ReputationChangeKind, ReputationChangeWeights}; -use reth_ethereum_forks::ForkId; +use alloy_eip2124::ForkId; use tracing::trace; use crate::{ diff --git a/crates/net/network/src/eth_requests.rs b/crates/net/network/src/eth_requests.rs index fe411912089b..e0d40dfb9567 100644 --- a/crates/net/network/src/eth_requests.rs +++ b/crates/net/network/src/eth_requests.rs @@ -44,7 +44,7 @@ const MAX_HEADERS_SERVE: usize = 1024; /// `SOFT_RESPONSE_LIMIT`. const MAX_BODIES_SERVE: usize = 1024; -/// Maximum size of replies to data retrievals. +/// Maximum size of replies to data retrievals: 2MB const SOFT_RESPONSE_LIMIT: usize = 2 * 1024 * 1024; /// Manages eth related requests on top of the p2p network. @@ -158,9 +158,7 @@ where &self, _peer_id: PeerId, request: GetBlockBodies, - response: oneshot::Sender< - RequestResult::Body>>, - >, + response: oneshot::Sender::Body>>>, ) { self.metrics.eth_bodies_requests_received_total.increment(1); let mut bodies = Vec::new(); @@ -169,7 +167,7 @@ where for hash in request.0 { if let Some(block) = self.client.block_by_hash(hash).unwrap_or_default() { - let (_, body) = block.split(); + let body = block.into_body(); total_bytes += body.length(); bodies.push(body); diff --git a/crates/net/network/src/manager.rs b/crates/net/network/src/manager.rs index c63f5025b560..936d808a0d11 100644 --- a/crates/net/network/src/manager.rs +++ b/crates/net/network/src/manager.rs @@ -37,10 +37,7 @@ use crate::{ }; use futures::{Future, StreamExt}; use parking_lot::Mutex; -use reth_eth_wire::{ - capability::CapabilityMessage, Capabilities, DisconnectReason, EthNetworkPrimitives, - NetworkPrimitives, -}; +use reth_eth_wire::{DisconnectReason, EthNetworkPrimitives, NetworkPrimitives}; use reth_fs_util::{self as fs, FsPathError}; use reth_metrics::common::mpsc::UnboundedMeteredSender; use reth_network_api::{ @@ -142,7 +139,25 @@ pub struct NetworkManager { disconnect_metrics: DisconnectMetrics, } -// === impl NetworkManager === +impl NetworkManager { + /// Creates the manager of a new network with [`EthNetworkPrimitives`] types. + /// + /// ```no_run + /// # async fn f() { + /// use reth_chainspec::MAINNET; + /// use reth_network::{NetworkConfig, NetworkManager}; + /// let config = + /// NetworkConfig::builder_with_rng_secret_key().build_with_noop_provider(MAINNET.clone()); + /// let manager = NetworkManager::eth(config).await; + /// # } + /// ``` + pub async fn eth( + config: NetworkConfig, + ) -> Result { + Self::new(config).await + } +} + impl NetworkManager { /// Sets the dedicated channel for events indented for the /// [`TransactionsManager`](crate::transactions::TransactionsManager). @@ -423,20 +438,6 @@ impl NetworkManager { } } - /// Event hook for an unexpected message from the peer. - fn on_invalid_message( - &mut self, - peer_id: PeerId, - _capabilities: Arc, - _message: CapabilityMessage, - ) { - trace!(target: "net", ?peer_id, "received unexpected message"); - self.swarm - .state_mut() - .peers_mut() - .apply_reputation_change(&peer_id, ReputationChangeKind::BadProtocol); - } - /// Sends an event to the [`TransactionsManager`](crate::transactions::TransactionsManager) if /// configured. fn notify_tx_manager(&self, event: NetworkTransactionEvent) { @@ -684,10 +685,6 @@ impl NetworkManager { // handle event match event { SwarmEvent::ValidMessage { peer_id, message } => self.on_peer_message(peer_id, message), - SwarmEvent::InvalidCapabilityMessage { peer_id, capabilities, message } => { - self.on_invalid_message(peer_id, capabilities, message); - self.metrics.invalid_messages_received.increment(1); - } SwarmEvent::TcpListenerClosed { remote_addr } => { trace!(target: "net", ?remote_addr, "TCP listener closed."); } diff --git a/crates/net/network/src/message.rs b/crates/net/network/src/message.rs index 8b738adeef73..7abdbfa93d7d 100644 --- a/crates/net/network/src/message.rs +++ b/crates/net/network/src/message.rs @@ -56,6 +56,8 @@ pub enum PeerMessage { /// All `eth` request variants. EthRequest(PeerRequest), /// Any other or manually crafted eth message. + /// + /// Caution: It is expected that this is a valid `eth_` capability message. Other(RawCapabilityMessage), } diff --git a/crates/net/network/src/network.rs b/crates/net/network/src/network.rs index a25ad0490818..d71933900570 100644 --- a/crates/net/network/src/network.rs +++ b/crates/net/network/src/network.rs @@ -505,7 +505,7 @@ pub(crate) enum NetworkHandleMessage, }, /// Applies a reputation change to the given peer. diff --git a/crates/net/network/src/peers.rs b/crates/net/network/src/peers.rs index a7e981b5890d..a42a5660d37a 100644 --- a/crates/net/network/src/peers.rs +++ b/crates/net/network/src/peers.rs @@ -329,9 +329,6 @@ impl PeersManager { // start a new tick, so the peer is not immediately rewarded for the time since last tick self.tick(); - let has_in_capacity = self.connection_info.has_in_capacity(); - self.connection_info.inc_in(); - match self.peers.entry(peer_id) { Entry::Occupied(mut entry) => { let peer = entry.get_mut(); @@ -359,6 +356,10 @@ impl PeersManager { } } + let has_in_capacity = self.connection_info.has_in_capacity(); + // increment new incoming connection + self.connection_info.inc_in(); + // disconnect the peer if we don't have capacity for more inbound connections if !is_trusted && !has_in_capacity { self.queued_actions.push_back(PeerAction::Disconnect { @@ -2696,4 +2697,93 @@ mod tests { assert_eq!(record.tcp_addr(), socket_addr); assert_eq!(record.udp_addr(), socket_addr); } + + #[tokio::test] + async fn test_incoming_connection_from_banned() { + let peer = PeerId::random(); + let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 2)), 8008); + let config = PeersConfig::test().with_max_inbound(3); + let mut peers = PeersManager::new(config); + peers.add_peer(peer, PeerAddr::from_tcp(socket_addr), None); + + match event!(peers) { + PeerAction::PeerAdded(peer_id) => { + assert_eq!(peer_id, peer); + } + _ => unreachable!(), + } + match event!(peers) { + PeerAction::Connect { peer_id, .. } => { + assert_eq!(peer_id, peer); + } + _ => unreachable!(), + } + + poll_fn(|cx| { + assert!(peers.poll(cx).is_pending()); + Poll::Ready(()) + }) + .await; + + // simulate new connection drops with error + loop { + peers.on_active_session_dropped( + &socket_addr, + &peer, + &EthStreamError::InvalidMessage(reth_eth_wire::message::MessageError::Invalid( + reth_eth_wire::EthVersion::Eth68, + reth_eth_wire::EthMessageID::Status, + )), + ); + + if peers.peers.get(&peer).unwrap().is_banned() { + break; + } + + assert!(peers.on_incoming_pending_session(socket_addr.ip()).is_ok()); + peers.on_incoming_session_established(peer, socket_addr); + + match event!(peers) { + PeerAction::Connect { peer_id, .. } => { + assert_eq!(peer_id, peer); + } + _ => unreachable!(), + } + } + + assert!(peers.peers.get(&peer).unwrap().is_banned()); + + // fill all incoming slots + for _ in 0..peers.connection_info.config.max_inbound { + assert!(peers.on_incoming_pending_session(socket_addr.ip()).is_ok()); + peers.on_incoming_session_established(peer, socket_addr); + + match event!(peers) { + PeerAction::DisconnectBannedIncoming { peer_id } => { + assert_eq!(peer_id, peer); + } + _ => unreachable!(), + } + } + + poll_fn(|cx| { + assert!(peers.poll(cx).is_pending()); + Poll::Ready(()) + }) + .await; + + assert_eq!(peers.connection_info.num_inbound, 0); + + let new_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 1, 3)), 8008); + + // Assert we can still accept new connections + assert!(peers.on_incoming_pending_session(new_addr.ip()).is_ok()); + assert_eq!(peers.connection_info.num_pending_in, 1); + + // the triggered DisconnectBannedIncoming will result in dropped connections, assert that + // connection info is updated via the peer's state which would be a noop here since the + // banned peer's state is idle + peers.on_active_session_gracefully_closed(peer); + assert_eq!(peers.connection_info.num_inbound, 0); + } } diff --git a/crates/net/network/src/session/active.rs b/crates/net/network/src/session/active.rs index 475ede1baf69..6781c2032a5f 100644 --- a/crates/net/network/src/session/active.rs +++ b/crates/net/network/src/session/active.rs @@ -54,6 +54,19 @@ const SAMPLE_IMPACT: f64 = 0.1; /// Amount of RTTs before timeout const TIMEOUT_SCALING: u32 = 3; +/// Restricts the number of queued outgoing messages for larger responses: +/// - Block Bodies +/// - Receipts +/// - Headers +/// - `PooledTransactions` +/// +/// With proper softlimits in place (2MB) this targets 10MB (4+1 * 2MB) of outgoing response data. +/// +/// This parameter serves as backpressure for reading additional requests from the remote. +/// Once we've queued up more responses than this, the session should priorotize message flushing +/// before reading any more messages from the remote peer, throttling the peer. +const MAX_QUEUED_OUTGOING_RESPONSES: usize = 4; + /// The type that advances an established session by listening for incoming messages (from local /// node or read from connection) and emitting events back to the /// [`SessionManager`](super::SessionManager). @@ -122,6 +135,11 @@ impl ActiveSession { self.queued_outgoing.shrink_to_fit(); } + /// Returns how many responses we've currently queued up. + fn queued_response_count(&self) -> usize { + self.queued_outgoing.messages.iter().filter(|m| m.is_response()).count() + } + /// Handle a message read from the connection. /// /// Returns an error if the message is considered to be in violation of the protocol. @@ -596,6 +614,29 @@ impl Future for ActiveSession { }; } + // check whether we should throttle incoming messages + if this.received_requests_from_remote.len() > MAX_QUEUED_OUTGOING_RESPONSES { + // we're currently waiting for the responses to the peer's requests which aren't + // queued as outgoing yet + // + // Note: we don't need to register the waker here because we polled the requests + // above + break 'receive + } + + // we also need to check if we have multiple responses queued up + if this.queued_outgoing.messages.len() > MAX_QUEUED_OUTGOING_RESPONSES && + this.queued_response_count() > MAX_QUEUED_OUTGOING_RESPONSES + { + // if we've queued up more responses than allowed, we don't poll for new + // messages and break the receive loop early + // + // Note: we don't need to register the waker here because we still have + // queued messages and the sink impl registered the waker because we've + // already advanced it to `Pending` earlier + break 'receive + } + match this.conn.poll_next_unpin(cx) { Poll::Pending => break, Poll::Ready(None) => { @@ -740,6 +781,16 @@ pub(crate) enum OutgoingMessage { Raw(RawCapabilityMessage), } +impl OutgoingMessage { + /// Returns true if this is a response. + const fn is_response(&self) -> bool { + match self { + Self::Eth(msg) => msg.is_response(), + _ => false, + } + } +} + impl From> for OutgoingMessage { fn from(value: EthMessage) -> Self { Self::Eth(value) diff --git a/crates/net/network/src/session/handle.rs b/crates/net/network/src/session/handle.rs index d24d7ec68417..ed465d33ec23 100644 --- a/crates/net/network/src/session/handle.rs +++ b/crates/net/network/src/session/handle.rs @@ -7,8 +7,7 @@ use crate::{ }; use reth_ecies::ECIESError; use reth_eth_wire::{ - capability::CapabilityMessage, errors::EthStreamError, Capabilities, DisconnectReason, - EthVersion, NetworkPrimitives, Status, + errors::EthStreamError, Capabilities, DisconnectReason, EthVersion, NetworkPrimitives, Status, }; use reth_network_api::PeerInfo; use reth_network_peers::{NodeRecord, PeerId}; @@ -49,7 +48,7 @@ impl PendingSessionHandle { /// An established session with a remote peer. /// -/// Within an active session that supports the `Ethereum Wire Protocol `, three high-level tasks can +/// Within an active session that supports the `Ethereum Wire Protocol`, three high-level tasks can /// be performed: chain synchronization, block propagation and transaction exchange. #[derive(Debug)] pub struct ActiveSessionHandle { @@ -257,15 +256,6 @@ pub enum ActiveSessionMessage { /// Message received from the peer. message: PeerMessage, }, - /// Received a message that does not match the announced capabilities of the peer. - InvalidMessage { - /// Identifier of the remote peer. - peer_id: PeerId, - /// Announced capabilities of the remote peer. - capabilities: Arc, - /// Message received from the peer. - message: CapabilityMessage, - }, /// Received a bad message from the peer. BadMessage { /// Identifier of the remote peer. diff --git a/crates/net/network/src/session/mod.rs b/crates/net/network/src/session/mod.rs index b19281b079af..d0da757ec69d 100644 --- a/crates/net/network/src/session/mod.rs +++ b/crates/net/network/src/session/mod.rs @@ -33,9 +33,9 @@ use counter::SessionCounter; use futures::{future::Either, io, FutureExt, StreamExt}; use reth_ecies::{stream::ECIESStream, ECIESError}; use reth_eth_wire::{ - capability::CapabilityMessage, errors::EthStreamError, multiplex::RlpxProtocolMultiplexer, - Capabilities, DisconnectReason, EthVersion, HelloMessageWithProtocols, NetworkPrimitives, - Status, UnauthedEthStream, UnauthedP2PStream, + errors::EthStreamError, multiplex::RlpxProtocolMultiplexer, Capabilities, DisconnectReason, + EthVersion, HelloMessageWithProtocols, NetworkPrimitives, Status, UnauthedEthStream, + UnauthedP2PStream, }; use reth_ethereum_forks::{ForkFilter, ForkId, ForkTransition, Head}; use reth_metrics::common::mpsc::MeteredPollSender; @@ -444,9 +444,6 @@ impl SessionManager { ActiveSessionMessage::ValidMessage { peer_id, message } => { Poll::Ready(SessionEvent::ValidMessage { peer_id, message }) } - ActiveSessionMessage::InvalidMessage { peer_id, capabilities, message } => { - Poll::Ready(SessionEvent::InvalidMessage { peer_id, message, capabilities }) - } ActiveSessionMessage::BadMessage { peer_id } => { Poll::Ready(SessionEvent::BadMessage { peer_id }) } @@ -703,15 +700,6 @@ pub enum SessionEvent { /// Message received from the peer. message: PeerMessage, }, - /// Received a message that does not match the announced capabilities of the peer. - InvalidMessage { - /// The remote node's public key - peer_id: PeerId, - /// Announced capabilities of the remote peer. - capabilities: Arc, - /// Message received from the peer. - message: CapabilityMessage, - }, /// Received a bad message from the peer. BadMessage { /// Identifier of the remote peer. diff --git a/crates/net/network/src/swarm.rs b/crates/net/network/src/swarm.rs index c4a2bd14d36e..1080f61e97f3 100644 --- a/crates/net/network/src/swarm.rs +++ b/crates/net/network/src/swarm.rs @@ -8,8 +8,8 @@ use crate::{ }; use futures::Stream; use reth_eth_wire::{ - capability::CapabilityMessage, errors::EthStreamError, Capabilities, DisconnectReason, - EthNetworkPrimitives, EthVersion, NetworkPrimitives, Status, + errors::EthStreamError, Capabilities, DisconnectReason, EthNetworkPrimitives, EthVersion, + NetworkPrimitives, Status, }; use reth_network_api::{PeerRequest, PeerRequestSender}; use reth_network_peers::PeerId; @@ -149,9 +149,6 @@ impl Swarm { SessionEvent::ValidMessage { peer_id, message } => { Some(SwarmEvent::ValidMessage { peer_id, message }) } - SessionEvent::InvalidMessage { peer_id, capabilities, message } => { - Some(SwarmEvent::InvalidCapabilityMessage { peer_id, capabilities, message }) - } SessionEvent::IncomingPendingSessionClosed { remote_addr, error } => { Some(SwarmEvent::IncomingPendingSessionClosed { remote_addr, error }) } @@ -344,14 +341,6 @@ pub(crate) enum SwarmEvent { /// Message received from the peer message: PeerMessage, }, - /// Received a message that does not match the announced capabilities of the peer. - InvalidCapabilityMessage { - peer_id: PeerId, - /// Announced capabilities of the remote peer. - capabilities: Arc, - /// Message received from the peer. - message: CapabilityMessage, - }, /// Received a bad message from the peer. BadMessage { /// Identifier of the remote peer. diff --git a/crates/net/network/src/transactions/mod.rs b/crates/net/network/src/transactions/mod.rs index a1c6ceb5d4f9..0be1f1746cf9 100644 --- a/crates/net/network/src/transactions/mod.rs +++ b/crates/net/network/src/transactions/mod.rs @@ -1217,7 +1217,7 @@ where let mut new_txs = Vec::with_capacity(transactions.len()); for tx in transactions { // recover transaction - let tx = match tx.try_into_ecrecovered() { + let tx = match tx.try_into_recovered() { Ok(tx) => tx, Err(badtx) => { trace!(target: "net::tx", diff --git a/crates/net/p2p/src/headers/client.rs b/crates/net/p2p/src/headers/client.rs index 606d8f389a84..23cfcdf9d7e0 100644 --- a/crates/net/p2p/src/headers/client.rs +++ b/crates/net/p2p/src/headers/client.rs @@ -94,6 +94,8 @@ pub trait HeadersClient: DownloadClient { } /// A Future that resolves to a single block body. +/// +/// Returns `None` if the peer responded with an empty header response. #[derive(Debug)] #[must_use = "futures do nothing unless polled"] pub struct SingleHeaderRequest { diff --git a/crates/net/p2p/src/test_utils/bodies.rs b/crates/net/p2p/src/test_utils/bodies.rs index 0689d403f2ce..a51ca1ea07fd 100644 --- a/crates/net/p2p/src/test_utils/bodies.rs +++ b/crates/net/p2p/src/test_utils/bodies.rs @@ -6,6 +6,7 @@ use crate::{ }; use alloy_primitives::B256; use futures::FutureExt; +use reth_network_peers::PeerId; use reth_primitives::BlockBody; use std::fmt::{Debug, Formatter}; use tokio::sync::oneshot; @@ -23,7 +24,7 @@ impl Debug for TestBodiesClient { } impl DownloadClient for TestBodiesClient { - fn report_bad_message(&self, _peer_id: reth_network_peers::PeerId) { + fn report_bad_message(&self, _peer_id: PeerId) { // noop } diff --git a/crates/node/api/src/node.rs b/crates/node/api/src/node.rs index 498297c2db8b..088729dd8f50 100644 --- a/crates/node/api/src/node.rs +++ b/crates/node/api/src/node.rs @@ -1,17 +1,13 @@ //! Traits for configuring a node. -use crate::ConfigureEvm; use alloy_rpc_types_engine::JwtSecret; use reth_consensus::{ConsensusError, FullConsensus}; -use reth_db_api::{ - database_metrics::{DatabaseMetadata, DatabaseMetrics}, - Database, -}; +use reth_db_api::{database_metrics::DatabaseMetrics, Database}; use reth_engine_primitives::BeaconConsensusEngineHandle; -use reth_evm::execute::BlockExecutorProvider; +use reth_evm::{execute::BlockExecutorProvider, ConfigureEvmFor}; use reth_network_api::FullNetwork; use reth_node_core::node_config::NodeConfig; -use reth_node_types::{HeaderTy, NodeTypes, NodeTypesWithDBAdapter, NodeTypesWithEngine, TxTy}; +use reth_node_types::{NodeTypes, NodeTypesWithDBAdapter, NodeTypesWithEngine, TxTy}; use reth_payload_builder_primitives::PayloadBuilder; use reth_provider::FullProvider; use reth_tasks::TaskExecutor; @@ -26,7 +22,7 @@ pub trait FullNodeTypes: Send + Sync + Unpin + 'static { /// Node's types with the database. type Types: NodeTypesWithEngine; /// Underlying database type used by the node to store and retrieve data. - type DB: Database + DatabaseMetrics + DatabaseMetadata + Clone + Unpin + 'static; + type DB: Database + DatabaseMetrics + Clone + Unpin + 'static; /// The provider type used to interact with the node. type Provider: FullProvider>; } @@ -38,7 +34,7 @@ pub struct FullNodeTypesAdapter(PhantomData<(Types, DB, Pro impl FullNodeTypes for FullNodeTypesAdapter where Types: NodeTypesWithEngine, - DB: Database + DatabaseMetrics + DatabaseMetadata + Clone + Unpin + 'static, + DB: Database + DatabaseMetrics + Clone + Unpin + 'static, Provider: FullProvider>, { type Types = Types; @@ -52,7 +48,7 @@ pub trait FullNodeComponents: FullNodeTypes + Clone + 'static { type Pool: TransactionPool>> + Unpin; /// The node's EVM configuration, defining settings for the Ethereum Virtual Machine. - type Evm: ConfigureEvm
, Transaction = TxTy>; + type Evm: ConfigureEvmFor<::Primitives>; /// The type that knows how to execute blocks. type Executor: BlockExecutorProvider::Primitives>; diff --git a/crates/node/builder/src/aliases.rs b/crates/node/builder/src/aliases.rs new file mode 100644 index 000000000000..9942d3a2fd07 --- /dev/null +++ b/crates/node/builder/src/aliases.rs @@ -0,0 +1,27 @@ +use reth_network::NetworkPrimitives; +use reth_node_api::BlockBody; +use reth_provider::BlockReader; + +/// This is a type alias to make type bounds simpler, when we have a [`NetworkPrimitives`] and need +/// a [`BlockReader`] whose associated types match the [`NetworkPrimitives`] associated types. +pub trait BlockReaderFor: + BlockReader< + Block = N::Block, + Header = N::BlockHeader, + Transaction = ::Transaction, + Receipt = N::Receipt, +> +{ +} + +impl BlockReaderFor for T +where + N: NetworkPrimitives, + T: BlockReader< + Block = N::Block, + Header = N::BlockHeader, + Transaction = ::Transaction, + Receipt = N::Receipt, + >, +{ +} diff --git a/crates/node/builder/src/builder/mod.rs b/crates/node/builder/src/builder/mod.rs index 8134f8d56eaf..235e76a25584 100644 --- a/crates/node/builder/src/builder/mod.rs +++ b/crates/node/builder/src/builder/mod.rs @@ -13,10 +13,7 @@ use alloy_eips::eip4844::env_settings::EnvKzgSettings; use futures::Future; use reth_chainspec::{EthChainSpec, EthereumHardforks, Hardforks}; use reth_cli_util::get_secret_key; -use reth_db_api::{ - database::Database, - database_metrics::{DatabaseMetadata, DatabaseMetrics}, -}; +use reth_db_api::{database::Database, database_metrics::DatabaseMetrics}; use reth_engine_tree::tree::TreeConfig; use reth_exex::ExExContext; use reth_network::{ @@ -34,7 +31,7 @@ use reth_node_core::{ primitives::Head, }; use reth_provider::{ - providers::{BlockchainProvider, NodeTypesForProvider, NodeTypesForTree}, + providers::{BlockchainProvider, NodeTypesForProvider}, ChainSpecProvider, FullProvider, }; use reth_tasks::TaskExecutor; @@ -236,13 +233,13 @@ impl NodeBuilder { impl NodeBuilder where - DB: Database + DatabaseMetrics + DatabaseMetadata + Clone + Unpin + 'static, + DB: Database + DatabaseMetrics + Clone + Unpin + 'static, ChainSpec: EthChainSpec + EthereumHardforks, { /// Configures the types of the node. pub fn with_types(self) -> NodeBuilderWithTypes> where - T: NodeTypesWithEngine + NodeTypesForTree, + T: NodeTypesWithEngine + NodeTypesForProvider, { self.with_types_and_provider() } @@ -266,7 +263,7 @@ where node: N, ) -> NodeBuilderWithComponents, N::ComponentsBuilder, N::AddOns> where - N: Node, ChainSpec = ChainSpec> + NodeTypesForTree, + N: Node, ChainSpec = ChainSpec> + NodeTypesForProvider, { self.with_types().with_components(node.components_builder()).with_add_ons(node.add_ons()) } @@ -297,13 +294,13 @@ impl WithLaunchContext> { impl WithLaunchContext> where - DB: Database + DatabaseMetrics + DatabaseMetadata + Clone + Unpin + 'static, + DB: Database + DatabaseMetrics + Clone + Unpin + 'static, ChainSpec: EthChainSpec + EthereumHardforks, { /// Configures the types of the node. pub fn with_types(self) -> WithLaunchContext>> where - T: NodeTypesWithEngine + NodeTypesForTree, + T: NodeTypesWithEngine + NodeTypesForProvider, { WithLaunchContext { builder: self.builder.with_types(), task_executor: self.task_executor } } @@ -332,7 +329,7 @@ where NodeBuilderWithComponents, N::ComponentsBuilder, N::AddOns>, > where - N: Node, ChainSpec = ChainSpec> + NodeTypesForTree, + N: Node, ChainSpec = ChainSpec> + NodeTypesForProvider, { self.with_types().with_components(node.components_builder()).with_add_ons(node.add_ons()) } @@ -351,14 +348,14 @@ where >>::Node, > where - N: Node, ChainSpec = ChainSpec> + NodeTypesForTree, + N: Node, ChainSpec = ChainSpec> + NodeTypesForProvider, N::AddOns: RethRpcAddOns< NodeAdapter< RethFullAdapter, >>::Components, >, >, - N::Primitives: FullNodePrimitives, + N::Primitives: FullNodePrimitives, EngineNodeLauncher: LaunchNode< NodeBuilderWithComponents, N::ComponentsBuilder, N::AddOns>, >, @@ -548,8 +545,8 @@ where impl WithLaunchContext, CB, AO>> where - DB: Database + DatabaseMetrics + DatabaseMetadata + Clone + Unpin + 'static, - T: NodeTypesWithEngine + NodeTypesForTree, + DB: Database + DatabaseMetrics + Clone + Unpin + 'static, + T: NodeTypesWithEngine + NodeTypesForProvider, CB: NodeComponentsBuilder>, AO: RethRpcAddOns, CB::Components>>, EngineNodeLauncher: LaunchNode, CB, AO>>, diff --git a/crates/node/builder/src/components/builder.rs b/crates/node/builder/src/components/builder.rs index 4e67616cd433..a9576d8c1882 100644 --- a/crates/node/builder/src/components/builder.rs +++ b/crates/node/builder/src/components/builder.rs @@ -5,12 +5,12 @@ use crate::{ Components, ConsensusBuilder, ExecutorBuilder, NetworkBuilder, NodeComponents, PayloadServiceBuilder, PoolBuilder, }, - BuilderContext, ConfigureEvm, FullNodeTypes, + BuilderContext, FullNodeTypes, }; use reth_consensus::{ConsensusError, FullConsensus}; -use reth_evm::execute::BlockExecutorProvider; +use reth_evm::{execute::BlockExecutorProvider, ConfigureEvmFor}; use reth_network::NetworkPrimitives; -use reth_node_api::{BlockTy, BodyTy, HeaderTy, NodeTypes, NodeTypesWithEngine, TxTy}; +use reth_node_api::{BlockTy, BodyTy, HeaderTy, NodeTypesWithEngine, PrimitivesTy, TxTy}; use reth_payload_builder::PayloadBuilderHandle; use reth_transaction_pool::{PoolTransaction, TransactionPool}; use std::{future::Future, marker::PhantomData}; @@ -405,12 +405,10 @@ where Pool: TransactionPool>> + Unpin + 'static, - EVM: ConfigureEvm
, Transaction = TxTy>, - Executor: BlockExecutorProvider::Primitives>, - Cons: FullConsensus<::Primitives, Error = ConsensusError> - + Clone - + Unpin - + 'static, + EVM: ConfigureEvmFor>, + Executor: BlockExecutorProvider>, + Cons: + FullConsensus, Error = ConsensusError> + Clone + Unpin + 'static, { type Components = Components; diff --git a/crates/node/builder/src/components/consensus.rs b/crates/node/builder/src/components/consensus.rs index 0620b2507d2a..426d932e018f 100644 --- a/crates/node/builder/src/components/consensus.rs +++ b/crates/node/builder/src/components/consensus.rs @@ -1,6 +1,6 @@ //! Consensus component for the node builder. use reth_consensus::{ConsensusError, FullConsensus}; -use reth_node_api::NodeTypes; +use reth_node_api::PrimitivesTy; use crate::{BuilderContext, FullNodeTypes}; use std::future::Future; @@ -8,7 +8,7 @@ use std::future::Future; /// A type that knows how to build the consensus implementation. pub trait ConsensusBuilder: Send { /// The consensus implementation to build. - type Consensus: FullConsensus<::Primitives, Error = ConsensusError> + type Consensus: FullConsensus, Error = ConsensusError> + Clone + Unpin + 'static; @@ -23,10 +23,8 @@ pub trait ConsensusBuilder: Send { impl ConsensusBuilder for F where Node: FullNodeTypes, - Consensus: FullConsensus<::Primitives, Error = ConsensusError> - + Clone - + Unpin - + 'static, + Consensus: + FullConsensus, Error = ConsensusError> + Clone + Unpin + 'static, F: FnOnce(&BuilderContext) -> Fut + Send, Fut: Future> + Send, { diff --git a/crates/node/builder/src/components/execute.rs b/crates/node/builder/src/components/execute.rs index e3226fa8e371..038f647efe24 100644 --- a/crates/node/builder/src/components/execute.rs +++ b/crates/node/builder/src/components/execute.rs @@ -1,7 +1,7 @@ //! EVM component for the node builder. use crate::{BuilderContext, FullNodeTypes}; -use reth_evm::execute::BlockExecutorProvider; -use reth_node_api::{ConfigureEvm, HeaderTy, TxTy}; +use reth_evm::{execute::BlockExecutorProvider, ConfigureEvmFor}; +use reth_node_api::PrimitivesTy; use std::future::Future; /// A type that knows how to build the executor types. @@ -9,12 +9,10 @@ pub trait ExecutorBuilder: Send { /// The EVM config to use. /// /// This provides the node with the necessary configuration to configure an EVM. - type EVM: ConfigureEvm
, Transaction = TxTy>; + type EVM: ConfigureEvmFor>; /// The type that knows how to execute blocks. - type Executor: BlockExecutorProvider< - Primitives = ::Primitives, - >; + type Executor: BlockExecutorProvider>; /// Creates the EVM config. fn build_evm( @@ -26,9 +24,8 @@ pub trait ExecutorBuilder: Send { impl ExecutorBuilder for F where Node: FullNodeTypes, - EVM: ConfigureEvm
, Transaction = TxTy>, - Executor: - BlockExecutorProvider::Primitives>, + EVM: ConfigureEvmFor>, + Executor: BlockExecutorProvider>, F: FnOnce(&BuilderContext) -> Fut + Send, Fut: Future> + Send, { diff --git a/crates/node/builder/src/components/mod.rs b/crates/node/builder/src/components/mod.rs index 93fe031bf577..538427289def 100644 --- a/crates/node/builder/src/components/mod.rs +++ b/crates/node/builder/src/components/mod.rs @@ -24,11 +24,11 @@ use reth_network_p2p::BlockClient; use crate::{ConfigureEvm, FullNodeTypes}; use reth_consensus::{ConsensusError, FullConsensus}; -use reth_evm::execute::BlockExecutorProvider; +use reth_evm::{execute::BlockExecutorProvider, ConfigureEvmFor}; use reth_network::{NetworkHandle, NetworkPrimitives}; use reth_network_api::FullNetwork; use reth_node_api::{ - BlockTy, BodyTy, HeaderTy, NodeTypes, NodeTypesWithEngine, PayloadBuilder, TxTy, + BlockTy, BodyTy, HeaderTy, NodeTypes, NodeTypesWithEngine, PayloadBuilder, PrimitivesTy, TxTy, }; use reth_payload_builder::PayloadBuilderHandle; use reth_transaction_pool::{PoolTransaction, TransactionPool}; @@ -43,7 +43,7 @@ pub trait NodeComponents: Clone + Unpin + Send + Sync + 'stati type Pool: TransactionPool>> + Unpin; /// The node's EVM configuration, defining settings for the Ethereum Virtual Machine. - type Evm: ConfigureEvm
, Transaction = TxTy>; + type Evm: ConfigureEvmFor<::Primitives>; /// The type that knows how to execute blocks. type Executor: BlockExecutorProvider::Primitives>; @@ -112,11 +112,9 @@ where + Unpin + 'static, EVM: ConfigureEvm
, Transaction = TxTy>, - Executor: BlockExecutorProvider::Primitives>, - Cons: FullConsensus<::Primitives, Error = ConsensusError> - + Clone - + Unpin - + 'static, + Executor: BlockExecutorProvider>, + Cons: + FullConsensus, Error = ConsensusError> + Clone + Unpin + 'static, { type Pool = Pool; type Evm = EVM; diff --git a/crates/node/builder/src/launch/common.rs b/crates/node/builder/src/launch/common.rs index 72dd8f091ed8..835e0185a1b2 100644 --- a/crates/node/builder/src/launch/common.rs +++ b/crates/node/builder/src/launch/common.rs @@ -13,10 +13,7 @@ use rayon::ThreadPoolBuilder; use reth_chainspec::{Chain, EthChainSpec, EthereumHardforks}; use reth_config::{config::EtlConfig, PruneConfig}; use reth_consensus::noop::NoopConsensus; -use reth_db_api::{ - database::Database, - database_metrics::{DatabaseMetadata, DatabaseMetrics}, -}; +use reth_db_api::{database::Database, database_metrics::DatabaseMetrics}; use reth_db_common::init::{init_genesis, InitStorageError}; use reth_downloaders::{bodies::noop::NoopBodiesDownloader, headers::noop::NoopHeaderDownloader}; use reth_engine_local::MiningMode; @@ -572,7 +569,7 @@ impl > where N: NodeTypes, - DB: Database + DatabaseMetrics + DatabaseMetadata + Clone + Unpin + 'static, + DB: Database + DatabaseMetrics + Clone + Unpin + 'static, { /// Returns the configured `ProviderFactory`. const fn provider_factory(&self) -> &ProviderFactory> { diff --git a/crates/node/builder/src/launch/engine.rs b/crates/node/builder/src/launch/engine.rs index b008b00d2020..9622cca0b6bc 100644 --- a/crates/node/builder/src/launch/engine.rs +++ b/crates/node/builder/src/launch/engine.rs @@ -4,10 +4,7 @@ use alloy_consensus::BlockHeader; use futures::{future::Either, stream, stream_select, StreamExt}; use reth_chainspec::EthChainSpec; use reth_consensus_debug_client::{DebugConsensusClient, EtherscanBlockProvider}; -use reth_db_api::{ - database_metrics::{DatabaseMetadata, DatabaseMetrics}, - Database, -}; +use reth_db_api::{database_metrics::DatabaseMetrics, Database}; use reth_engine_local::{LocalEngineService, LocalPayloadAttributesBuilder}; use reth_engine_service::service::{ChainEvent, EngineService}; use reth_engine_tree::{ @@ -71,7 +68,7 @@ impl EngineNodeLauncher { impl LaunchNode> for EngineNodeLauncher where Types: NodeTypesForProvider + NodeTypesWithEngine, - DB: Database + DatabaseMetrics + DatabaseMetadata + Clone + Unpin + 'static, + DB: Database + DatabaseMetrics + Clone + Unpin + 'static, T: FullNodeTypes< Types = Types, DB = DB, diff --git a/crates/node/builder/src/launch/exex.rs b/crates/node/builder/src/launch/exex.rs index 0235dd929e2a..de84b68f87f3 100644 --- a/crates/node/builder/src/launch/exex.rs +++ b/crates/node/builder/src/launch/exex.rs @@ -9,7 +9,7 @@ use reth_exex::{ ExExContext, ExExHandle, ExExManager, ExExManagerHandle, ExExNotificationSource, Wal, DEFAULT_EXEX_MANAGER_CAPACITY, }; -use reth_node_api::{FullNodeComponents, NodeTypes}; +use reth_node_api::{FullNodeComponents, NodeTypes, PrimitivesTy}; use reth_primitives::Head; use reth_provider::CanonStateSubscriptions; use reth_tracing::tracing::{debug, info}; @@ -42,7 +42,7 @@ impl ExExLauncher { /// installed. pub async fn launch( self, - ) -> eyre::Result::Primitives>>> { + ) -> eyre::Result>>> { let Self { head, extensions, components, config_container } = self; if extensions.is_empty() { diff --git a/crates/node/builder/src/lib.rs b/crates/node/builder/src/lib.rs index a4f87c479846..fe66cec08966 100644 --- a/crates/node/builder/src/lib.rs +++ b/crates/node/builder/src/lib.rs @@ -38,6 +38,10 @@ pub mod rpc; pub mod setup; +/// Type aliases for traits that are often used together +pub mod aliases; +pub use aliases::*; + /// Support for installing the ExExs (execution extensions) in a node. pub mod exex; diff --git a/crates/node/builder/src/node.rs b/crates/node/builder/src/node.rs index 93deb47a0110..ce7d12fee3d3 100644 --- a/crates/node/builder/src/node.rs +++ b/crates/node/builder/src/node.rs @@ -7,14 +7,13 @@ use std::{ sync::Arc, }; -use reth_network::NetworkPrimitives; -use reth_node_api::{BlockBody, EngineTypes, FullNodeComponents}; +use reth_node_api::{EngineTypes, FullNodeComponents}; use reth_node_core::{ dirs::{ChainPath, DataDirPath}, node_config::NodeConfig, }; use reth_payload_builder::PayloadBuilderHandle; -use reth_provider::{BlockReader, ChainSpecProvider}; +use reth_provider::ChainSpecProvider; use reth_rpc_api::EngineApiClient; use reth_rpc_builder::{auth::AuthServerHandle, RpcServerHandle}; use reth_tasks::TaskExecutor; @@ -211,27 +210,3 @@ impl> DerefMut for FullNode: - BlockReader< - Block = N::Block, - Header = N::BlockHeader, - Transaction = ::Transaction, - Receipt = N::Receipt, -> -{ -} - -impl BlockReaderFor for T -where - N: NetworkPrimitives, - T: BlockReader< - Block = N::Block, - Header = N::BlockHeader, - Transaction = ::Transaction, - Receipt = N::Receipt, - >, -{ -} diff --git a/crates/node/core/build.rs b/crates/node/core/build.rs index 2a4c2705ed46..898770678062 100644 --- a/crates/node/core/build.rs +++ b/crates/node/core/build.rs @@ -29,7 +29,8 @@ fn main() -> Result<(), Box> { println!("cargo:rustc-env=VERGEN_GIT_SHA_SHORT={}", &sha[..8]); // Set the build profile - let profile = env::var("PROFILE")?; + let out_dir = env::var("OUT_DIR").unwrap(); + let profile = out_dir.rsplit(std::path::MAIN_SEPARATOR).nth(3).unwrap(); println!("cargo:rustc-env=RETH_BUILD_PROFILE={profile}"); // Set formatted version strings diff --git a/crates/node/types/Cargo.toml b/crates/node/types/Cargo.toml index 588fe7c4062f..fefe98a2cc17 100644 --- a/crates/node/types/Cargo.toml +++ b/crates/node/types/Cargo.toml @@ -17,10 +17,12 @@ reth-db-api.workspace = true reth-engine-primitives.workspace = true reth-primitives-traits.workspace = true reth-trie-db.workspace = true +reth-payload-primitives.workspace = true [features] default = ["std"] std = [ - "reth-primitives-traits/std", - "reth-chainspec/std", -] \ No newline at end of file + "reth-primitives-traits/std", + "reth-chainspec/std", + "reth-engine-primitives/std" +] diff --git a/crates/node/types/src/lib.rs b/crates/node/types/src/lib.rs index 8cdf9015f38b..4adf105b3b3f 100644 --- a/crates/node/types/src/lib.rs +++ b/crates/node/types/src/lib.rs @@ -15,11 +15,9 @@ pub use reth_primitives_traits::{ }; use reth_chainspec::EthChainSpec; -use reth_db_api::{ - database_metrics::{DatabaseMetadata, DatabaseMetrics}, - Database, -}; -use reth_engine_primitives::{BuiltPayload, EngineTypes}; +use reth_db_api::{database_metrics::DatabaseMetrics, Database}; +use reth_engine_primitives::EngineTypes; +use reth_payload_primitives::BuiltPayload; use reth_trie_db::StateCommitment; /// The type that configures the essential types of an Ethereum-like node. @@ -50,7 +48,7 @@ pub trait NodeTypesWithEngine: NodeTypes { /// Its types are configured by node internally and are not intended to be user configurable. pub trait NodeTypesWithDB: NodeTypes { /// Underlying database type used by the node to store and retrieve data. - type DB: Database + DatabaseMetrics + DatabaseMetadata + Clone + Unpin + 'static; + type DB: Database + DatabaseMetrics + Clone + Unpin + 'static; } /// An adapter type combining [`NodeTypes`] and db into [`NodeTypesWithDB`]. @@ -101,7 +99,7 @@ where impl NodeTypesWithDB for NodeTypesWithDBAdapter where Types: NodeTypes, - DB: Database + DatabaseMetrics + DatabaseMetadata + Clone + Unpin + 'static, + DB: Database + DatabaseMetrics + Clone + Unpin + 'static, { type DB = DB; } @@ -234,16 +232,19 @@ where } /// Helper adapter type for accessing [`NodePrimitives::Block`] on [`NodeTypes`]. -pub type BlockTy = <::Primitives as NodePrimitives>::Block; +pub type BlockTy = as NodePrimitives>::Block; /// Helper adapter type for accessing [`NodePrimitives::BlockHeader`] on [`NodeTypes`]. -pub type HeaderTy = <::Primitives as NodePrimitives>::BlockHeader; +pub type HeaderTy = as NodePrimitives>::BlockHeader; /// Helper adapter type for accessing [`NodePrimitives::BlockBody`] on [`NodeTypes`]. -pub type BodyTy = <::Primitives as NodePrimitives>::BlockBody; +pub type BodyTy = as NodePrimitives>::BlockBody; /// Helper adapter type for accessing [`NodePrimitives::SignedTx`] on [`NodeTypes`]. -pub type TxTy = <::Primitives as NodePrimitives>::SignedTx; +pub type TxTy = as NodePrimitives>::SignedTx; /// Helper adapter type for accessing [`NodePrimitives::Receipt`] on [`NodeTypes`]. -pub type ReceiptTy = <::Primitives as NodePrimitives>::Receipt; +pub type ReceiptTy = as NodePrimitives>::Receipt; + +/// Helper type for getting the `Primitives` associated type from a [`NodeTypes`]. +pub type PrimitivesTy = ::Primitives; diff --git a/crates/optimism/bin/src/main.rs b/crates/optimism/bin/src/main.rs index bba31e250117..06a5786819a0 100644 --- a/crates/optimism/bin/src/main.rs +++ b/crates/optimism/bin/src/main.rs @@ -3,12 +3,9 @@ #![cfg(feature = "optimism")] use clap::Parser; -use reth_node_builder::{engine_tree_config::TreeConfig, EngineNodeLauncher, Node}; use reth_optimism_cli::{chainspec::OpChainSpecParser, Cli}; use reth_optimism_node::{args::RollupArgs, OpNode}; -use reth_provider::providers::BlockchainProvider; - -use tracing as _; +use tracing::info; #[global_allocator] static ALLOC: reth_cli_util::allocator::Allocator = reth_cli_util::allocator::new_allocator(); @@ -23,29 +20,8 @@ fn main() { if let Err(err) = Cli::::parse().run(|builder, rollup_args| async move { - let engine_tree_config = TreeConfig::default() - .with_persistence_threshold(builder.config().engine.persistence_threshold) - .with_memory_block_buffer_target(builder.config().engine.memory_block_buffer_target) - .with_state_root_task(builder.config().engine.state_root_task_enabled) - .with_always_compare_trie_updates( - builder.config().engine.state_root_task_compare_updates, - ); - - let op_node = OpNode::new(rollup_args.clone()); - let handle = builder - .with_types_and_provider::>() - .with_components(op_node.components()) - .with_add_ons(op_node.add_ons()) - .launch_with_fn(|builder| { - let launcher = EngineNodeLauncher::new( - builder.task_executor().clone(), - builder.config().datadir(), - engine_tree_config, - ); - builder.launch_with(launcher) - }) - .await?; - + info!(target: "reth::cli", "Launching node"); + let handle = builder.launch_node(OpNode::new(rollup_args)).await?; handle.node_exit_future.await }) { diff --git a/crates/optimism/cli/src/lib.rs b/crates/optimism/cli/src/lib.rs index 5c3900a0e483..e08426293afc 100644 --- a/crates/optimism/cli/src/lib.rs +++ b/crates/optimism/cli/src/lib.rs @@ -69,7 +69,7 @@ use reth_node_metrics::recorder::install_prometheus_recorder; pub struct Cli { /// The command to run #[command(subcommand)] - command: Commands, + pub command: Commands, /// The chain this node is running. /// @@ -82,7 +82,7 @@ pub struct Cli, + pub chain: Arc, /// Add a new instance of a node. /// @@ -98,10 +98,11 @@ pub struct Cli, - EvmConfig: Clone - + Unpin - + Sync - + Send - + 'static - + ConfigureEvm
, + EvmConfig: ConfigureEvmFor + Clone + Unpin + Sync + Send + 'static, { type Primitives = N; type Strategy + Display>> = @@ -107,8 +104,6 @@ where chain_spec: Arc, /// How to create an EVM. evm_config: EvmConfig, - /// Optional overrides for the transactions environment. - tx_env_overrides: Option>, /// Current state for block execution. state: State, /// Utility to call system smart contracts. @@ -130,14 +125,7 @@ where receipt_builder: Arc>, ) -> Self { let system_caller = SystemCaller::new(evm_config.clone(), chain_spec.clone()); - Self { - state, - chain_spec, - evm_config, - system_caller, - tx_env_overrides: None, - receipt_builder, - } + Self { state, chain_spec, evm_config, system_caller, receipt_builder } } } @@ -146,19 +134,15 @@ where DB: Database + Display>, N: NodePrimitives< BlockHeader = alloy_consensus::Header, - SignedTx: DepositTransaction, + SignedTx: OpTransaction, Receipt: DepositReceipt, >, - EvmConfig: ConfigureEvm
, + EvmConfig: ConfigureEvmFor, { type DB = DB; type Primitives = N; type Error = BlockExecutionError; - fn init(&mut self, tx_env_overrides: Box) { - self.tx_env_overrides = Some(tx_env_overrides); - } - fn apply_pre_execution_changes( &mut self, block: &RecoveredBlock, @@ -226,14 +210,10 @@ where .transpose() .map_err(|_| OpBlockExecutionError::AccountLoadFailed(*sender))?; - self.evm_config.fill_tx_env(evm.tx_mut(), transaction, *sender); - - if let Some(tx_env_overrides) = &mut self.tx_env_overrides { - tx_env_overrides.apply(evm.tx_mut()); - } + let tx_env = self.evm_config.tx_env(transaction, *sender); // Execute transaction. - let result_and_state = evm.transact().map_err(move |err| { + let result_and_state = evm.transact(tx_env).map_err(move |err| { let new_err = err.map_db_err(|e| e.into()); // Ensure hash is calculated for error log, if not already done BlockValidationError::EVM { @@ -462,8 +442,8 @@ mod tests { .unwrap(); let receipts = executor.receipts(); - let tx_receipt = receipts[0][0].as_ref().unwrap(); - let deposit_receipt = receipts[0][1].as_ref().unwrap(); + let tx_receipt = &receipts[0][0]; + let deposit_receipt = &receipts[0][1]; assert!(!matches!(tx_receipt, OpReceipt::Deposit(_))); // deposit_nonce is present only in deposit transactions @@ -538,8 +518,8 @@ mod tests { .expect("Executing a block while canyon is active should not fail"); let receipts = executor.receipts(); - let tx_receipt = receipts[0][0].as_ref().unwrap(); - let deposit_receipt = receipts[0][1].as_ref().unwrap(); + let tx_receipt = &receipts[0][0]; + let deposit_receipt = &receipts[0][1]; // deposit_receipt_version is set to 1 for post canyon deposit transactions assert!(!matches!(tx_receipt, OpReceipt::Deposit(_))); diff --git a/crates/optimism/evm/src/lib.rs b/crates/optimism/evm/src/lib.rs index ad172c764411..a17a7e8709b7 100644 --- a/crates/optimism/evm/src/lib.rs +++ b/crates/optimism/evm/src/lib.rs @@ -12,19 +12,20 @@ extern crate alloc; -use alloc::{sync::Arc, vec::Vec}; -use alloy_consensus::Header; +use alloc::sync::Arc; +use alloy_consensus::{BlockHeader, Header}; use alloy_eips::eip7840::BlobParams; use alloy_primitives::{Address, U256}; +use core::fmt::Debug; use op_alloy_consensus::EIP1559ParamError; -use reth_evm::{env::EvmEnv, ConfigureEvm, ConfigureEvmEnv, NextBlockEnvAttributes}; +use reth_evm::{env::EvmEnv, ConfigureEvm, ConfigureEvmEnv, Evm, NextBlockEnvAttributes}; use reth_optimism_chainspec::OpChainSpec; use reth_optimism_primitives::OpTransactionSigned; use reth_primitives_traits::FillTxEnv; use reth_revm::{ inspector_handle_register, primitives::{AnalysisKind, CfgEnvWithHandlerCfg, TxEnv}, - Database, Evm, EvmBuilder, GetInspector, + Database, EvmBuilder, GetInspector, }; mod config; @@ -39,44 +40,37 @@ pub use receipts::*; mod error; pub use error::OpBlockExecutionError; use revm_primitives::{ - BlobExcessGasAndPrice, BlockEnv, Bytes, CfgEnv, Env, HandlerCfg, OptimismFields, SpecId, TxKind, + BlobExcessGasAndPrice, BlockEnv, Bytes, CfgEnv, EVMError, HandlerCfg, OptimismFields, + ResultAndState, SpecId, TxKind, }; -/// Optimism-related EVM configuration. -#[derive(Debug, Clone)] -pub struct OpEvmConfig { - chain_spec: Arc, -} +/// OP EVM implementation. +#[derive(derive_more::Debug, derive_more::Deref, derive_more::DerefMut, derive_more::From)] +#[debug(bound(DB::Error: Debug))] +pub struct OpEvm<'a, EXT, DB: Database>(reth_revm::Evm<'a, EXT, DB>); -impl OpEvmConfig { - /// Creates a new [`OpEvmConfig`] with the given chain spec. - pub const fn new(chain_spec: Arc) -> Self { - Self { chain_spec } - } +impl Evm for OpEvm<'_, EXT, DB> { + type DB = DB; + type Tx = TxEnv; + type Error = EVMError; - /// Returns the chain spec associated with this configuration. - pub const fn chain_spec(&self) -> &Arc { - &self.chain_spec + fn block(&self) -> &BlockEnv { + self.0.block() } -} - -impl ConfigureEvmEnv for OpEvmConfig { - type Header = Header; - type Transaction = OpTransactionSigned; - type Error = EIP1559ParamError; - fn fill_tx_env(&self, tx_env: &mut TxEnv, transaction: &OpTransactionSigned, sender: Address) { - transaction.fill_tx_env(tx_env, sender); + fn transact(&mut self, tx: Self::Tx) -> Result { + *self.tx_mut() = tx; + self.0.transact() } - fn fill_tx_env_system_contract_call( - &self, - env: &mut Env, + fn transact_system_call( + &mut self, caller: Address, contract: Address, data: Bytes, - ) { - env.tx = TxEnv { + ) -> Result { + #[allow(clippy::needless_update)] // side-effect of optimism fields + let tx_env = TxEnv { caller, transact_to: TxKind::Call(contract), // Explicitly set nonce to None so revm does not do any nonce checks @@ -107,24 +101,85 @@ impl ConfigureEvmEnv for OpEvmConfig { }, }; + *self.tx_mut() = tx_env; + + let prev_block_env = self.block().clone(); + // ensure the block gas limit is >= the tx - env.block.gas_limit = U256::from(env.tx.gas_limit); + self.block_mut().gas_limit = U256::from(self.tx().gas_limit); // disable the base fee check for this call by setting the base fee to zero - env.block.basefee = U256::ZERO; + self.block_mut().basefee = U256::ZERO; + + let res = self.0.transact(); + + // re-set the block env + *self.block_mut() = prev_block_env; + + res + } + + fn db_mut(&mut self) -> &mut Self::DB { + &mut self.context.evm.db + } +} + +/// Optimism-related EVM configuration. +#[derive(Debug, Clone)] +pub struct OpEvmConfig { + chain_spec: Arc, +} + +impl OpEvmConfig { + /// Creates a new [`OpEvmConfig`] with the given chain spec. + pub const fn new(chain_spec: Arc) -> Self { + Self { chain_spec } } - fn fill_cfg_env(&self, cfg_env: &mut CfgEnvWithHandlerCfg, header: &Self::Header) { - let spec_id = revm_spec(self.chain_spec(), header); + /// Returns the chain spec associated with this configuration. + pub const fn chain_spec(&self) -> &Arc { + &self.chain_spec + } +} + +impl ConfigureEvmEnv for OpEvmConfig { + type Header = Header; + type Transaction = OpTransactionSigned; + type Error = EIP1559ParamError; + type TxEnv = TxEnv; + type Spec = SpecId; + fn tx_env(&self, transaction: &Self::Transaction, signer: Address) -> Self::TxEnv { + let mut tx_env = TxEnv::default(); + transaction.fill_tx_env(&mut tx_env, signer); + tx_env + } + + fn evm_env(&self, header: &Self::Header) -> EvmEnv { + let spec = config::revm_spec(self.chain_spec(), header); + + let mut cfg_env = CfgEnv::default(); cfg_env.chain_id = self.chain_spec.chain().id(); - cfg_env.perf_analyse_created_bytecodes = AnalysisKind::Analyse; + cfg_env.perf_analyse_created_bytecodes = AnalysisKind::default(); + + let block_env = BlockEnv { + number: U256::from(header.number()), + coinbase: header.beneficiary(), + timestamp: U256::from(header.timestamp()), + difficulty: if spec >= SpecId::MERGE { U256::ZERO } else { header.difficulty() }, + prevrandao: if spec >= SpecId::MERGE { header.mix_hash() } else { None }, + gas_limit: U256::from(header.gas_limit()), + basefee: U256::from(header.base_fee_per_gas().unwrap_or_default()), + // EIP-4844 excess blob gas of this block, introduced in Cancun + blob_excess_gas_and_price: header.excess_blob_gas().map(|excess_blob_gas| { + BlobExcessGasAndPrice::new(excess_blob_gas, spec >= SpecId::PRAGUE) + }), + }; - cfg_env.handler_cfg.spec_id = spec_id; - cfg_env.handler_cfg.is_optimism = true; + EvmEnv { cfg_env, block_env, spec } } - fn next_cfg_and_block_env( + fn next_evm_env( &self, parent: &Self::Header, attributes: NextBlockEnvAttributes, @@ -168,43 +223,45 @@ impl ConfigureEvmEnv for OpEvmConfig { } impl ConfigureEvm for OpEvmConfig { - fn evm_with_env( - &self, - db: DB, - mut evm_env: EvmEnv, - tx: TxEnv, - ) -> Evm<'_, (), DB> { - evm_env.cfg_env_with_handler_cfg.handler_cfg.is_optimism = true; + type Evm<'a, DB: Database + 'a, I: 'a> = OpEvm<'a, I, DB>; + + fn evm_with_env(&self, db: DB, evm_env: EvmEnv) -> Self::Evm<'_, DB, ()> { + let cfg_env_with_handler_cfg = CfgEnvWithHandlerCfg { + cfg_env: evm_env.cfg_env, + handler_cfg: HandlerCfg { spec_id: evm_env.spec, is_optimism: true }, + }; EvmBuilder::default() .with_db(db) - .with_cfg_env_with_handler_cfg(evm_env.cfg_env_with_handler_cfg) + .with_cfg_env_with_handler_cfg(cfg_env_with_handler_cfg) .with_block_env(evm_env.block_env) - .with_tx_env(tx) .build() + .into() } fn evm_with_env_and_inspector( &self, db: DB, - mut evm_env: EvmEnv, - tx: TxEnv, + evm_env: EvmEnv, inspector: I, - ) -> Evm<'_, I, DB> + ) -> Self::Evm<'_, DB, I> where DB: Database, I: GetInspector, { - evm_env.cfg_env_with_handler_cfg.handler_cfg.is_optimism = true; + let cfg_env_with_handler_cfg = CfgEnvWithHandlerCfg { + cfg_env: evm_env.cfg_env, + handler_cfg: HandlerCfg { spec_id: evm_env.spec, is_optimism: true }, + }; EvmBuilder::default() .with_db(db) .with_external_context(inspector) - .with_cfg_env_with_handler_cfg(evm_env.cfg_env_with_handler_cfg) + .with_cfg_env_with_handler_cfg(cfg_env_with_handler_cfg) .with_block_env(evm_env.block_env) - .with_tx_env(tx) .append_handler_register(inspector_handle_register) .build() + .into() } } @@ -252,13 +309,12 @@ mod tests { // Use the `OpEvmConfig` to create the `cfg_env` and `block_env` based on the ChainSpec, // Header, and total difficulty - let EvmEnv { cfg_env_with_handler_cfg, .. } = - OpEvmConfig::new(Arc::new(OpChainSpec { inner: chain_spec.clone() })) - .cfg_and_block_env(&header); + let EvmEnv { cfg_env, .. } = + OpEvmConfig::new(Arc::new(OpChainSpec { inner: chain_spec.clone() })).evm_env(&header); // Assert that the chain ID in the `cfg_env` is correctly set to the chain ID of the // ChainSpec - assert_eq!(cfg_env_with_handler_cfg.chain_id, chain_spec.chain().id()); + assert_eq!(cfg_env.chain_id, chain_spec.chain().id()); } #[test] @@ -269,10 +325,10 @@ mod tests { let evm_env = EvmEnv::default(); - let evm = evm_config.evm_with_env(db, evm_env.clone(), Default::default()); + let evm = evm_config.evm_with_env(db, evm_env.clone()); // Check that the EVM environment - assert_eq!(evm.context.evm.env.cfg, evm_env.cfg_env_with_handler_cfg.cfg_env); + assert_eq!(evm.context.evm.env.cfg, evm_env.cfg_env); // Default spec ID assert_eq!(evm.handler.spec_id(), SpecId::LATEST); @@ -290,15 +346,9 @@ mod tests { // Create a custom configuration environment with a chain ID of 111 let cfg = CfgEnv::default().with_chain_id(111); - let evm_env = EvmEnv { - cfg_env_with_handler_cfg: CfgEnvWithHandlerCfg { - cfg_env: cfg.clone(), - handler_cfg: Default::default(), - }, - ..Default::default() - }; + let evm_env = EvmEnv { cfg_env: cfg.clone(), ..Default::default() }; - let evm = evm_config.evm_with_env(db, evm_env, Default::default()); + let evm = evm_config.evm_with_env(db, evm_env); // Check that the EVM environment is initialized with the custom environment assert_eq!(evm.context.evm.inner.env.cfg, cfg); @@ -323,15 +373,13 @@ mod tests { number: U256::from(42), ..Default::default() }; - let tx = TxEnv { gas_limit: 5_000_000, gas_price: U256::from(50), ..Default::default() }; let evm_env = EvmEnv { block_env: block, ..Default::default() }; - let evm = evm_config.evm_with_env(db, evm_env.clone(), tx.clone()); + let evm = evm_config.evm_with_env(db, evm_env.clone()); // Verify that the block and transaction environments are set correctly assert_eq!(evm.context.evm.env.block, evm_env.block_env); - assert_eq!(evm.context.evm.env.tx, tx); // Default spec ID assert_eq!(evm.handler.spec_id(), SpecId::LATEST); @@ -346,17 +394,9 @@ mod tests { let db = CacheDB::>::default(); - let handler_cfg = HandlerCfg { spec_id: SpecId::ECOTONE, ..Default::default() }; - - let evm_env = EvmEnv { - cfg_env_with_handler_cfg: CfgEnvWithHandlerCfg { - handler_cfg, - cfg_env: Default::default(), - }, - ..Default::default() - }; + let evm_env = EvmEnv { spec: SpecId::ECOTONE, ..Default::default() }; - let evm = evm_config.evm_with_env(db, evm_env, Default::default()); + let evm = evm_config.evm_with_env(db, evm_env); // Check that the spec ID is setup properly assert_eq!(evm.handler.spec_id(), SpecId::ECOTONE); @@ -370,24 +410,13 @@ mod tests { let evm_config = test_evm_config(); let db = CacheDB::>::default(); - let evm_env = EvmEnv { - cfg_env_with_handler_cfg: CfgEnvWithHandlerCfg { - cfg_env: Default::default(), - handler_cfg: HandlerCfg { is_optimism: true, ..Default::default() }, - }, - ..Default::default() - }; + let evm_env = EvmEnv { cfg_env: Default::default(), ..Default::default() }; - let evm = evm_config.evm_with_env_and_inspector( - db, - evm_env.clone(), - Default::default(), - NoOpInspector, - ); + let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector); // Check that the EVM environment is set to default values assert_eq!(evm.context.evm.env.block, evm_env.block_env); - assert_eq!(evm.context.evm.env.cfg, evm_env.cfg_env_with_handler_cfg.cfg_env); + assert_eq!(evm.context.evm.env.cfg, evm_env.cfg_env); assert_eq!(evm.context.evm.env.tx, Default::default()); assert_eq!(evm.context.external, NoOpInspector); assert_eq!(evm.handler.spec_id(), SpecId::LATEST); @@ -403,22 +432,13 @@ mod tests { let cfg = CfgEnv::default().with_chain_id(111); let block = BlockEnv::default(); - let tx = TxEnv::default(); - let evm_env = EvmEnv { - block_env: block, - cfg_env_with_handler_cfg: CfgEnvWithHandlerCfg { - cfg_env: cfg.clone(), - handler_cfg: Default::default(), - }, - }; + let evm_env = EvmEnv { block_env: block, cfg_env: cfg.clone(), ..Default::default() }; - let evm = - evm_config.evm_with_env_and_inspector(db, evm_env.clone(), tx.clone(), NoOpInspector); + let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector); // Check that the EVM environment is set with custom configuration assert_eq!(evm.context.evm.env.cfg, cfg); assert_eq!(evm.context.evm.env.block, evm_env.block_env); - assert_eq!(evm.context.evm.env.tx, tx); assert_eq!(evm.context.external, NoOpInspector); assert_eq!(evm.handler.spec_id(), SpecId::LATEST); @@ -438,15 +458,12 @@ mod tests { number: U256::from(42), ..Default::default() }; - let tx = TxEnv { gas_limit: 5_000_000, gas_price: U256::from(50), ..Default::default() }; let evm_env = EvmEnv { block_env: block, ..Default::default() }; - let evm = - evm_config.evm_with_env_and_inspector(db, evm_env.clone(), tx.clone(), NoOpInspector); + let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector); // Verify that the block and transaction environments are set correctly assert_eq!(evm.context.evm.env.block, evm_env.block_env); - assert_eq!(evm.context.evm.env.tx, tx); assert_eq!(evm.context.external, NoOpInspector); assert_eq!(evm.handler.spec_id(), SpecId::LATEST); @@ -459,25 +476,13 @@ mod tests { let evm_config = test_evm_config(); let db = CacheDB::>::default(); - let handler_cfg = HandlerCfg { spec_id: SpecId::ECOTONE, ..Default::default() }; - let evm_env = EvmEnv { - cfg_env_with_handler_cfg: CfgEnvWithHandlerCfg { - cfg_env: Default::default(), - handler_cfg, - }, - ..Default::default() - }; + let evm_env = EvmEnv { spec: SpecId::ECOTONE, ..Default::default() }; - let evm = evm_config.evm_with_env_and_inspector( - db, - evm_env.clone(), - Default::default(), - NoOpInspector, - ); + let evm = evm_config.evm_with_env_and_inspector(db, evm_env.clone(), NoOpInspector); // Check that the spec ID is set properly assert_eq!(evm.handler.spec_id(), SpecId::ECOTONE); - assert_eq!(evm.context.evm.env.cfg, evm_env.cfg_env_with_handler_cfg.cfg_env); + assert_eq!(evm.context.evm.env.cfg, evm_env.cfg_env); assert_eq!(evm.context.evm.env.block, evm_env.block_env); assert_eq!(evm.context.external, NoOpInspector); @@ -523,8 +528,7 @@ mod tests { }); // Create a Receipts object with a vector of receipt vectors - let receipts = - Receipts { receipt_vec: vec![vec![Some(receipt1.clone())], vec![Some(receipt2)]] }; + let receipts = Receipts { receipt_vec: vec![vec![receipt1.clone()], vec![receipt2]] }; // Create an ExecutionOutcome object with the created bundle, receipts, an empty requests // vector, and first_block set to 10 @@ -546,7 +550,7 @@ mod tests { // Create an ExecutionOutcome object with a single receipt vector containing receipt1 let execution_outcome1 = ExecutionOutcome { bundle: Default::default(), - receipts: Receipts { receipt_vec: vec![vec![Some(receipt1)]] }, + receipts: Receipts { receipt_vec: vec![vec![receipt1]] }, requests: vec![], first_block: 10, }; @@ -662,11 +666,11 @@ mod tests { fn test_get_logs() { // Create a Receipts object with a vector of receipt vectors let receipts = Receipts { - receipt_vec: vec![vec![Some(OpReceipt::Legacy(Receipt { + receipt_vec: vec![vec![OpReceipt::Legacy(Receipt { cumulative_gas_used: 46913, logs: vec![Log::::default()], status: true.into(), - }))]], + })]], }; // Define the first block number diff --git a/crates/optimism/node/Cargo.toml b/crates/optimism/node/Cargo.toml index 8a597b9ae8a7..76c3e1ae57ae 100644 --- a/crates/optimism/node/Cargo.toml +++ b/crates/optimism/node/Cargo.toml @@ -52,6 +52,7 @@ alloy-eips.workspace = true alloy-primitives.workspace = true op-alloy-consensus.workspace = true op-alloy-rpc-types-engine.workspace = true +op-alloy-flz.workspace = true alloy-rpc-types-engine.workspace = true alloy-consensus.workspace = true diff --git a/crates/optimism/node/src/engine.rs b/crates/optimism/node/src/engine.rs index 29a13f9d8b3e..184952e023aa 100644 --- a/crates/optimism/node/src/engine.rs +++ b/crates/optimism/node/src/engine.rs @@ -105,6 +105,19 @@ impl EngineValidator for OpEngineValidator where Types: EngineTypes, { + fn validate_execution_requests( + &self, + requests: &alloy_eips::eip7685::Requests, + ) -> Result<(), EngineObjectValidationError> { + // according to op spec, execution requests must be empty + if !requests.is_empty() { + return Err(EngineObjectValidationError::InvalidParams( + "NonEmptyExecutionRequests".to_string().into(), + )) + } + Ok(()) + } + fn validate_version_specific_fields( &self, version: EngineApiMessageVersion, @@ -204,14 +217,14 @@ pub fn validate_withdrawals_presence( #[cfg(test)] mod test { + use super::*; use crate::engine; use alloy_primitives::{b64, Address, B256, B64}; use alloy_rpc_types_engine::PayloadAttributes; + use reth_node_builder::EngineValidator; use reth_optimism_chainspec::BASE_SEPOLIA; - use super::*; - fn get_chainspec() -> Arc { let hardforks = OpHardfork::base_sepolia(); Arc::new(OpChainSpec { @@ -251,7 +264,7 @@ mod test { let validator = OpEngineValidator::new(get_chainspec()); let attributes = get_attributes(None, 1732633199); - let result = >::ensure_well_formed_attributes( &validator, EngineApiMessageVersion::V3, &attributes @@ -264,7 +277,7 @@ mod test { let validator = OpEngineValidator::new(get_chainspec()); let attributes = get_attributes(None, 1732633200); - let result = >::ensure_well_formed_attributes( &validator, EngineApiMessageVersion::V3, &attributes @@ -277,7 +290,7 @@ mod test { let validator = OpEngineValidator::new(get_chainspec()); let attributes = get_attributes(Some(b64!("0000000000000008")), 1732633200); - let result = >::ensure_well_formed_attributes( &validator, EngineApiMessageVersion::V3, &attributes @@ -290,7 +303,7 @@ mod test { let validator = OpEngineValidator::new(get_chainspec()); let attributes = get_attributes(Some(b64!("0000000800000008")), 1732633200); - let result = >::ensure_well_formed_attributes( &validator, EngineApiMessageVersion::V3, &attributes @@ -303,7 +316,7 @@ mod test { let validator = OpEngineValidator::new(get_chainspec()); let attributes = get_attributes(Some(b64!("0000000000000000")), 1732633200); - let result = >::ensure_well_formed_attributes( &validator, EngineApiMessageVersion::V3, &attributes diff --git a/crates/optimism/node/src/node.rs b/crates/optimism/node/src/node.rs index 8e61f0a4b0ba..4bcf99cbb2ee 100644 --- a/crates/optimism/node/src/node.rs +++ b/crates/optimism/node/src/node.rs @@ -9,9 +9,9 @@ use crate::{ use op_alloy_consensus::OpPooledTransaction; use reth_basic_payload_builder::{BasicPayloadJobGenerator, BasicPayloadJobGeneratorConfig}; use reth_chainspec::{EthChainSpec, Hardforks}; -use reth_evm::{execute::BasicBlockExecutorProvider, ConfigureEvm}; +use reth_evm::{execute::BasicBlockExecutorProvider, ConfigureEvmEnv, ConfigureEvmFor}; use reth_network::{NetworkConfig, NetworkHandle, NetworkManager, NetworkPrimitives, PeersInfo}; -use reth_node_api::{AddOnsContext, FullNodeComponents, HeaderTy, NodeAddOns, TxTy}; +use reth_node_api::{AddOnsContext, FullNodeComponents, NodeAddOns, PrimitivesTy, TxTy}; use reth_node_builder::{ components::{ ComponentsBuilder, ConsensusBuilder, ExecutorBuilder, NetworkBuilder, @@ -43,6 +43,7 @@ use reth_transaction_pool::{ TransactionValidationTaskExecutor, }; use reth_trie_db::MerklePatriciaTrie; +use revm::primitives::TxEnv; use std::sync::Arc; /// Storage implementation for Optimism. @@ -190,6 +191,7 @@ where Storage = OpStorage, Engine = OpEngineTypes, >, + Evm: ConfigureEvmEnv, >, { type Handle = RpcHandle>; @@ -239,6 +241,7 @@ where Storage = OpStorage, Engine = OpEngineTypes, >, + Evm: ConfigureEvmEnv, >, { type EthApi = OpEthApi; @@ -484,7 +487,7 @@ where Pool: TransactionPool>> + Unpin + 'static, - Evm: ConfigureEvm
, Transaction = TxTy>, + Evm: ConfigureEvmFor>, { let payload_builder = reth_optimism_payload_builder::OpPayloadBuilder::with_builder_config( evm_config, diff --git a/crates/optimism/node/src/txpool.rs b/crates/optimism/node/src/txpool.rs index 0cce61fb7a01..62ee3a820fe4 100644 --- a/crates/optimism/node/src/txpool.rs +++ b/crates/optimism/node/src/txpool.rs @@ -11,7 +11,7 @@ use reth_node_api::{Block, BlockBody}; use reth_optimism_evm::RethL1BlockInfo; use reth_optimism_primitives::{OpBlock, OpTransactionSigned}; use reth_primitives::{ - transaction::TransactionConversionError, GotExpected, InvalidTransactionError, RecoveredTx, + transaction::TransactionConversionError, GotExpected, InvalidTransactionError, Recovered, SealedBlock, }; use reth_primitives_traits::SignedTransaction; @@ -25,7 +25,7 @@ use reth_transaction_pool::{ use revm::primitives::{AccessList, KzgSettings}; use std::sync::{ atomic::{AtomicU64, Ordering}, - Arc, + Arc, OnceLock, }; /// Type alias for default optimism transaction pool @@ -36,131 +36,155 @@ pub type OpTransactionPool = Pool< >; /// Pool transaction for OP. +/// +/// This type wraps the actual transaction and caches values that are frequently used by the pool. +/// For payload building this lazily tracks values that are required during payload building: +/// - Estimated compressed size of this transaction #[derive(Debug, Clone, derive_more::Deref)] -pub struct OpPooledTransaction(EthPooledTransaction); +pub struct OpPooledTransaction { + #[deref] + inner: EthPooledTransaction, + /// The estimated size of this transaction, lazily computed. + estimated_tx_compressed_size: OnceLock, +} impl OpPooledTransaction { /// Create new instance of [Self]. - pub fn new(transaction: RecoveredTx, encoded_length: usize) -> Self { - Self(EthPooledTransaction::new(transaction, encoded_length)) + pub fn new(transaction: Recovered, encoded_length: usize) -> Self { + Self { + inner: EthPooledTransaction::new(transaction, encoded_length), + estimated_tx_compressed_size: Default::default(), + } + } + + /// Returns the estimated compressed size of a transaction in bytes scaled by 1e6. + /// This value is computed based on the following formula: + /// `max(minTransactionSize, intercept + fastlzCoef*fastlzSize)` + pub fn estimated_compressed_size(&self) -> u64 { + *self.estimated_tx_compressed_size.get_or_init(|| { + op_alloy_flz::tx_estimated_size_fjord(&self.inner.transaction().encoded_2718()) + }) } } -impl From> for OpPooledTransaction { - fn from(tx: RecoveredTx) -> Self { +impl From> for OpPooledTransaction { + fn from(tx: Recovered) -> Self { let encoded_len = tx.encode_2718_len(); let tx = tx.map_transaction(|tx| tx.into()); - Self(EthPooledTransaction::new(tx, encoded_len)) + Self { + inner: EthPooledTransaction::new(tx, encoded_len), + estimated_tx_compressed_size: Default::default(), + } } } -impl TryFrom> for OpPooledTransaction { +impl TryFrom> for OpPooledTransaction { type Error = TransactionConversionError; - fn try_from(value: RecoveredTx) -> Result { + fn try_from(value: Recovered) -> Result { let (tx, signer) = value.into_parts(); - let pooled: RecoveredTx = - RecoveredTx::new_unchecked(tx.try_into()?, signer); + let pooled: Recovered = + Recovered::new_unchecked(tx.try_into()?, signer); Ok(pooled.into()) } } -impl From for RecoveredTx { +impl From for Recovered { fn from(value: OpPooledTransaction) -> Self { - value.0.transaction + value.inner.transaction } } impl PoolTransaction for OpPooledTransaction { - type TryFromConsensusError = >>::Error; + type TryFromConsensusError = >>::Error; type Consensus = OpTransactionSigned; type Pooled = op_alloy_consensus::OpPooledTransaction; - fn clone_into_consensus(&self) -> RecoveredTx { - self.transaction().clone() + fn clone_into_consensus(&self) -> Recovered { + self.inner.transaction().clone() } fn try_consensus_into_pooled( - tx: RecoveredTx, - ) -> Result, Self::TryFromConsensusError> { + tx: Recovered, + ) -> Result, Self::TryFromConsensusError> { let (tx, signer) = tx.into_parts(); - Ok(RecoveredTx::new_unchecked(tx.try_into()?, signer)) + Ok(Recovered::new_unchecked(tx.try_into()?, signer)) } fn hash(&self) -> &TxHash { - self.transaction.tx_hash() + self.inner.transaction.tx_hash() } fn sender(&self) -> Address { - self.transaction.signer() + self.inner.transaction.signer() } fn sender_ref(&self) -> &Address { - self.transaction.signer_ref() + self.inner.transaction.signer_ref() } fn nonce(&self) -> u64 { - self.transaction.nonce() + self.inner.transaction.nonce() } fn cost(&self) -> &U256 { - &self.cost + &self.inner.cost } fn gas_limit(&self) -> u64 { - self.transaction.gas_limit() + self.inner.transaction.gas_limit() } fn max_fee_per_gas(&self) -> u128 { - self.transaction.transaction.max_fee_per_gas() + self.inner.transaction.transaction.max_fee_per_gas() } fn access_list(&self) -> Option<&AccessList> { - self.transaction.access_list() + self.inner.transaction.access_list() } fn max_priority_fee_per_gas(&self) -> Option { - self.transaction.transaction.max_priority_fee_per_gas() + self.inner.transaction.transaction.max_priority_fee_per_gas() } fn max_fee_per_blob_gas(&self) -> Option { - self.transaction.max_fee_per_blob_gas() + self.inner.transaction.max_fee_per_blob_gas() } fn effective_tip_per_gas(&self, base_fee: u64) -> Option { - self.transaction.effective_tip_per_gas(base_fee) + self.inner.transaction.effective_tip_per_gas(base_fee) } fn priority_fee_or_price(&self) -> u128 { - self.transaction.priority_fee_or_price() + self.inner.transaction.priority_fee_or_price() } fn kind(&self) -> TxKind { - self.transaction.kind() + self.inner.transaction.kind() } fn is_create(&self) -> bool { - self.transaction.is_create() + self.inner.transaction.is_create() } fn input(&self) -> &[u8] { - self.transaction.input() + self.inner.transaction.input() } fn size(&self) -> usize { - self.transaction.transaction.input().len() + self.inner.transaction.transaction.input().len() } fn tx_type(&self) -> u8 { - self.transaction.ty() + self.inner.transaction.ty() } fn encoded_length(&self) -> usize { - self.encoded_length + self.inner.encoded_length } fn chain_id(&self) -> Option { - self.transaction.chain_id() + self.inner.transaction.chain_id() } } @@ -176,12 +200,12 @@ impl EthPoolTransaction for OpPooledTransaction { fn try_into_pooled_eip4844( self, _sidecar: Arc, - ) -> Option> { + ) -> Option> { None } fn try_from_eip4844( - _tx: RecoveredTx, + _tx: Recovered, _sidecar: BlobTransactionSidecar, ) -> Option { None @@ -196,7 +220,7 @@ impl EthPoolTransaction for OpPooledTransaction { } fn authorization_count(&self) -> usize { - match &self.transaction.transaction { + match &self.inner.transaction.transaction { OpTypedTransaction::Eip7702(tx) => tx.authorization_list.len(), _ => 0, } @@ -429,7 +453,7 @@ mod tests { use op_alloy_consensus::{OpTypedTransaction, TxDeposit}; use reth_chainspec::MAINNET; use reth_optimism_primitives::OpTransactionSigned; - use reth_primitives::RecoveredTx; + use reth_primitives::Recovered; use reth_provider::test_utils::MockEthProvider; use reth_transaction_pool::{ blobstore::InMemoryBlobStore, validate::EthTransactionValidatorBuilder, TransactionOrigin, @@ -458,7 +482,7 @@ mod tests { }); let signature = Signature::test_signature(); let signed_tx = OpTransactionSigned::new_unhashed(deposit_tx, signature); - let signed_recovered = RecoveredTx::new_unchecked(signed_tx, signer); + let signed_recovered = Recovered::new_unchecked(signed_tx, signer); let len = signed_recovered.encode_2718_len(); let pooled_tx = OpPooledTransaction::new(signed_recovered, len); let outcome = validator.validate_one(origin, pooled_tx); diff --git a/crates/optimism/node/tests/it/priority.rs b/crates/optimism/node/tests/it/priority.rs index defce4466267..b8d48de2df57 100644 --- a/crates/optimism/node/tests/it/priority.rs +++ b/crates/optimism/node/tests/it/priority.rs @@ -28,7 +28,7 @@ use reth_optimism_node::{ use reth_optimism_payload_builder::builder::OpPayloadTransactions; use reth_optimism_primitives::{OpPrimitives, OpTransactionSigned}; use reth_payload_util::{PayloadTransactions, PayloadTransactionsChain, PayloadTransactionsFixed}; -use reth_primitives::RecoveredTx; +use reth_primitives::Recovered; use reth_provider::providers::BlockchainProvider; use reth_tasks::TaskManager; use reth_transaction_pool::{pool::BestPayloadTransactions, PoolTransaction}; @@ -67,7 +67,7 @@ impl OpPayloadTransactions for CustomTxPriority { ..Default::default() }; let signature = sender.sign_transaction_sync(&mut end_of_block_tx).unwrap(); - let end_of_block_tx = RecoveredTx::new_unchecked( + let end_of_block_tx = Recovered::new_unchecked( OpTransactionSigned::new_unhashed( OpTypedTransaction::Eip1559(end_of_block_tx), signature, diff --git a/crates/optimism/payload/src/builder.rs b/crates/optimism/payload/src/builder.rs index 3f8c72ecea70..47bec70b1be8 100644 --- a/crates/optimism/payload/src/builder.rs +++ b/crates/optimism/payload/src/builder.rs @@ -1,21 +1,25 @@ //! Optimism payload builder implementation. use crate::{ - config::OpBuilderConfig, + config::{OpBuilderConfig, OpDAConfig}, error::OpPayloadBuilderError, payload::{OpBuiltPayload, OpPayloadBuilderAttributes}, }; use alloy_consensus::{Eip658Value, Header, Transaction, Typed2718, EMPTY_OMMER_ROOT_HASH}; use alloy_eips::{eip4895::Withdrawals, merge::BEACON_NONCE}; use alloy_primitives::{Address, Bytes, B256, U256}; +use alloy_rlp::Encodable; use alloy_rpc_types_debug::ExecutionWitness; use alloy_rpc_types_engine::PayloadId; use op_alloy_consensus::{OpDepositReceipt, OpTxType}; use op_alloy_rpc_types_engine::OpPayloadAttributes; use reth_basic_payload_builder::*; -use reth_chain_state::ExecutedBlock; +use reth_chain_state::{ExecutedBlock, ExecutedBlockWithTrieUpdates}; use reth_chainspec::{ChainSpecProvider, EthereumHardforks}; -use reth_evm::{env::EvmEnv, system_calls::SystemCaller, ConfigureEvm, NextBlockEnvAttributes}; +use reth_evm::{ + env::EvmEnv, system_calls::SystemCaller, ConfigureEvm, ConfigureEvmEnv, Evm, + NextBlockEnvAttributes, +}; use reth_execution_types::ExecutionOutcome; use reth_optimism_chainspec::OpChainSpec; use reth_optimism_consensus::calculate_receipt_root_no_memo_optimism; @@ -25,9 +29,9 @@ use reth_payload_builder_primitives::PayloadBuilderError; use reth_payload_primitives::PayloadBuilderAttributes; use reth_payload_util::{NoopPayloadTransactions, PayloadTransactions}; use reth_primitives::{ - proofs, transaction::SignedTransactionIntoRecoveredExt, Block, BlockBody, SealedHeader, + transaction::SignedTransactionIntoRecoveredExt, Block, BlockBody, SealedHeader, }; -use reth_primitives_traits::{block::Block as _, RecoveredBlock}; +use reth_primitives_traits::{block::Block as _, proofs, RecoveredBlock}; use reth_provider::{ HashedPostStateProvider, ProviderError, StateProofProvider, StateProviderFactory, StateRootProvider, @@ -122,7 +126,7 @@ where Txs: PayloadTransactions, { let evm_env = self - .cfg_and_block_env(&args.config.attributes, &args.config.parent_header) + .evm_env(&args.config.attributes, &args.config.parent_header) .map_err(PayloadBuilderError::other)?; let BuildArguments { client, pool: _, mut cached_reads, config, cancel, best_payload } = @@ -130,6 +134,7 @@ where let ctx = OpPayloadBuilderCtx { evm_config: self.evm_config.clone(), + da_config: self.config.da_config.clone(), chain_spec: client.chain_spec(), config, evm_env, @@ -158,18 +163,18 @@ where /// Returns the configured [`EvmEnv`] for the targeted payload /// (that has the `parent` as its parent). - pub fn cfg_and_block_env( + pub fn evm_env( &self, attributes: &OpPayloadBuilderAttributes, parent: &Header, - ) -> Result { + ) -> Result, EvmConfig::Error> { let next_attributes = NextBlockEnvAttributes { timestamp: attributes.timestamp(), suggested_fee_recipient: attributes.suggested_fee_recipient(), prev_randao: attributes.prev_randao(), gas_limit: attributes.gas_limit.unwrap_or(parent.gas_limit), }; - self.evm_config.next_cfg_and_block_env(parent, next_attributes) + self.evm_config.next_evm_env(parent, next_attributes) } /// Computes the witness for the payload. @@ -185,12 +190,12 @@ where let attributes = OpPayloadBuilderAttributes::try_new(parent.hash(), attributes, 3) .map_err(PayloadBuilderError::other)?; - let evm_env = - self.cfg_and_block_env(&attributes, &parent).map_err(PayloadBuilderError::other)?; + let evm_env = self.evm_env(&attributes, &parent).map_err(PayloadBuilderError::other)?; let config = PayloadConfig { parent_header: Arc::new(parent), attributes }; let ctx = OpPayloadBuilderCtx { evm_config: self.evm_config.clone(), + da_config: self.config.da_config.clone(), chain_spec: client.chain_spec(), config, evm_env, @@ -430,13 +435,15 @@ where debug!(target: "payload_builder", id=%ctx.attributes().payload_id(), sealed_block_header = ?sealed_block.header(), "sealed built block"); // create the executed block data - let executed: ExecutedBlock = ExecutedBlock { - recovered_block: Arc::new(RecoveredBlock::new_sealed( - sealed_block.as_ref().clone(), - info.executed_senders, - )), - execution_output: Arc::new(execution_outcome), - hashed_state: Arc::new(hashed_state), + let executed: ExecutedBlockWithTrieUpdates = ExecutedBlockWithTrieUpdates { + block: ExecutedBlock { + recovered_block: Arc::new(RecoveredBlock::new_sealed( + sealed_block.as_ref().clone(), + info.executed_senders, + )), + execution_output: Arc::new(execution_outcome), + hashed_state: Arc::new(hashed_state), + }, trie: Arc::new(trie_output), }; @@ -525,6 +532,8 @@ pub struct ExecutionInfo { pub receipts: Vec, /// All gas used so far pub cumulative_gas_used: u64, + /// Estimated DA size + pub cumulative_da_bytes_used: u64, /// Tracks fees from executed mempool transactions pub total_fees: U256, } @@ -537,29 +546,58 @@ impl ExecutionInfo { executed_senders: Vec::with_capacity(capacity), receipts: Vec::with_capacity(capacity), cumulative_gas_used: 0, + cumulative_da_bytes_used: 0, total_fees: U256::ZERO, } } + + /// Returns true if the transaction would exceed the block limits: + /// - block gas limit: ensures the transaction still fits into the block. + /// - tx DA limit: if configured, ensures the tx does not exceed the maximum allowed DA limit + /// per tx. + /// - block DA limit: if configured, ensures the transaction's DA size does not exceed the + /// maximum allowed DA limit per block. + pub fn is_tx_over_limits( + &self, + tx: &OpTransactionSigned, + block_gas_limit: u64, + tx_data_limit: Option, + block_data_limit: Option, + ) -> bool { + if tx_data_limit.is_some_and(|da_limit| tx.length() as u64 > da_limit) { + return true; + } + + if block_data_limit + .is_some_and(|da_limit| self.cumulative_da_bytes_used + (tx.length() as u64) > da_limit) + { + return true; + } + + self.cumulative_gas_used + tx.gas_limit() > block_gas_limit + } } /// Container type that holds all necessities to build a new payload. #[derive(Debug)] -pub struct OpPayloadBuilderCtx { +pub struct OpPayloadBuilderCtx { /// The type that knows how to perform system calls and configure the evm. pub evm_config: EvmConfig, + /// The DA config for the payload builder + pub da_config: OpDAConfig, /// The chainspec pub chain_spec: Arc, /// How to build the payload. pub config: PayloadConfig, /// Evm Settings - pub evm_env: EvmEnv, + pub evm_env: EvmEnv, /// Marker to check whether the job has been cancelled. pub cancel: Cancelled, /// The currently best payload. pub best_payload: Option, } -impl OpPayloadBuilderCtx { +impl OpPayloadBuilderCtx { /// Returns the parent block the payload will be build on. pub fn parent(&self) -> &SealedHeader { &self.config.parent_header @@ -739,8 +777,7 @@ where DB: Database, { let mut info = ExecutionInfo::with_capacity(self.attributes().transactions.len()); - let mut evm = - self.evm_config.evm_with_env(&mut *db, self.evm_env.clone(), Default::default()); + let mut evm = self.evm_config.evm_with_env(&mut *db, self.evm_env.clone()); for sequencer_tx in &self.attributes().transactions { // A sequencer's block should never contain blob transactions. @@ -754,10 +791,9 @@ where // purely for the purposes of utilizing the `evm_config.tx_env`` function. // Deposit transactions do not have signatures, so if the tx is a deposit, this // will just pull in its `from` address. - let sequencer_tx = - sequencer_tx.value().clone().try_into_ecrecovered().map_err(|_| { - PayloadBuilderError::other(OpPayloadBuilderError::TransactionEcRecoverFailed) - })?; + let sequencer_tx = sequencer_tx.value().try_clone_into_recovered().map_err(|_| { + PayloadBuilderError::other(OpPayloadBuilderError::TransactionEcRecoverFailed) + })?; // Cache the depositor account prior to the state transition for the deposit nonce. // @@ -777,9 +813,9 @@ where )) })?; - *evm.tx_mut() = self.evm_config.tx_env(sequencer_tx.tx(), sequencer_tx.signer()); + let tx_env = self.evm_config.tx_env(sequencer_tx.tx(), sequencer_tx.signer()); - let ResultAndState { result, state } = match evm.transact() { + let ResultAndState { result, state } = match evm.transact(tx_env) { Ok(res) => res, Err(err) => { match err { @@ -847,14 +883,14 @@ where DB: Database, { let block_gas_limit = self.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 = self.base_fee(); - let mut evm = - self.evm_config.evm_with_env(&mut *db, self.evm_env.clone(), Default::default()); + let mut evm = self.evm_config.evm_with_env(&mut *db, self.evm_env.clone()); while let Some(tx) = best_txs.next(()) { - // ensure we still have capacity for this transaction - if info.cumulative_gas_used + tx.gas_limit() > block_gas_limit { + if info.is_tx_over_limits(tx.tx(), block_gas_limit, tx_da_limit, block_da_limit) { // we can't fit this transaction into the block, so we need to mark it as // invalid which also removes all dependent transaction from // the iterator before we can continue @@ -874,9 +910,9 @@ where } // Configure the environment for the tx. - *evm.tx_mut() = self.evm_config.tx_env(tx.tx(), tx.signer()); + let tx_env = self.evm_config.tx_env(tx.tx(), tx.signer()); - let ResultAndState { result, state } = match evm.transact() { + let ResultAndState { result, state } = match evm.transact(tx_env) { Ok(res) => res, Err(err) => { match err { @@ -909,6 +945,7 @@ where // add gas used by the transaction to cumulative gas used, before creating the // receipt info.cumulative_gas_used += gas_used; + info.cumulative_da_bytes_used += tx.length() as u64; let receipt = alloy_consensus::Receipt { status: Eip658Value::Eip658(result.is_success()), diff --git a/crates/optimism/payload/src/error.rs b/crates/optimism/payload/src/error.rs index 6b2a85e7a97b..451d003cbcf1 100644 --- a/crates/optimism/payload/src/error.rs +++ b/crates/optimism/payload/src/error.rs @@ -4,7 +4,7 @@ #[derive(Debug, thiserror::Error)] pub enum OpPayloadBuilderError { /// Thrown when a transaction fails to convert to a - /// [`reth_primitives::RecoveredTx`]. + /// [`reth_primitives::Recovered`]. #[error("failed to convert deposit transaction to RecoveredTx")] TransactionEcRecoverFailed, /// Thrown when the L1 block info could not be parsed from the calldata of the diff --git a/crates/optimism/payload/src/payload.rs b/crates/optimism/payload/src/payload.rs index 10c4f2780cd5..26269b7e754b 100644 --- a/crates/optimism/payload/src/payload.rs +++ b/crates/optimism/payload/src/payload.rs @@ -11,7 +11,7 @@ use op_alloy_consensus::{encode_holocene_extra_data, EIP1559ParamError}; /// Re-export for use in downstream arguments. pub use op_alloy_rpc_types_engine::OpPayloadAttributes; use op_alloy_rpc_types_engine::{OpExecutionPayloadEnvelopeV3, OpExecutionPayloadEnvelopeV4}; -use reth_chain_state::ExecutedBlock; +use reth_chain_state::ExecutedBlockWithTrieUpdates; use reth_chainspec::EthereumHardforks; use reth_optimism_chainspec::OpChainSpec; use reth_optimism_primitives::{OpBlock, OpPrimitives, OpTransactionSigned}; @@ -137,7 +137,7 @@ pub struct OpBuiltPayload { /// The built 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, /// The blobs, proofs, and commitments in the block. If the block is pre-cancun, this will be @@ -159,7 +159,7 @@ impl OpBuiltPayload { fees: U256, chain_spec: Arc, attributes: OpPayloadBuilderAttributes, - executed_block: Option>, + executed_block: Option>, ) -> Self { Self { id, block, executed_block, fees, sidecars: Vec::new(), chain_spec, attributes } } @@ -196,7 +196,7 @@ impl BuiltPayload for OpBuiltPayload { self.fees } - fn executed_block(&self) -> Option> { + fn executed_block(&self) -> Option> { self.executed_block.clone() } @@ -216,7 +216,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/primitives/Cargo.toml b/crates/optimism/primitives/Cargo.toml index 6632cc53be93..3938a70ecbe2 100644 --- a/crates/optimism/primitives/Cargo.toml +++ b/crates/optimism/primitives/Cargo.toml @@ -35,7 +35,6 @@ serde = { workspace = true, optional = true } # misc derive_more = { workspace = true, features = ["deref", "from", "into", "constructor"] } -once_cell.workspace = true rand = { workspace = true, optional = true } # test @@ -67,7 +66,6 @@ std = [ "alloy-rlp/std", "reth-zstd-compressors?/std", "op-alloy-consensus/std", - "once_cell/std" ] reth-codec = [ "dep:reth-codecs", diff --git a/crates/optimism/primitives/src/lib.rs b/crates/optimism/primitives/src/lib.rs index 7b62586f4d1c..15d21dd6148e 100644 --- a/crates/optimism/primitives/src/lib.rs +++ b/crates/optimism/primitives/src/lib.rs @@ -38,5 +38,3 @@ impl reth_primitives_traits::NodePrimitives for OpPrimitives { type SignedTx = OpTransactionSigned; type Receipt = OpReceipt; } - -use once_cell as _; diff --git a/crates/optimism/primitives/src/transaction/signed.rs b/crates/optimism/primitives/src/transaction/signed.rs index c0d07105d2e0..a6def1d4a4af 100644 --- a/crates/optimism/primitives/src/transaction/signed.rs +++ b/crates/optimism/primitives/src/transaction/signed.rs @@ -20,18 +20,15 @@ use core::{ mem, }; use derive_more::{AsRef, Deref}; -#[cfg(not(feature = "std"))] -use once_cell::sync::OnceCell as OnceLock; use op_alloy_consensus::{DepositTransaction, OpPooledTransaction, OpTypedTransaction, TxDeposit}; #[cfg(any(test, feature = "reth-codec"))] use proptest as _; use reth_primitives_traits::{ crypto::secp256k1::{recover_signer, recover_signer_unchecked}, - transaction::error::TransactionConversionError, + sync::OnceLock, + transaction::{error::TransactionConversionError, signed::RecoveryError}, InMemorySize, SignedTransaction, }; -#[cfg(feature = "std")] -use std::sync::OnceLock; /// Signed transaction. #[cfg_attr(any(test, feature = "reth-codec"), reth_codecs::add_arbitrary_tests(rlp))] @@ -82,11 +79,11 @@ impl SignedTransaction for OpTransactionSigned { &self.signature } - fn recover_signer(&self) -> Option
{ + fn recover_signer(&self) -> Result { // Optimism's Deposit transaction does not have a signature. Directly return the // `from` address. if let OpTypedTransaction::Deposit(TxDeposit { from, .. }) = self.transaction { - return Some(from) + return Ok(from) } let Self { transaction, signature, .. } = self; @@ -94,11 +91,11 @@ impl SignedTransaction for OpTransactionSigned { recover_signer(signature, signature_hash) } - fn recover_signer_unchecked(&self) -> Option
{ + fn recover_signer_unchecked(&self) -> Result { // Optimism's Deposit transaction does not have a signature. Directly return the // `from` address. if let OpTypedTransaction::Deposit(TxDeposit { from, .. }) = &self.transaction { - return Some(*from) + return Ok(*from) } let Self { transaction, signature, .. } = self; @@ -106,11 +103,14 @@ impl SignedTransaction for OpTransactionSigned { recover_signer_unchecked(signature, signature_hash) } - fn recover_signer_unchecked_with_buf(&self, buf: &mut Vec) -> Option
{ + fn recover_signer_unchecked_with_buf( + &self, + buf: &mut Vec, + ) -> Result { match &self.transaction { // Optimism's Deposit transaction does not have a signature. Directly return the // `from` address. - OpTypedTransaction::Deposit(tx) => return Some(tx.from), + OpTypedTransaction::Deposit(tx) => return Ok(tx.from), OpTypedTransaction::Legacy(tx) => tx.encode_for_signing(buf), OpTypedTransaction::Eip2930(tx) => tx.encode_for_signing(buf), OpTypedTransaction::Eip1559(tx) => tx.encode_for_signing(buf), @@ -124,6 +124,19 @@ impl SignedTransaction for OpTransactionSigned { } } +/// A trait that represents an optimism transaction, mainly used to indicate whether or not the +/// transaction is a deposit transaction. +pub trait OpTransaction { + /// Whether or not the transaction is a dpeosit transaction. + fn is_deposit(&self) -> bool; +} + +impl OpTransaction for OpTransactionSigned { + fn is_deposit(&self) -> bool { + self.is_deposit() + } +} + #[cfg(feature = "optimism")] impl reth_primitives_traits::FillTxEnv for OpTransactionSigned { fn fill_tx_env(&self, tx_env: &mut revm_primitives::TxEnv, sender: Address) { @@ -619,10 +632,6 @@ impl DepositTransaction for OpTransactionSigned { fn is_system_transaction(&self) -> bool { self.is_deposit() } - - fn is_deposit(&self) -> bool { - self.is_deposit() - } } /// Bincode-compatible transaction type serde implementations. diff --git a/crates/optimism/rpc/src/eth/call.rs b/crates/optimism/rpc/src/eth/call.rs index 959d765e3491..aed5cf689f7b 100644 --- a/crates/optimism/rpc/src/eth/call.rs +++ b/crates/optimism/rpc/src/eth/call.rs @@ -28,7 +28,8 @@ where impl Call for OpEthApi where - Self: LoadState>> + SpawnBlocking, + Self: LoadState, TxEnv = TxEnv>> + + SpawnBlocking, Self::Error: From, N: OpNodeCore, { diff --git a/crates/optimism/rpc/src/eth/transaction.rs b/crates/optimism/rpc/src/eth/transaction.rs index ba3b43d071f6..a2a425dc7723 100644 --- a/crates/optimism/rpc/src/eth/transaction.rs +++ b/crates/optimism/rpc/src/eth/transaction.rs @@ -7,7 +7,7 @@ use op_alloy_consensus::{OpTxEnvelope, OpTypedTransaction}; use op_alloy_rpc_types::{OpTransactionRequest, Transaction}; use reth_node_api::FullNodeComponents; use reth_optimism_primitives::{OpReceipt, OpTransactionSigned}; -use reth_primitives::RecoveredTx; +use reth_primitives::Recovered; use reth_primitives_traits::transaction::signed::SignedTransaction; use reth_provider::{ BlockReader, BlockReaderIdExt, ProviderTx, ReceiptProvider, TransactionsProvider, @@ -84,7 +84,7 @@ where fn fill( &self, - tx: RecoveredTx, + tx: Recovered, tx_info: TransactionInfo, ) -> Result { let from = tx.signer(); @@ -119,7 +119,7 @@ where block_hash, block_number, index: transaction_index, base_fee, .. } = tx_info; - let effective_gas_price = if inner.is_deposit() { + let effective_gas_price = if matches!(inner, OpTxEnvelope::Deposit(_)) { // For deposits, we must always set the `gasPrice` field to 0 in rpc // deposit tx don't have a gas price field, but serde of `Transaction` will take care of // it diff --git a/crates/payload/basic/Cargo.toml b/crates/payload/basic/Cargo.toml index f50f6159bd0f..9fc7760de09e 100644 --- a/crates/payload/basic/Cargo.toml +++ b/crates/payload/basic/Cargo.toml @@ -15,6 +15,7 @@ workspace = true # reth reth-chainspec.workspace = true reth-primitives.workspace = true +reth-primitives-traits.workspace = true reth-transaction-pool.workspace = true reth-provider.workspace = true reth-payload-builder.workspace = true diff --git a/crates/payload/basic/src/lib.rs b/crates/payload/basic/src/lib.rs index b8cec6dacedc..32e6ec30201b 100644 --- a/crates/payload/basic/src/lib.rs +++ b/crates/payload/basic/src/lib.rs @@ -19,7 +19,8 @@ use reth_evm::state_change::post_block_withdrawals_balance_increments; use reth_payload_builder::{KeepPayloadJobAlive, PayloadId, PayloadJob, PayloadJobGenerator}; use reth_payload_builder_primitives::PayloadBuilderError; use reth_payload_primitives::{BuiltPayload, PayloadBuilderAttributes, PayloadKind}; -use reth_primitives::{proofs, NodePrimitives, SealedHeader}; +use reth_primitives::{NodePrimitives, SealedHeader}; +use reth_primitives_traits::proofs; use reth_provider::{BlockReaderIdExt, CanonStateNotification, StateProviderFactory}; use reth_revm::cached::CachedReads; use reth_tasks::TaskSpawner; diff --git a/crates/payload/builder/src/test_utils.rs b/crates/payload/builder/src/test_utils.rs index 9cd680ce6521..f38c8e0038c9 100644 --- a/crates/payload/builder/src/test_utils.rs +++ b/crates/payload/builder/src/test_utils.rs @@ -6,7 +6,7 @@ use crate::{ }; use alloy_primitives::U256; -use reth_chain_state::{CanonStateNotification, ExecutedBlock}; +use reth_chain_state::{CanonStateNotification, ExecutedBlockWithTrieUpdates}; use reth_payload_builder_primitives::PayloadBuilderError; use reth_payload_primitives::{PayloadKind, PayloadTypes}; use reth_primitives::Block; @@ -90,7 +90,7 @@ impl PayloadJob for TestPayloadJob { self.attr.payload_id(), Arc::new(Block::default().seal_slow()), U256::ZERO, - Some(ExecutedBlock::default()), + Some(ExecutedBlockWithTrieUpdates::default()), Some(Default::default()), )) } diff --git a/crates/payload/primitives/Cargo.toml b/crates/payload/primitives/Cargo.toml index caeb538e1e2d..78f054e6d255 100644 --- a/crates/payload/primitives/Cargo.toml +++ b/crates/payload/primitives/Cargo.toml @@ -35,4 +35,16 @@ tokio = { workspace = true, default-features = false, features = ["sync"] } assert_matches.workspace = true [features] +default = ["std"] +std = [ + "reth-chainspec/std", + "reth-primitives/std", + "revm-primitives/std", + "alloy-eips/std", + "alloy-primitives/std", + "alloy-rpc-types-engine/std", + "op-alloy-rpc-types-engine?/std", + "serde/std", + "thiserror/std", +] op = ["dep:op-alloy-rpc-types-engine"] \ No newline at end of file diff --git a/crates/payload/primitives/src/error.rs b/crates/payload/primitives/src/error.rs index ffe4e027e966..c4f518b66263 100644 --- a/crates/payload/primitives/src/error.rs +++ b/crates/payload/primitives/src/error.rs @@ -1,5 +1,6 @@ //! Error types emitted by types or implementations of this crate. +use alloc::boxed::Box; use alloy_primitives::B256; use alloy_rpc_types_engine::ForkchoiceUpdateError; use reth_errors::{ProviderError, RethError}; diff --git a/crates/payload/primitives/src/lib.rs b/crates/payload/primitives/src/lib.rs index eaafaf9959e6..676748c46b72 100644 --- a/crates/payload/primitives/src/lib.rs +++ b/crates/payload/primitives/src/lib.rs @@ -7,7 +7,11 @@ )] #![cfg_attr(not(test), warn(unused_crate_dependencies))] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +#![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + +use crate::alloc::string::ToString; use alloy_primitives::Bytes; use reth_chainspec::EthereumHardforks; @@ -379,14 +383,14 @@ pub fn validate_execution_requests(requests: &[Bytes]) -> Result<(), EngineObjec for request in requests { if request.len() <= 1 { return Err(EngineObjectValidationError::InvalidParams( - "empty execution request".to_string().into(), + "EmptyExecutionRequest".to_string().into(), )) } let request_type = request[0]; if Some(request_type) < last_request_type { return Err(EngineObjectValidationError::InvalidParams( - "execution requests out of order".to_string().into(), + "OutOfOrderExecutionRequest".to_string().into(), )) } diff --git a/crates/payload/primitives/src/payload.rs b/crates/payload/primitives/src/payload.rs index bcf48cea8343..5f4eec46ea9a 100644 --- a/crates/payload/primitives/src/payload.rs +++ b/crates/payload/primitives/src/payload.rs @@ -1,4 +1,5 @@ use crate::{MessageValidationKind, PayloadAttributes}; +use alloc::vec::Vec; use alloy_eips::eip4895::Withdrawal; use alloy_primitives::B256; use alloy_rpc_types_engine::ExecutionPayload; diff --git a/crates/payload/primitives/src/traits.rs b/crates/payload/primitives/src/traits.rs index 05c58e35b94a..18100c796233 100644 --- a/crates/payload/primitives/src/traits.rs +++ b/crates/payload/primitives/src/traits.rs @@ -1,15 +1,17 @@ +use alloc::vec::Vec; use alloy_eips::{ eip4895::{Withdrawal, Withdrawals}, eip7685::Requests, }; use alloy_primitives::{Address, B256, U256}; use alloy_rpc_types_engine::{PayloadAttributes as EthPayloadAttributes, PayloadId}; -use reth_chain_state::ExecutedBlock; +use core::fmt; +use reth_chain_state::ExecutedBlockWithTrieUpdates; use reth_primitives::{NodePrimitives, SealedBlock}; /// Represents a built payload type that contains a built `SealedBlock` and can be converted into /// engine API execution payloads. -pub trait BuiltPayload: Send + Sync + std::fmt::Debug { +pub trait BuiltPayload: Send + Sync + fmt::Debug { /// The node's primitive types type Primitives: NodePrimitives; @@ -20,7 +22,7 @@ pub trait BuiltPayload: Send + Sync + std::fmt::Debug { fn fees(&self) -> U256; /// Returns the entire execution data for the built block, if available. - fn executed_block(&self) -> Option> { + fn executed_block(&self) -> Option> { None } @@ -32,7 +34,7 @@ pub trait BuiltPayload: Send + Sync + std::fmt::Debug { /// /// This is used as a conversion type, transforming a payload attributes type that the engine API /// receives, into a type that the payload builder can use. -pub trait PayloadBuilderAttributes: Send + Sync + std::fmt::Debug { +pub trait PayloadBuilderAttributes: Send + Sync + fmt::Debug { /// The payload attributes that can be used to construct this type. Used as the argument in /// [`PayloadBuilderAttributes::try_new`]. type RpcPayloadAttributes; @@ -77,7 +79,7 @@ pub trait PayloadBuilderAttributes: Send + Sync + std::fmt::Debug { /// /// This type is emitted as part of the forkchoiceUpdated call pub trait PayloadAttributes: - serde::de::DeserializeOwned + serde::Serialize + std::fmt::Debug + Clone + Send + Sync + 'static + serde::de::DeserializeOwned + serde::Serialize + fmt::Debug + Clone + Send + Sync + 'static { /// Returns the timestamp to be used in the payload job. fn timestamp(&self) -> u64; diff --git a/crates/payload/util/src/traits.rs b/crates/payload/util/src/traits.rs index 3baed7d9da25..43e38312f678 100644 --- a/crates/payload/util/src/traits.rs +++ b/crates/payload/util/src/traits.rs @@ -1,5 +1,5 @@ use alloy_primitives::Address; -use reth_primitives::RecoveredTx; +use reth_primitives::Recovered; /// Iterator that returns transactions for the block building process in the order they should be /// included in the block. @@ -15,7 +15,7 @@ pub trait PayloadTransactions { &mut self, // In the future, `ctx` can include access to state for block building purposes. ctx: (), - ) -> Option>; + ) -> Option>; /// Exclude descendants of the transaction with given sender and nonce from the iterator, /// because this transaction won't be included in the block. @@ -35,7 +35,7 @@ impl Default for NoopPayloadTransactions { impl PayloadTransactions for NoopPayloadTransactions { type Transaction = T; - fn next(&mut self, _ctx: ()) -> Option> { + fn next(&mut self, _ctx: ()) -> Option> { None } diff --git a/crates/payload/util/src/transaction.rs b/crates/payload/util/src/transaction.rs index e6e41d7b1e9a..0893e8d10325 100644 --- a/crates/payload/util/src/transaction.rs +++ b/crates/payload/util/src/transaction.rs @@ -1,7 +1,7 @@ use crate::PayloadTransactions; use alloy_consensus::Transaction; use alloy_primitives::Address; -use reth_primitives::RecoveredTx; +use reth_primitives::Recovered; /// An implementation of [`crate::traits::PayloadTransactions`] that yields /// a pre-defined set of transactions. @@ -26,10 +26,10 @@ impl PayloadTransactionsFixed { } } -impl PayloadTransactions for PayloadTransactionsFixed> { +impl PayloadTransactions for PayloadTransactionsFixed> { type Transaction = T; - fn next(&mut self, _ctx: ()) -> Option> { + fn next(&mut self, _ctx: ()) -> Option> { (self.index < self.transactions.len()).then(|| { let tx = self.transactions[self.index].clone(); self.index += 1; @@ -96,7 +96,7 @@ where { type Transaction = A::Transaction; - fn next(&mut self, ctx: ()) -> Option> { + fn next(&mut self, ctx: ()) -> Option> { while let Some(tx) = self.before.next(ctx) { if let Some(before_max_gas) = self.before_max_gas { if self.before_gas + tx.tx().gas_limit() <= before_max_gas { diff --git a/crates/primitives-traits/Cargo.toml b/crates/primitives-traits/Cargo.toml index d017ea650e4a..7f0c98fc85b4 100644 --- a/crates/primitives-traits/Cargo.toml +++ b/crates/primitives-traits/Cargo.toml @@ -54,6 +54,7 @@ rayon = { workspace = true, optional = true } [dev-dependencies] reth-codecs.workspace = true +reth-chainspec = { workspace = true, features = ["arbitrary"] } alloy-primitives = { workspace = true, features = ["arbitrary", "serde"] } alloy-consensus = { workspace = true, features = ["arbitrary", "serde"] } @@ -93,12 +94,14 @@ std = [ "thiserror/std", "alloy-trie/std", "op-alloy-consensus?/std", - "serde_json/std" + "serde_json/std", + "reth-chainspec/std" ] secp256k1 = ["dep:secp256k1"] test-utils = [ "arbitrary", - "reth-codecs?/test-utils" + "reth-codecs?/test-utils", + "reth-chainspec/test-utils" ] arbitrary = [ "std", @@ -113,7 +116,8 @@ arbitrary = [ "secp256k1?/global-context", "secp256k1?/rand", "op-alloy-consensus?/arbitrary", - "alloy-trie/arbitrary" + "alloy-trie/arbitrary", + "reth-chainspec/arbitrary" ] serde-bincode-compat = [ "serde", diff --git a/crates/primitives-traits/src/block/body.rs b/crates/primitives-traits/src/block/body.rs index 7120d95b3577..783480c02a04 100644 --- a/crates/primitives-traits/src/block/body.rs +++ b/crates/primitives-traits/src/block/body.rs @@ -33,18 +33,37 @@ pub trait BlockBody: + MaybeSerde + 'static { - /// Ordered list of signed transactions as committed in block. + /// Ordered list of signed transactions as committed in the block. type Transaction: SignedTransaction; /// Ommer header type. type OmmerHeader: BlockHeader; - /// Returns reference to transactions in block. + /// Returns reference to transactions in the block. fn transactions(&self) -> &[Self::Transaction]; + /// Returns an iterator over the transactions in the block. + fn transactions_iter(&self) -> impl Iterator { + self.transactions().iter() + } + + /// Returns the transaction with the matching hash. + /// + /// This is a convenience function for `transactions_iter().find()` + fn transaction_by_hash(&self, hash: &B256) -> Option<&Self::Transaction> { + self.transactions_iter().find(|tx| tx.tx_hash() == hash) + } + + /// Clones the transactions in the block. + /// + /// This is a convenience function for `transactions().to_vec()` + fn clone_transactions(&self) -> Vec { + self.transactions().to_vec() + } + /// Returns an iterator over all transaction hashes in the block body. fn transaction_hashes_iter(&self) -> impl Iterator + '_ { - self.transactions().iter().map(|tx| tx.tx_hash()) + self.transactions_iter().map(|tx| tx.tx_hash()) } /// Returns the number of the transactions in the block. @@ -57,7 +76,7 @@ pub trait BlockBody: /// Returns `true` if the block body contains a transaction of the given type. fn contains_transaction_type(&self, tx_type: u8) -> bool { - self.transactions().iter().any(|tx| tx.is_type(tx_type)) + self.transactions_iter().any(|tx| tx.is_type(tx_type)) } /// Calculate the transaction root for the block body. @@ -89,12 +108,12 @@ pub trait BlockBody: /// Calculates the total blob gas used by _all_ EIP-4844 transactions in the block. fn blob_gas_used(&self) -> u64 { - self.transactions().iter().filter_map(|tx| tx.blob_gas_used()).sum() + self.transactions_iter().filter_map(|tx| tx.blob_gas_used()).sum() } /// Returns an iterator over all blob versioned hashes in the block body. fn blob_versioned_hashes_iter(&self) -> impl Iterator + '_ { - self.transactions().iter().filter_map(|tx| tx.blob_versioned_hashes()).flatten() + self.transactions_iter().filter_map(|tx| tx.blob_versioned_hashes()).flatten() } /// Returns an iterator over the encoded 2718 transactions. @@ -104,7 +123,7 @@ pub trait BlockBody: /// See also [`Encodable2718`]. #[doc(alias = "raw_transactions_iter")] fn encoded_2718_transactions_iter(&self) -> impl Iterator> + '_ { - self.transactions().iter().map(|tx| tx.encoded_2718()) + self.transactions_iter().map(|tx| tx.encoded_2718()) } /// Returns a vector of encoded 2718 transactions. @@ -118,11 +137,11 @@ pub trait BlockBody: } /// Recover signer addresses for all transactions in the block body. - fn recover_signers(&self) -> Option> + fn recover_signers(&self) -> Result, RecoveryError> where Self::Transaction: SignedTransaction, { - crate::transaction::recover::recover_signers(self.transactions()) + crate::transaction::recover::recover_signers(self.transactions()).map_err(|_| RecoveryError) } /// Recover signer addresses for all transactions in the block body. @@ -132,14 +151,14 @@ pub trait BlockBody: where Self::Transaction: SignedTransaction, { - self.recover_signers().ok_or(RecoveryError) + self.recover_signers() } /// Recover signer addresses for all transactions in the block body _without ensuring that the /// signature has a low `s` value_. /// /// Returns `None`, if some transaction's signature is invalid. - fn recover_signers_unchecked(&self) -> Option> + fn recover_signers_unchecked(&self) -> Result, RecoveryError> where Self::Transaction: SignedTransaction, { @@ -154,7 +173,7 @@ pub trait BlockBody: where Self::Transaction: SignedTransaction, { - self.recover_signers_unchecked().ok_or(RecoveryError) + self.recover_signers_unchecked() } } diff --git a/crates/primitives-traits/src/block/mod.rs b/crates/primitives-traits/src/block/mod.rs index f0a9c36702ae..464d5080fbcd 100644 --- a/crates/primitives-traits/src/block/mod.rs +++ b/crates/primitives-traits/src/block/mod.rs @@ -16,8 +16,8 @@ use alloy_primitives::{Address, B256}; use alloy_rlp::{Decodable, Encodable}; use crate::{ - BlockBody, BlockHeader, FullBlockBody, FullBlockHeader, InMemorySize, MaybeSerde, SealedHeader, - SignedTransaction, + block::error::BlockRecoveryError, transaction::signed::RecoveryError, BlockBody, BlockHeader, + FullBlockBody, FullBlockHeader, InMemorySize, MaybeSerde, SealedHeader, SignedTransaction, }; /// Bincode-compatible header type serde implementations. @@ -81,7 +81,7 @@ pub trait Block: /// Seal the block with a known hash. /// /// WARNING: This method does not perform validation whether the hash is correct. - fn seal(self, hash: B256) -> SealedBlock { + fn seal_unchecked(self, hash: B256) -> SealedBlock { SealedBlock::new_unchecked(self, hash) } @@ -121,60 +121,60 @@ pub trait Block: } /// Expensive operation that recovers transaction signer. - fn senders(&self) -> Option> + fn recover_signers(&self) -> Result, RecoveryError> where ::Transaction: SignedTransaction, { self.body().recover_signers() } - /// Transform into a [`RecoveredBlock`]. - /// - /// # Panics - /// - /// If the number of senders does not match the number of transactions in the block - /// and the signer recovery for one of the transactions fails. - /// - /// Note: this is expected to be called with blocks read from disk. - #[track_caller] - fn with_senders_unchecked(self, senders: Vec
) -> RecoveredBlock - where - ::Transaction: SignedTransaction, - { - self.try_with_senders_unchecked(senders).expect("stored block is valid") - } - - /// Transform into a [`RecoveredBlock`] using the given senders. + /// Transform the block into a [`RecoveredBlock`] using the given senders. /// /// If the number of senders does not match the number of transactions in the block, this falls /// back to manually recovery, but _without ensuring that the signature has a low `s` value_. /// - /// Returns an error if a signature is invalid. - #[track_caller] - fn try_with_senders_unchecked(self, senders: Vec
) -> Result, Self> + /// Returns the block as error if a signature is invalid. + fn try_into_recovered_unchecked( + self, + senders: Vec
, + ) -> Result, BlockRecoveryError> where ::Transaction: SignedTransaction, { let senders = if self.body().transactions().len() == senders.len() { senders } else { - let Some(senders) = self.body().recover_signers_unchecked() else { return Err(self) }; + // Fall back to recovery if lengths don't match + let Ok(senders) = self.body().recover_signers_unchecked() else { + return Err(BlockRecoveryError::new(self)) + }; senders }; - Ok(RecoveredBlock::new_unhashed(self, senders)) } + /// Transform the block into a [`RecoveredBlock`] using the given signers. + /// + /// Note: This method assumes the signers are correct and does not validate them. + fn into_recovered_with_signers(self, signers: Vec
) -> RecoveredBlock + where + ::Transaction: SignedTransaction, + { + RecoveredBlock::new_unhashed(self, signers) + } + /// **Expensive**. Transform into a [`RecoveredBlock`] by recovering senders in the contained /// transactions. /// - /// Returns `None` if a transaction is invalid. - fn with_recovered_senders(self) -> Option> + /// Returns the block as error if a signature is invalid. + fn try_into_recovered(self) -> Result, BlockRecoveryError> where ::Transaction: SignedTransaction, { - let senders = self.senders()?; - Some(RecoveredBlock::new_unhashed(self, senders)) + let Ok(signers) = self.body().recover_signers() else { + return Err(BlockRecoveryError::new(self)) + }; + Ok(RecoveredBlock::new_unhashed(self, signers)) } } diff --git a/crates/primitives-traits/src/block/recovered.rs b/crates/primitives-traits/src/block/recovered.rs index 0828646cf1f6..9ae2b5f0a631 100644 --- a/crates/primitives-traits/src/block/recovered.rs +++ b/crates/primitives-traits/src/block/recovered.rs @@ -291,9 +291,17 @@ impl RecoveredBlock { self.senders.iter().zip(self.block.body().transactions()) } - /// Returns an iterator over all transactions in the block. + /// Returns an iterator over `Recovered<&Transaction>` #[inline] - pub fn into_transactions_ecrecovered( + pub fn transactions_recovered( + &self, + ) -> impl Iterator::Transaction>> { + self.transactions_with_sender().map(|(sender, tx)| Recovered::new_unchecked(tx, *sender)) + } + + /// Consumes the type and returns an iterator over all [`Recovered`] transactions in the block. + #[inline] + pub fn into_transactions_recovered( self, ) -> impl Iterator::Transaction>> { self.block diff --git a/crates/primitives-traits/src/block/sealed.rs b/crates/primitives-traits/src/block/sealed.rs index 5ff43bff6724..611b1ad7e41b 100644 --- a/crates/primitives-traits/src/block/sealed.rs +++ b/crates/primitives-traits/src/block/sealed.rs @@ -2,6 +2,7 @@ use crate::{ block::{error::BlockRecoveryError, RecoveredBlock}, + transaction::signed::RecoveryError, Block, BlockBody, GotExpected, InMemorySize, SealedHeader, }; use alloc::vec::Vec; @@ -179,7 +180,7 @@ impl SealedBlock { /// Recovers all senders from the transactions in the block. /// /// Returns `None` if any of the transactions fail to recover the sender. - pub fn senders(&self) -> Option> { + pub fn senders(&self) -> Result, RecoveryError> { self.body().recover_signers() } diff --git a/crates/primitives-traits/src/crypto.rs b/crates/primitives-traits/src/crypto.rs index 99a6521cd3c5..0f59b97db4d8 100644 --- a/crates/primitives-traits/src/crypto.rs +++ b/crates/primitives-traits/src/crypto.rs @@ -22,6 +22,7 @@ pub mod secp256k1 { #[cfg(feature = "secp256k1")] use super::impl_secp256k1 as imp; + use crate::transaction::signed::RecoveryError; pub use imp::{public_key_to_address, sign_message}; /// Recover signer from message hash, _without ensuring that the signature has a low `s` @@ -30,7 +31,10 @@ pub mod secp256k1 { /// Using this for signature validation will succeed, even if the signature is malleable or not /// compliant with EIP-2. This is provided for compatibility with old signatures which have /// large `s` values. - pub fn recover_signer_unchecked(signature: &Signature, hash: B256) -> Option
{ + pub fn recover_signer_unchecked( + signature: &Signature, + hash: B256, + ) -> Result { let mut sig: [u8; 65] = [0; 65]; sig[0..32].copy_from_slice(&signature.r().to_be_bytes::<32>()); @@ -39,7 +43,7 @@ pub mod secp256k1 { // NOTE: we are removing error from underlying crypto library as it will restrain primitive // errors and we care only if recovery is passing or not. - imp::recover_signer_unchecked(&sig, &hash.0).ok() + imp::recover_signer_unchecked(&sig, &hash.0).map_err(|_| RecoveryError) } /// Recover signer address from message hash. This ensures that the signature S value is @@ -47,11 +51,10 @@ pub mod secp256k1 { /// [EIP-2](https://eips.ethereum.org/EIPS/eip-2). /// /// If the S value is too large, then this will return `None` - pub fn recover_signer(signature: &Signature, hash: B256) -> Option
{ + pub fn recover_signer(signature: &Signature, hash: B256) -> Result { if signature.s() > SECP256K1N_HALF { - return None + return Err(RecoveryError) } - recover_signer_unchecked(signature, hash) } } diff --git a/crates/primitives-traits/src/header/sealed.rs b/crates/primitives-traits/src/header/sealed.rs index c7a61f253206..0cae46d3b441 100644 --- a/crates/primitives-traits/src/header/sealed.rs +++ b/crates/primitives-traits/src/header/sealed.rs @@ -96,14 +96,16 @@ impl SealedHeader { let hash = self.hash(); (self.header, hash) } +} - /// Clones the header and returns a new sealed header. - pub fn cloned(self) -> Self +impl SealedHeader<&H> { + /// Maps a `SealedHeader<&H>` to a `SealedHeader` by cloning the header. + pub fn cloned(self) -> SealedHeader where H: Clone, { - let (header, hash) = self.split(); - Self::new(header, hash) + let Self { hash, header } = self; + SealedHeader { hash, header: header.clone() } } } diff --git a/crates/primitives-traits/src/lib.rs b/crates/primitives-traits/src/lib.rs index f09875ed3482..d82c7e1a2406 100644 --- a/crates/primitives-traits/src/lib.rs +++ b/crates/primitives-traits/src/lib.rs @@ -43,6 +43,14 @@ //! upgraded to a [`RecoveredBlock`] by recovering the sender addresses: //! [`SealedBlock::try_recover`]. A [`RecoveredBlock`] can be downgraded to a [`SealedBlock`] by //! removing the sender addresses: [`RecoveredBlock::into_sealed_block`]. +//! +//! #### Naming +//! +//! The types in this crate support multiple recovery functions, e.g. +//! [`SealedBlock::try_recover_unchecked`] and [`SealedBlock::try_recover_unchecked`]. The `_unchecked` suffix indicates that this function recovers the signer _without ensuring that the signature has a low `s` value_, in other words this rule introduced in [EIP-2](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2.md) is ignored. +//! Hence this function is necessary when dealing with pre EIP-2 transactions on the ethereum +//! mainnet. Newer transactions must always be recovered with the regular `recover` functions, see +//! also [`recover_signer`](crypto::secp256k1::recover_signer). #![doc( html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png", @@ -93,6 +101,8 @@ pub use error::{GotExpected, GotExpectedBoxed}; mod log; pub use alloy_primitives::{logs_bloom, Log, LogData}; +pub mod proofs; + mod storage; pub use storage::StorageEntry; diff --git a/crates/primitives-traits/src/proofs.rs b/crates/primitives-traits/src/proofs.rs new file mode 100644 index 000000000000..8d5d4bce46a6 --- /dev/null +++ b/crates/primitives-traits/src/proofs.rs @@ -0,0 +1,90 @@ +//! Helper function for calculating Merkle proofs and hashes. +pub use alloy_trie::root::ordered_trie_root_with_encoder; + +pub use alloy_consensus::proofs::calculate_receipt_root; + +/// Calculate a transaction root. +/// +/// `(rlp(index), encoded(tx))` pairs. +#[doc(inline)] +pub use alloy_consensus::proofs::calculate_transaction_root; + +/// Calculates the root hash of the withdrawals. +#[doc(inline)] +pub use alloy_consensus::proofs::calculate_withdrawals_root; + +/// Calculates the root hash for ommer/uncle headers. +#[doc(inline)] +pub use alloy_consensus::proofs::calculate_ommers_root; + +#[cfg(test)] +mod tests { + use alloy_consensus::EMPTY_ROOT_HASH; + use alloy_genesis::GenesisAccount; + use alloy_primitives::{b256, hex_literal::hex, Address, B256, U256}; + use alloy_trie::root::{state_root_ref_unhashed, state_root_unhashed}; + use reth_chainspec::{HOLESKY, MAINNET, SEPOLIA}; + use std::collections::HashMap; + + #[test] + fn check_empty_state_root() { + let genesis_alloc = HashMap::::new(); + let root = state_root_unhashed(genesis_alloc); + assert_eq!(root, EMPTY_ROOT_HASH); + } + + #[test] + fn test_simple_account_state_root() { + // each fixture specifies an address and expected root hash - the address is initialized + // with a maximum balance, and is the only account in the state. + // these test cases are generated by using geth with a custom genesis.json (with a single + // account that has max balance) + let fixtures: Vec<(Address, B256)> = vec![ + ( + hex!("9fe4abd71ad081f091bd06dd1c16f7e92927561e").into(), + hex!("4b35be4231841d212ce2fa43aedbddeadd6eb7d420195664f9f0d55629db8c32").into(), + ), + ( + hex!("c2ba9d87f8be0ade00c60d3656c1188e008fbfa2").into(), + hex!("e1389256c47d63df8856d7729dec9dc2dae074a7f0cbc49acad1cf7b29f7fe94").into(), + ), + ]; + + for (test_addr, expected_root) in fixtures { + let mut genesis_alloc = HashMap::new(); + genesis_alloc + .insert(test_addr, GenesisAccount { balance: U256::MAX, ..Default::default() }); + + let root = state_root_unhashed(genesis_alloc); + + assert_eq!(root, expected_root); + } + } + + #[test] + fn test_chain_state_roots() { + let expected_mainnet_state_root = + b256!("d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544"); + let calculated_mainnet_state_root = state_root_ref_unhashed(&MAINNET.genesis.alloc); + assert_eq!( + expected_mainnet_state_root, calculated_mainnet_state_root, + "mainnet state root mismatch" + ); + + let expected_sepolia_state_root = + b256!("5eb6e371a698b8d68f665192350ffcecbbbf322916f4b51bd79bb6887da3f494"); + let calculated_sepolia_state_root = state_root_ref_unhashed(&SEPOLIA.genesis.alloc); + assert_eq!( + expected_sepolia_state_root, calculated_sepolia_state_root, + "sepolia state root mismatch" + ); + + let expected_holesky_state_root = + b256!("69d8c9d72f6fa4ad42d4702b433707212f90db395eb54dc20bc85de253788783"); + let calculated_holesky_state_root = state_root_ref_unhashed(&HOLESKY.genesis.alloc); + assert_eq!( + expected_holesky_state_root, calculated_holesky_state_root, + "holesky state root mismatch" + ); + } +} diff --git a/crates/primitives-traits/src/size.rs b/crates/primitives-traits/src/size.rs index 185f9f08ecce..5a03d7a589dc 100644 --- a/crates/primitives-traits/src/size.rs +++ b/crates/primitives-traits/src/size.rs @@ -1,3 +1,4 @@ +use alloc::vec::Vec; use alloy_consensus::{ transaction::PooledTransaction, Header, TxEip1559, TxEip2930, TxEip4844, TxEip4844WithSidecar, TxEip7702, TxLegacy, TxType, @@ -104,41 +105,50 @@ impl InMemorySize for alloy_consensus::Block { } } -#[cfg(feature = "op")] -impl InMemorySize for op_alloy_consensus::OpDepositReceipt { +impl InMemorySize for Vec { fn size(&self) -> usize { - let Self { inner, deposit_nonce, deposit_receipt_version } = self; - inner.size() + - core::mem::size_of_val(deposit_nonce) + - core::mem::size_of_val(deposit_receipt_version) + // Note: This does not track additional capacity + self.iter().map(T::size).sum::() } } +/// Implementation for optimism types #[cfg(feature = "op")] -impl InMemorySize for op_alloy_consensus::OpTypedTransaction { - fn size(&self) -> usize { - match self { - Self::Legacy(tx) => tx.size(), - Self::Eip2930(tx) => tx.size(), - Self::Eip1559(tx) => tx.size(), - Self::Eip7702(tx) => tx.size(), - Self::Deposit(tx) => tx.size(), +mod op { + use super::*; + + impl InMemorySize for op_alloy_consensus::OpDepositReceipt { + fn size(&self) -> usize { + let Self { inner, deposit_nonce, deposit_receipt_version } = self; + inner.size() + + core::mem::size_of_val(deposit_nonce) + + core::mem::size_of_val(deposit_receipt_version) } } -} -#[cfg(feature = "op")] -impl InMemorySize for op_alloy_consensus::OpPooledTransaction { - fn size(&self) -> usize { - match self { - Self::Legacy(tx) => tx.size(), - Self::Eip2930(tx) => tx.size(), - Self::Eip1559(tx) => tx.size(), - Self::Eip7702(tx) => tx.size(), + impl InMemorySize for op_alloy_consensus::OpTypedTransaction { + fn size(&self) -> usize { + match self { + Self::Legacy(tx) => tx.size(), + Self::Eip2930(tx) => tx.size(), + Self::Eip1559(tx) => tx.size(), + Self::Eip7702(tx) => tx.size(), + Self::Deposit(tx) => tx.size(), + } } } -} + impl InMemorySize for op_alloy_consensus::OpPooledTransaction { + fn size(&self) -> usize { + match self { + Self::Legacy(tx) => tx.size(), + Self::Eip2930(tx) => tx.size(), + Self::Eip1559(tx) => tx.size(), + Self::Eip7702(tx) => tx.size(), + } + } + } +} #[cfg(test)] mod tests { use super::*; diff --git a/crates/primitives-traits/src/transaction/recover.rs b/crates/primitives-traits/src/transaction/recover.rs index cad57bc26607..a7436c3efae3 100644 --- a/crates/primitives-traits/src/transaction/recover.rs +++ b/crates/primitives-traits/src/transaction/recover.rs @@ -8,7 +8,7 @@ pub use iter::*; #[cfg(feature = "rayon")] mod rayon { - use crate::SignedTransaction; + use crate::{transaction::signed::RecoveryError, SignedTransaction}; use alloc::vec::Vec; use alloy_primitives::Address; use rayon::prelude::{IntoParallelIterator, ParallelIterator}; @@ -16,7 +16,7 @@ mod rayon { /// Recovers a list of signers from a transaction list iterator. /// /// Returns `None`, if some transaction's signature is invalid - pub fn recover_signers<'a, I, T>(txes: I) -> Option> + pub fn recover_signers<'a, I, T>(txes: I) -> Result, RecoveryError> where T: SignedTransaction, I: IntoParallelIterator + IntoIterator + Send, @@ -28,7 +28,7 @@ mod rayon { /// signature has a low `s` value_. /// /// Returns `None`, if some transaction's signature is invalid. - pub fn recover_signers_unchecked<'a, I, T>(txes: I) -> Option> + pub fn recover_signers_unchecked<'a, I, T>(txes: I) -> Result, RecoveryError> where T: SignedTransaction, I: IntoParallelIterator + IntoIterator + Send, @@ -39,14 +39,14 @@ mod rayon { #[cfg(not(feature = "rayon"))] mod iter { - use crate::SignedTransaction; + use crate::{transaction::signed::RecoveryError, SignedTransaction}; use alloc::vec::Vec; use alloy_primitives::Address; /// Recovers a list of signers from a transaction list iterator. /// - /// Returns `None`, if some transaction's signature is invalid - pub fn recover_signers<'a, I, T>(txes: I) -> Option> + /// Returns `Err(RecoveryError)`, if some transaction's signature is invalid + pub fn recover_signers<'a, I, T>(txes: I) -> Result, RecoveryError> where T: SignedTransaction, I: IntoIterator + IntoIterator, @@ -57,8 +57,8 @@ mod iter { /// Recovers a list of signers from a transaction list iterator _without ensuring that the /// signature has a low `s` value_. /// - /// Returns `None`, if some transaction's signature is invalid. - pub fn recover_signers_unchecked<'a, I, T>(txes: I) -> Option> + /// Returns `Err(RecoveryError)`, if some transaction's signature is invalid. + pub fn recover_signers_unchecked<'a, I, T>(txes: I) -> Result, RecoveryError> where T: SignedTransaction, I: IntoIterator + IntoIterator, diff --git a/crates/primitives-traits/src/transaction/signed.rs b/crates/primitives-traits/src/transaction/signed.rs index bc64b4521277..272951de3505 100644 --- a/crates/primitives-traits/src/transaction/signed.rs +++ b/crates/primitives-traits/src/transaction/signed.rs @@ -62,13 +62,13 @@ pub trait SignedTransaction: /// This can fail for some early ethereum mainnet transactions pre EIP-2, use /// [`Self::recover_signer_unchecked`] if you want to recover the signer without ensuring that /// the signature has a low `s` value. - fn recover_signer(&self) -> Option
; + fn recover_signer(&self) -> Result; /// Recover signer from signature and hash. /// /// Returns an error if the transaction's signature is invalid. fn try_recover(&self) -> Result { - self.recover_signer().ok_or(RecoveryError) + self.recover_signer().map_err(|_| RecoveryError) } /// Recover signer from signature and hash _without ensuring that the signature has a low `s` @@ -76,8 +76,8 @@ pub trait SignedTransaction: /// /// Returns `None` if the transaction's signature is invalid, see also /// `reth_primitives::transaction::recover_signer_unchecked`. - fn recover_signer_unchecked(&self) -> Option
{ - self.recover_signer_unchecked_with_buf(&mut Vec::new()) + fn recover_signer_unchecked(&self) -> Result { + self.recover_signer_unchecked_with_buf(&mut Vec::new()).map_err(|_| RecoveryError) } /// Recover signer from signature and hash _without ensuring that the signature has a low `s` @@ -85,12 +85,15 @@ pub trait SignedTransaction: /// /// Returns an error if the transaction's signature is invalid. fn try_recover_unchecked(&self) -> Result { - self.recover_signer_unchecked().ok_or(RecoveryError) + self.recover_signer_unchecked() } /// Same as [`Self::recover_signer_unchecked`] but receives a buffer to operate on. This is used /// during batch recovery to avoid allocating a new buffer for each transaction. - fn recover_signer_unchecked_with_buf(&self, buf: &mut Vec) -> Option
; + fn recover_signer_unchecked_with_buf( + &self, + buf: &mut Vec, + ) -> Result; /// Calculate transaction hash, eip2728 transaction does not contain rlp header and start with /// tx type. @@ -120,12 +123,15 @@ impl SignedTransaction for PooledTransaction { } } - fn recover_signer(&self) -> Option
{ + fn recover_signer(&self) -> Result { let signature_hash = self.signature_hash(); recover_signer(self.signature(), signature_hash) } - fn recover_signer_unchecked_with_buf(&self, buf: &mut Vec) -> Option
{ + fn recover_signer_unchecked_with_buf( + &self, + buf: &mut Vec, + ) -> Result { match self { Self::Legacy(tx) => tx.tx().encode_for_signing(buf), Self::Eip2930(tx) => tx.tx().encode_for_signing(buf), @@ -158,12 +164,15 @@ impl SignedTransaction for op_alloy_consensus::OpPooledTransaction { } } - fn recover_signer(&self) -> Option
{ + fn recover_signer(&self) -> Result { let signature_hash = self.signature_hash(); recover_signer(self.signature(), signature_hash) } - fn recover_signer_unchecked_with_buf(&self, buf: &mut Vec) -> Option
{ + fn recover_signer_unchecked_with_buf( + &self, + buf: &mut Vec, + ) -> Result { match self { Self::Legacy(tx) => tx.tx().encode_for_signing(buf), Self::Eip2930(tx) => tx.tx().encode_for_signing(buf), @@ -178,19 +187,18 @@ impl SignedTransaction for op_alloy_consensus::OpPooledTransaction { /// Extension trait for [`SignedTransaction`] to convert it into [`Recovered`]. pub trait SignedTransactionIntoRecoveredExt: SignedTransaction { /// Tries to recover signer and return [`Recovered`] by cloning the type. - fn try_ecrecovered(&self) -> Option> { - let signer = self.recover_signer()?; - Some(Recovered::new_unchecked(self.clone(), signer)) + fn try_clone_into_recovered(&self) -> Result, RecoveryError> { + self.recover_signer().map(|signer| Recovered::new_unchecked(self.clone(), signer)) } /// Tries to recover signer and return [`Recovered`]. /// /// Returns `Err(Self)` if the transaction's signature is invalid, see also /// [`SignedTransaction::recover_signer`]. - fn try_into_ecrecovered(self) -> Result, Self> { + fn try_into_recovered(self) -> Result, Self> { match self.recover_signer() { - None => Err(self), - Some(signer) => Ok(Recovered::new_unchecked(self, signer)), + Ok(signer) => Ok(Recovered::new_unchecked(self, signer)), + Err(_) => Err(self), } } @@ -198,12 +206,13 @@ pub trait SignedTransactionIntoRecoveredExt: SignedTransaction { /// ensuring that the signature has a low `s` value_ (EIP-2). /// /// Returns `None` if the transaction's signature is invalid. - fn into_ecrecovered_unchecked(self) -> Option> { - let signer = self.recover_signer_unchecked()?; - Some(Recovered::new_unchecked(self, signer)) + fn into_recovered_unchecked(self) -> Result, RecoveryError> { + self.recover_signer_unchecked().map(|signer| Recovered::new_unchecked(self, signer)) } /// Returns the [`Recovered`] transaction with the given sender. + /// + /// Note: assumes the given signer is the signer of this transaction. fn with_signer(self, signer: Address) -> Recovered { Recovered::new_unchecked(self, signer) } diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index 0206035bf117..064487e7250c 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -21,8 +21,6 @@ reth-static-file-types.workspace = true # ethereum alloy-consensus.workspace = true alloy-primitives = { workspace = true, features = ["rand", "rlp"] } -alloy-eips = { workspace = true, features = ["serde"] } -alloy-trie = { workspace = true, features = ["serde"] } # for eip-4844 c-kzg = { workspace = true, features = ["serde"], optional = true } @@ -37,9 +35,7 @@ arbitrary = { workspace = true, features = ["derive"], optional = true } [dev-dependencies] # eth -reth-chainspec = { workspace = true, features = ["arbitrary"] } reth-primitives-traits = { workspace = true, features = ["arbitrary", "test-utils"] } -reth-trie-common = { workspace = true, features = ["arbitrary"] } alloy-rlp.workspace = true alloy-eips = { workspace = true, features = ["arbitrary"] } @@ -47,7 +43,6 @@ alloy-genesis.workspace = true arbitrary = { workspace = true, features = ["derive"] } -assert_matches.workspace = true proptest-arbitrary-interop.workspace = true proptest.workspace = true serde_json.workspace = true @@ -70,12 +65,9 @@ std = [ "alloy-primitives/std", "once_cell/std", "serde/std", - "alloy-trie/std", "reth-ethereum-forks/std", "derive_more/std", - "reth-trie-common/std", "serde_json/std", - "reth-chainspec/std", "reth-ethereum-primitives/std", "alloy-rlp/std" ] @@ -91,11 +83,8 @@ arbitrary = [ "reth-codec", "reth-ethereum-forks/arbitrary", "reth-primitives-traits/arbitrary", - "reth-chainspec/arbitrary", "alloy-consensus/arbitrary", "alloy-primitives/arbitrary", - "alloy-trie/arbitrary", - "reth-trie-common/arbitrary", "reth-ethereum-primitives/arbitrary", "reth-codecs/arbitrary" ] @@ -112,8 +101,6 @@ alloy-compat = [ ] test-utils = [ "reth-primitives-traits/test-utils", - "reth-chainspec/test-utils", - "reth-trie-common/test-utils", "arbitrary", "reth-codecs/test-utils" ] @@ -121,7 +108,6 @@ serde-bincode-compat = [ "alloy-eips/serde-bincode-compat", "alloy-consensus/serde-bincode-compat", "reth-primitives-traits/serde-bincode-compat", - "reth-trie-common/serde-bincode-compat", "reth-ethereum-primitives/serde-bincode-compat" ] diff --git a/crates/primitives/benches/recover_ecdsa_crit.rs b/crates/primitives/benches/recover_ecdsa_crit.rs index 9273d71f6f56..af275c72a8c2 100644 --- a/crates/primitives/benches/recover_ecdsa_crit.rs +++ b/crates/primitives/benches/recover_ecdsa_crit.rs @@ -13,7 +13,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { let raw =hex!("f88b8212b085028fa6ae00830f424094aad593da0c8116ef7d2d594dd6a63241bccfc26c80a48318b64b000000000000000000000000641c5d790f862a58ec7abcfd644c0442e9c201b32aa0a6ef9e170bca5ffb7ac05433b13b7043de667fbb0b4a5e45d3b54fb2d6efcc63a0037ec2c05c3d60c5f5f78244ce0a3859e3a18a36c61efb061b383507d3ce19d2"); let mut pointer = raw.as_ref(); let tx = TransactionSigned::decode(&mut pointer).unwrap(); - tx.recover_signer(); + tx.recover_signer().unwrap(); } ) }); diff --git a/crates/primitives/src/block.rs b/crates/primitives/src/block.rs index f30ce2c70aa0..3ea6da42da2e 100644 --- a/crates/primitives/src/block.rs +++ b/crates/primitives/src/block.rs @@ -26,200 +26,3 @@ pub type BlockWithSenders = reth_primitives_traits::block::RecoveredB /// Ethereum recovered block #[deprecated(note = "Use `RecoveredBlock` instead")] pub type SealedBlockWithSenders = reth_primitives_traits::block::RecoveredBlock; - -#[cfg(test)] -mod tests { - use super::*; - use alloy_eips::{ - eip1898::HexStringMissingPrefixError, BlockId, BlockNumberOrTag, BlockNumberOrTag::*, - RpcBlockHash, - }; - use alloy_primitives::{hex_literal::hex, B256}; - use alloy_rlp::{Decodable, Encodable}; - use reth_primitives_traits::{BlockBody, RecoveredBlock}; - use std::str::FromStr; - - const fn _traits() { - const fn assert_block() {} - assert_block::(); - } - - /// Check parsing according to EIP-1898. - #[test] - fn can_parse_blockid_u64() { - let num = serde_json::json!( - {"blockNumber": "0xaf"} - ); - - let id = serde_json::from_value::(num); - assert_eq!(id.unwrap(), BlockId::from(175)); - } - #[test] - fn can_parse_block_hash() { - let block_hash = - B256::from_str("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") - .unwrap(); - let block_hash_json = serde_json::json!( - { "blockHash": "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"} - ); - let id = serde_json::from_value::(block_hash_json).unwrap(); - assert_eq!(id, BlockId::from(block_hash,)); - } - #[test] - fn can_parse_block_hash_with_canonical() { - let block_hash = - B256::from_str("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") - .unwrap(); - let block_id = BlockId::Hash(RpcBlockHash::from_hash(block_hash, Some(true))); - let block_hash_json = serde_json::json!( - { "blockHash": "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3", "requireCanonical": true } - ); - let id = serde_json::from_value::(block_hash_json).unwrap(); - assert_eq!(id, block_id) - } - #[test] - fn can_parse_blockid_tags() { - let tags = - [("latest", Latest), ("finalized", Finalized), ("safe", Safe), ("pending", Pending)]; - for (value, tag) in tags { - let num = serde_json::json!({ "blockNumber": value }); - let id = serde_json::from_value::(num); - assert_eq!(id.unwrap(), BlockId::from(tag)) - } - } - #[test] - fn repeated_keys_is_err() { - let num = serde_json::json!({"blockNumber": 1, "requireCanonical": true, "requireCanonical": false}); - assert!(serde_json::from_value::(num).is_err()); - let num = - serde_json::json!({"blockNumber": 1, "requireCanonical": true, "blockNumber": 23}); - assert!(serde_json::from_value::(num).is_err()); - } - /// Serde tests - #[test] - fn serde_blockid_tags() { - let block_ids = [Latest, Finalized, Safe, Pending].map(BlockId::from); - for block_id in &block_ids { - let serialized = serde_json::to_string(&block_id).unwrap(); - let deserialized: BlockId = serde_json::from_str(&serialized).unwrap(); - assert_eq!(deserialized, *block_id) - } - } - - #[test] - fn serde_blockid_number() { - let block_id = BlockId::from(100u64); - let serialized = serde_json::to_string(&block_id).unwrap(); - let deserialized: BlockId = serde_json::from_str(&serialized).unwrap(); - assert_eq!(deserialized, block_id) - } - - #[test] - fn serde_blockid_hash() { - let block_id = BlockId::from(B256::default()); - let serialized = serde_json::to_string(&block_id).unwrap(); - let deserialized: BlockId = serde_json::from_str(&serialized).unwrap(); - assert_eq!(deserialized, block_id) - } - - #[test] - fn serde_blockid_hash_from_str() { - let val = "\"0x898753d8fdd8d92c1907ca21e68c7970abd290c647a202091181deec3f30a0b2\""; - let block_hash: B256 = serde_json::from_str(val).unwrap(); - let block_id: BlockId = serde_json::from_str(val).unwrap(); - assert_eq!(block_id, BlockId::Hash(block_hash.into())); - } - - #[test] - fn serde_rpc_payload_block_tag() { - let payload = r#"{"method":"eth_call","params":[{"to":"0xebe8efa441b9302a0d7eaecc277c09d20d684540","data":"0x45848dfc"},"latest"],"id":1,"jsonrpc":"2.0"}"#; - let value: serde_json::Value = serde_json::from_str(payload).unwrap(); - let block_id_param = value.pointer("/params/1").unwrap(); - let block_id: BlockId = serde_json::from_value::(block_id_param.clone()).unwrap(); - assert_eq!(BlockId::Number(BlockNumberOrTag::Latest), block_id); - } - #[test] - fn serde_rpc_payload_block_object() { - let example_payload = r#"{"method":"eth_call","params":[{"to":"0xebe8efa441b9302a0d7eaecc277c09d20d684540","data":"0x45848dfc"},{"blockHash": "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"}],"id":1,"jsonrpc":"2.0"}"#; - let value: serde_json::Value = serde_json::from_str(example_payload).unwrap(); - let block_id_param = value.pointer("/params/1").unwrap().to_string(); - let block_id: BlockId = serde_json::from_str::(&block_id_param).unwrap(); - let hash = - B256::from_str("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") - .unwrap(); - assert_eq!(BlockId::from(hash), block_id); - let serialized = serde_json::to_string(&BlockId::from(hash)).unwrap(); - assert_eq!("{\"blockHash\":\"0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3\"}", serialized) - } - #[test] - fn serde_rpc_payload_block_number() { - let example_payload = r#"{"method":"eth_call","params":[{"to":"0xebe8efa441b9302a0d7eaecc277c09d20d684540","data":"0x45848dfc"},{"blockNumber": "0x0"}],"id":1,"jsonrpc":"2.0"}"#; - let value: serde_json::Value = serde_json::from_str(example_payload).unwrap(); - let block_id_param = value.pointer("/params/1").unwrap().to_string(); - let block_id: BlockId = serde_json::from_str::(&block_id_param).unwrap(); - assert_eq!(BlockId::from(0u64), block_id); - let serialized = serde_json::to_string(&BlockId::from(0u64)).unwrap(); - assert_eq!("\"0x0\"", serialized) - } - #[test] - #[should_panic] - fn serde_rpc_payload_block_number_duplicate_key() { - let payload = r#"{"blockNumber": "0x132", "blockNumber": "0x133"}"#; - let parsed_block_id = serde_json::from_str::(payload); - parsed_block_id.unwrap(); - } - #[test] - fn serde_rpc_payload_block_hash() { - let payload = r#"{"blockHash": "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"}"#; - let parsed = serde_json::from_str::(payload).unwrap(); - let expected = BlockId::from( - B256::from_str("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") - .unwrap(), - ); - assert_eq!(parsed, expected); - } - - #[test] - fn encode_decode_raw_block() { - let bytes = hex!("f90288f90218a0fe21bb173f43067a9f90cfc59bbb6830a7a2929b5de4a61f372a9db28e87f9aea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a061effbbcca94f0d3e02e5bd22e986ad57142acabf0cb3d129a6ad8d0f8752e94a0d911c25e97e27898680d242b7780b6faef30995c355a2d5de92e6b9a7212ad3aa0056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008003834c4b408252081e80a00000000000000000000000000000000000000000000000000000000000000000880000000000000000842806be9da056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421f869f86702842806be9e82520894658bdf435d810c91414ec09147daa6db624063798203e880820a95a040ce7918eeb045ebf8c8b1887ca139d076bda00fa828a07881d442a72626c42da0156576a68e456e295e4c9cf67cf9f53151f329438916e0f24fc69d6bbb7fbacfc0c0"); - let bytes_buf = &mut bytes.as_ref(); - let block: Block = Block::decode(bytes_buf).unwrap(); - let mut encoded_buf = Vec::with_capacity(bytes.len()); - block.encode(&mut encoded_buf); - assert_eq!(bytes[..], encoded_buf); - } - - #[test] - fn serde_blocknumber_non_0xprefix() { - let s = "\"2\""; - let err = serde_json::from_str::(s).unwrap_err(); - assert_eq!(err.to_string(), HexStringMissingPrefixError::default().to_string()); - } - - #[test] - fn block_with_senders() { - let mut block: Block = Block::default(); - block.body.transactions.push(TransactionSigned::default()); - let block = RecoveredBlock::try_new_unhashed(block.clone(), vec![]).unwrap(); - assert_eq!(block.senders().len(), 1); - } - - #[test] - fn empty_block_rlp() { - let body = alloy_consensus::BlockBody::::default(); - let mut buf = Vec::new(); - body.encode(&mut buf); - let decoded = alloy_consensus::BlockBody::decode(&mut buf.as_slice()).unwrap(); - assert_eq!(body, decoded); - } - - #[test] - fn test_transaction_count() { - let mut block = Block::default(); - assert_eq!(block.body.transaction_count(), 0); - block.body.transactions.push(TransactionSigned::default()); - assert_eq!(block.body.transaction_count(), 1); - block.body.transactions.push(TransactionSigned::default()); - assert_eq!(block.body.transaction_count(), 2); - } -} diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 3d05fb9bf237..38f6b79c18a3 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -22,7 +22,6 @@ extern crate alloc; mod block; -pub mod proofs; mod receipt; pub use reth_static_file_types as static_file; pub mod transaction; @@ -40,14 +39,20 @@ pub use reth_primitives_traits::{ pub use static_file::StaticFileSegment; pub use alloy_consensus::{ - transaction::{PooledTransaction, Recovered as RecoveredTx, TransactionMeta}, + transaction::{PooledTransaction, Recovered, TransactionMeta}, ReceiptWithBloom, }; + +/// Recovered transaction +#[deprecated(note = "use `Recovered` instead")] +pub type RecoveredTx = Recovered; + pub use transaction::{ util::secp256k1::{public_key_to_address, recover_signer_unchecked, sign_message}, - InvalidTransactionError, PooledTransactionsElementEcRecovered, Transaction, TransactionSigned, - TransactionSignedEcRecovered, TxType, + InvalidTransactionError, Transaction, TransactionSigned, TxType, }; +#[allow(deprecated)] +pub use transaction::{PooledTransactionsElementEcRecovered, TransactionSignedEcRecovered}; // Re-exports pub use reth_ethereum_forks::*; diff --git a/crates/primitives/src/receipt.rs b/crates/primitives/src/receipt.rs index 0d946dc45a75..b792f3877ac9 100644 --- a/crates/primitives/src/receipt.rs +++ b/crates/primitives/src/receipt.rs @@ -24,7 +24,7 @@ pub use reth_ethereum_primitives::Receipt; )] pub struct Receipts { /// A two-dimensional vector of optional `Receipt` instances. - pub receipt_vec: Vec>>, + pub receipt_vec: Vec>, } impl Receipts { @@ -39,26 +39,25 @@ impl Receipts { } /// Push a new vector of receipts into the `Receipts` collection. - pub fn push(&mut self, receipts: Vec>) { + pub fn push(&mut self, receipts: Vec) { self.receipt_vec.push(receipts); } /// Retrieves all recorded receipts from index and calculates the root using the given closure. - pub fn root_slow(&self, index: usize, f: impl FnOnce(&[&T]) -> B256) -> Option { - let receipts = - self.receipt_vec[index].iter().map(Option::as_ref).collect::>>()?; - Some(f(receipts.as_slice())) + pub fn root_slow(&self, index: usize, f: impl FnOnce(&[&T]) -> B256) -> B256 { + let receipts = self.receipt_vec[index].iter().collect::>(); + f(receipts.as_slice()) } } impl From> for Receipts { fn from(block_receipts: Vec) -> Self { - Self { receipt_vec: vec![block_receipts.into_iter().map(Option::Some).collect()] } + Self { receipt_vec: vec![block_receipts.into_iter().collect()] } } } -impl FromIterator>> for Receipts { - fn from_iter>>>(iter: I) -> Self { +impl FromIterator> for Receipts { + fn from_iter>>(iter: I) -> Self { iter.into_iter().collect::>().into() } } diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index 3e5c48a4660a..4bd2cc975d37 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -1,8 +1,9 @@ //! Transaction types. -use crate::RecoveredTx; +use crate::Recovered; pub use alloy_consensus::transaction::PooledTransaction; use once_cell as _; +#[allow(deprecated)] pub use pooled::PooledTransactionsElementEcRecovered; pub use reth_primitives_traits::{ sync::{LazyLock, OnceLock}, @@ -29,370 +30,5 @@ mod tx_type; pub use reth_ethereum_primitives::{Transaction, TransactionSigned}; /// Type alias kept for backward compatibility. -pub type TransactionSignedEcRecovered = RecoveredTx; - -#[cfg(test)] -mod tests { - use crate::{Transaction, TransactionSigned}; - use alloy_consensus::{Transaction as _, TxEip1559, TxLegacy}; - use alloy_eips::eip2718::{Decodable2718, Encodable2718}; - use alloy_primitives::{ - address, b256, bytes, hex, Address, Bytes, PrimitiveSignature as Signature, TxKind, B256, - U256, - }; - use alloy_rlp::{Decodable, Encodable, Error as RlpError}; - use reth_chainspec::MIN_TRANSACTION_GAS; - use reth_codecs::Compact; - use reth_primitives_traits::SignedTransaction; - use std::str::FromStr; - - #[test] - fn test_decode_empty_typed_tx() { - let input = [0x80u8]; - let res = TransactionSigned::decode(&mut &input[..]).unwrap_err(); - assert_eq!(RlpError::InputTooShort, res); - } - - #[test] - fn raw_kind_encoding_sanity() { - // check the 0x80 encoding for Create - let mut buf = Vec::new(); - TxKind::Create.encode(&mut buf); - assert_eq!(buf, vec![0x80]); - - // check decoding - let buf = [0x80]; - let decoded = TxKind::decode(&mut &buf[..]).unwrap(); - assert_eq!(decoded, TxKind::Create); - } - - #[test] - fn test_decode_create_goerli() { - // test that an example create tx from goerli decodes properly - let tx_bytes = hex!("b901f202f901ee05228459682f008459682f11830209bf8080b90195608060405234801561001057600080fd5b50610175806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80630c49c36c14610030575b600080fd5b61003861004e565b604051610045919061011d565b60405180910390f35b60606020600052600f6020527f68656c6c6f2073746174656d696e64000000000000000000000000000000000060405260406000f35b600081519050919050565b600082825260208201905092915050565b60005b838110156100be5780820151818401526020810190506100a3565b838111156100cd576000848401525b50505050565b6000601f19601f8301169050919050565b60006100ef82610084565b6100f9818561008f565b93506101098185602086016100a0565b610112816100d3565b840191505092915050565b6000602082019050818103600083015261013781846100e4565b90509291505056fea264697066735822122051449585839a4ea5ac23cae4552ef8a96b64ff59d0668f76bfac3796b2bdbb3664736f6c63430008090033c080a0136ebffaa8fc8b9fda9124de9ccb0b1f64e90fbd44251b4c4ac2501e60b104f9a07eb2999eec6d185ef57e91ed099afb0a926c5b536f0155dd67e537c7476e1471"); - - let decoded = TransactionSigned::decode(&mut &tx_bytes[..]).unwrap(); - assert_eq!(tx_bytes.len(), decoded.length()); - assert_eq!(tx_bytes, &alloy_rlp::encode(decoded)[..]); - } - - #[test] - fn test_decode_recover_mainnet_tx() { - // random mainnet tx - let tx_bytes = hex!("02f872018307910d808507204d2cb1827d0094388c818ca8b9251b393131c08a736a67ccb19297880320d04823e2701c80c001a0cf024f4815304df2867a1a74e9d2707b6abda0337d2d54a4438d453f4160f190a07ac0e6b3bc9395b5b9c8b9e6d77204a236577a5b18467b9175c01de4faa208d9"); - - let decoded = TransactionSigned::decode_2718(&mut &tx_bytes[..]).unwrap(); - assert_eq!( - decoded.recover_signer(), - Some(Address::from_str("0x95222290DD7278Aa3Ddd389Cc1E1d165CC4BAfe5").unwrap()) - ); - } - - #[test] - // Test vector from https://sepolia.etherscan.io/tx/0x9a22ccb0029bc8b0ddd073be1a1d923b7ae2b2ea52100bae0db4424f9107e9c0 - // Blobscan: https://sepolia.blobscan.com/tx/0x9a22ccb0029bc8b0ddd073be1a1d923b7ae2b2ea52100bae0db4424f9107e9c0 - fn test_decode_recover_sepolia_4844_tx() { - use alloy_primitives::{address, b256}; - - // https://sepolia.etherscan.io/getRawTx?tx=0x9a22ccb0029bc8b0ddd073be1a1d923b7ae2b2ea52100bae0db4424f9107e9c0 - let raw_tx = alloy_primitives::hex::decode("0x03f9011d83aa36a7820fa28477359400852e90edd0008252089411e9ca82a3a762b4b5bd264d4173a242e7a770648080c08504a817c800f8a5a0012ec3d6f66766bedb002a190126b3549fce0047de0d4c25cffce0dc1c57921aa00152d8e24762ff22b1cfd9f8c0683786a7ca63ba49973818b3d1e9512cd2cec4a0013b98c6c83e066d5b14af2b85199e3d4fc7d1e778dd53130d180f5077e2d1c7a001148b495d6e859114e670ca54fb6e2657f0cbae5b08063605093a4b3dc9f8f1a0011ac212f13c5dff2b2c6b600a79635103d6f580a4221079951181b25c7e654901a0c8de4cced43169f9aa3d36506363b2d2c44f6c49fc1fd91ea114c86f3757077ea01e11fdd0d1934eda0492606ee0bb80a7bf8f35cc5f86ec60fe5031ba48bfd544").unwrap(); - let decoded = TransactionSigned::decode_2718(&mut raw_tx.as_slice()).unwrap(); - assert!(alloy_consensus::Typed2718::is_eip4844(&decoded)); - - let from = decoded.recover_signer(); - assert_eq!(from, Some(address!("A83C816D4f9b2783761a22BA6FADB0eB0606D7B2"))); - - let tx = decoded.transaction; - - assert_eq!(tx.to(), Some(address!("11E9CA82A3a762b4B5bd264d4173a242e7a77064"))); - - assert_eq!( - tx.blob_versioned_hashes(), - Some( - &[ - b256!("012ec3d6f66766bedb002a190126b3549fce0047de0d4c25cffce0dc1c57921a"), - b256!("0152d8e24762ff22b1cfd9f8c0683786a7ca63ba49973818b3d1e9512cd2cec4"), - b256!("013b98c6c83e066d5b14af2b85199e3d4fc7d1e778dd53130d180f5077e2d1c7"), - b256!("01148b495d6e859114e670ca54fb6e2657f0cbae5b08063605093a4b3dc9f8f1"), - b256!("011ac212f13c5dff2b2c6b600a79635103d6f580a4221079951181b25c7e6549"), - ][..] - ) - ); - } - - #[test] - fn decode_transaction_consumes_buffer() { - let bytes = &mut &hex!("b87502f872041a8459682f008459682f0d8252089461815774383099e24810ab832a5b2a5425c154d58829a2241af62c000080c001a059e6b67f48fb32e7e570dfb11e042b5ad2e55e3ce3ce9cd989c7e06e07feeafda0016b83f4f980694ed2eee4d10667242b1f40dc406901b34125b008d334d47469")[..]; - let _transaction_res = TransactionSigned::decode(bytes).unwrap(); - assert_eq!( - bytes.len(), - 0, - "did not consume all bytes in the buffer, {:?} remaining", - bytes.len() - ); - } - - #[test] - fn decode_multiple_network_txs() { - let bytes = hex!("f86b02843b9aca00830186a094d3e8763675e4c425df46cc3b5c0f6cbdac39604687038d7ea4c68000802ba00eb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5aea03a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca18"); - let transaction = Transaction::Legacy(TxLegacy { - chain_id: Some(4u64), - nonce: 2, - gas_price: 1000000000, - gas_limit: 100000, - to: Address::from_str("d3e8763675e4c425df46cc3b5c0f6cbdac396046").unwrap().into(), - value: U256::from(1000000000000000u64), - input: Bytes::default(), - }); - let signature = Signature::new( - U256::from_str("0xeb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5ae") - .unwrap(), - U256::from_str("0x3a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca18") - .unwrap(), - false, - ); - let hash = b256!("a517b206d2223278f860ea017d3626cacad4f52ff51030dc9a96b432f17f8d34"); - test_decode_and_encode(&bytes, transaction, signature, Some(hash)); - - let bytes = hex!("f86b01843b9aca00830186a094d3e8763675e4c425df46cc3b5c0f6cbdac3960468702769bb01b2a00802ba0e24d8bd32ad906d6f8b8d7741e08d1959df021698b19ee232feba15361587d0aa05406ad177223213df262cb66ccbb2f46bfdccfdfbbb5ffdda9e2c02d977631da"); - let transaction = Transaction::Legacy(TxLegacy { - chain_id: Some(4), - nonce: 1u64, - gas_price: 1000000000, - gas_limit: 100000, - to: Address::from_slice(&hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046")[..]).into(), - value: U256::from(693361000000000u64), - input: Default::default(), - }); - let signature = Signature::new( - U256::from_str("0xe24d8bd32ad906d6f8b8d7741e08d1959df021698b19ee232feba15361587d0a") - .unwrap(), - U256::from_str("0x5406ad177223213df262cb66ccbb2f46bfdccfdfbbb5ffdda9e2c02d977631da") - .unwrap(), - false, - ); - test_decode_and_encode(&bytes, transaction, signature, None); - - let bytes = hex!("f86b0384773594008398968094d3e8763675e4c425df46cc3b5c0f6cbdac39604687038d7ea4c68000802ba0ce6834447c0a4193c40382e6c57ae33b241379c5418caac9cdc18d786fd12071a03ca3ae86580e94550d7c071e3a02eadb5a77830947c9225165cf9100901bee88"); - let transaction = Transaction::Legacy(TxLegacy { - chain_id: Some(4), - nonce: 3, - gas_price: 2000000000, - gas_limit: 10000000, - to: Address::from_slice(&hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046")[..]).into(), - value: U256::from(1000000000000000u64), - input: Bytes::default(), - }); - let signature = Signature::new( - U256::from_str("0xce6834447c0a4193c40382e6c57ae33b241379c5418caac9cdc18d786fd12071") - .unwrap(), - U256::from_str("0x3ca3ae86580e94550d7c071e3a02eadb5a77830947c9225165cf9100901bee88") - .unwrap(), - false, - ); - test_decode_and_encode(&bytes, transaction, signature, None); - - let bytes = hex!("b87502f872041a8459682f008459682f0d8252089461815774383099e24810ab832a5b2a5425c154d58829a2241af62c000080c001a059e6b67f48fb32e7e570dfb11e042b5ad2e55e3ce3ce9cd989c7e06e07feeafda0016b83f4f980694ed2eee4d10667242b1f40dc406901b34125b008d334d47469"); - let transaction = Transaction::Eip1559(TxEip1559 { - chain_id: 4, - nonce: 26, - max_priority_fee_per_gas: 1500000000, - max_fee_per_gas: 1500000013, - gas_limit: MIN_TRANSACTION_GAS, - to: Address::from_slice(&hex!("61815774383099e24810ab832a5b2a5425c154d5")[..]).into(), - value: U256::from(3000000000000000000u64), - input: Default::default(), - access_list: Default::default(), - }); - let signature = Signature::new( - U256::from_str("0x59e6b67f48fb32e7e570dfb11e042b5ad2e55e3ce3ce9cd989c7e06e07feeafd") - .unwrap(), - U256::from_str("0x016b83f4f980694ed2eee4d10667242b1f40dc406901b34125b008d334d47469") - .unwrap(), - true, - ); - test_decode_and_encode(&bytes, transaction, signature, None); - - let bytes = hex!("f8650f84832156008287fb94cf7f9e66af820a19257a2108375b180b0ec491678204d2802ca035b7bfeb9ad9ece2cbafaaf8e202e706b4cfaeb233f46198f00b44d4a566a981a0612638fb29427ca33b9a3be2a0a561beecfe0269655be160d35e72d366a6a860"); - let transaction = Transaction::Legacy(TxLegacy { - chain_id: Some(4), - nonce: 15, - gas_price: 2200000000, - gas_limit: 34811, - to: Address::from_slice(&hex!("cf7f9e66af820a19257a2108375b180b0ec49167")[..]).into(), - value: U256::from(1234), - input: Bytes::default(), - }); - let signature = Signature::new( - U256::from_str("0x35b7bfeb9ad9ece2cbafaaf8e202e706b4cfaeb233f46198f00b44d4a566a981") - .unwrap(), - U256::from_str("0x612638fb29427ca33b9a3be2a0a561beecfe0269655be160d35e72d366a6a860") - .unwrap(), - true, - ); - test_decode_and_encode(&bytes, transaction, signature, None); - } - - fn test_decode_and_encode( - bytes: &[u8], - transaction: Transaction, - signature: Signature, - hash: Option, - ) { - let expected = TransactionSigned::new_unhashed(transaction, signature); - if let Some(hash) = hash { - assert_eq!(hash, *expected.tx_hash()); - } - assert_eq!(bytes.len(), expected.length()); - - let decoded = TransactionSigned::decode(&mut &bytes[..]).unwrap(); - assert_eq!(expected, decoded); - assert_eq!(bytes, &alloy_rlp::encode(expected)); - } - - #[test] - fn decode_raw_tx_and_recover_signer() { - use alloy_primitives::hex_literal::hex; - // transaction is from ropsten - - let hash: B256 = - hex!("559fb34c4a7f115db26cbf8505389475caaab3df45f5c7a0faa4abfa3835306c").into(); - let signer: Address = hex!("641c5d790f862a58ec7abcfd644c0442e9c201b3").into(); - let raw = hex!("f88b8212b085028fa6ae00830f424094aad593da0c8116ef7d2d594dd6a63241bccfc26c80a48318b64b000000000000000000000000641c5d790f862a58ec7abcfd644c0442e9c201b32aa0a6ef9e170bca5ffb7ac05433b13b7043de667fbb0b4a5e45d3b54fb2d6efcc63a0037ec2c05c3d60c5f5f78244ce0a3859e3a18a36c61efb061b383507d3ce19d2"); - - let mut pointer = raw.as_ref(); - let tx = TransactionSigned::decode(&mut pointer).unwrap(); - assert_eq!(*tx.tx_hash(), hash, "Expected same hash"); - assert_eq!(tx.recover_signer(), Some(signer), "Recovering signer should pass."); - } - - #[test] - fn test_envelop_encode() { - // random tx: - let input = hex!("02f871018302a90f808504890aef60826b6c94ddf4c5025d1a5742cf12f74eec246d4432c295e487e09c3bbcc12b2b80c080a0f21a4eacd0bf8fea9c5105c543be5a1d8c796516875710fafafdf16d16d8ee23a001280915021bb446d1973501a67f93d2b38894a514b976e7b46dc2fe54598d76"); - let decoded = TransactionSigned::decode(&mut &input[..]).unwrap(); - - let encoded = decoded.encoded_2718(); - assert_eq!(encoded[..], input); - } - - #[test] - fn test_envelop_decode() { - // random tx: - let input = bytes!("02f871018302a90f808504890aef60826b6c94ddf4c5025d1a5742cf12f74eec246d4432c295e487e09c3bbcc12b2b80c080a0f21a4eacd0bf8fea9c5105c543be5a1d8c796516875710fafafdf16d16d8ee23a001280915021bb446d1973501a67f93d2b38894a514b976e7b46dc2fe54598d76"); - let decoded = TransactionSigned::decode_2718(&mut input.as_ref()).unwrap(); - - let encoded = decoded.encoded_2718(); - assert_eq!(encoded, input); - } - - #[test] - fn test_decode_tx() { - // some random transactions pulled from hive tests - let data = hex!("b86f02f86c0705843b9aca008506fc23ac00830124f89400000000000000000000000000000000000003160180c001a00293c713e2f1eab91c366621ff2f867e05ad7e99d4aa5d069aafeb9e1e8c9b6aa05ec6c0605ff20b57c90a6484ec3b0509e5923733d06f9b69bee9a2dabe4f1352"); - let tx = TransactionSigned::decode(&mut data.as_slice()).unwrap(); - let mut b = Vec::with_capacity(data.len()); - tx.encode(&mut b); - assert_eq!(data.as_slice(), b.as_slice()); - - let data = hex!("f865048506fc23ac00830124f8940000000000000000000000000000000000000316018032a06b8fdfdcb84790816b7af85b19305f493665fe8b4e7c51ffdd7cc144cd776a60a028a09ab55def7b8d6602ba1c97a0ebbafe64ffc9c8e89520cec97a8edfb2ebe9"); - let tx = TransactionSigned::decode(&mut data.as_slice()).unwrap(); - let mut b = Vec::with_capacity(data.len()); - tx.encode(&mut b); - assert_eq!(data.as_slice(), b.as_slice()); - } - - // - #[test] - fn recover_legacy_singer() { - let data = hex!("f9015482078b8505d21dba0083022ef1947a250d5630b4cf539739df2c5dacb4c659f2488d880c46549a521b13d8b8e47ff36ab50000000000000000000000000000000000000000000066ab5a608bd00a23f2fe000000000000000000000000000000000000000000000000000000000000008000000000000000000000000048c04ed5691981c42154c6167398f95e8f38a7ff00000000000000000000000000000000000000000000000000000000632ceac70000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006c6ee5e31d828de241282b9606c8e98ea48526e225a0c9077369501641a92ef7399ff81c21639ed4fd8fc69cb793cfa1dbfab342e10aa0615facb2f1bcf3274a354cfe384a38d0cc008a11c2dd23a69111bc6930ba27a8"); - let tx = TransactionSigned::fallback_decode(&mut data.as_slice()).unwrap(); - assert!(tx.is_legacy()); - let sender = tx.recover_signer().unwrap(); - assert_eq!(sender, address!("a12e1462d0ceD572f396F58B6E2D03894cD7C8a4")); - } - - // - // - #[test] - fn recover_enveloped() { - let data = hex!("02f86f0102843b9aca0085029e7822d68298f094d9e1459a7a482635700cbc20bbaf52d495ab9c9680841b55ba3ac080a0c199674fcb29f353693dd779c017823b954b3c69dffa3cd6b2a6ff7888798039a028ca912de909e7e6cdef9cdcaf24c54dd8c1032946dfa1d85c206b32a9064fe8"); - let tx = TransactionSigned::decode_2718(&mut data.as_slice()).unwrap(); - let sender = tx.recover_signer().unwrap(); - assert_eq!(sender, address!("001e2b7dE757bA469a57bF6b23d982458a07eFcE")); - assert_eq!(tx.to(), Some(address!("D9e1459A7A482635700cBc20BBAF52D495Ab9C96"))); - assert_eq!(tx.input().as_ref(), hex!("1b55ba3a")); - let encoded = tx.encoded_2718(); - assert_eq!(encoded.as_ref(), data.to_vec()); - } - - // - // - #[test] - fn recover_pre_eip2() { - let data = hex!("f8ea0c850ba43b7400832dc6c0942935aa0a2d2fbb791622c29eb1c117b65b7a908580b884590528a9000000000000000000000001878ace42092b7f1ae1f28d16c1272b1aa80ca4670000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000d02ab486cedc0000000000000000000000000000000000000000000000000000557fe293cabc08cf1ca05bfaf3fda0a56b49cc78b22125feb5ae6a99d2b4781f00507d8b02c173771c85a0b5da0dbe6c5bc53740d0071fc83eb17ba0f709e49e9ae7df60dee625ef51afc5"); - let tx = TransactionSigned::decode_2718(&mut data.as_slice()).unwrap(); - let sender = tx.recover_signer(); - assert!(sender.is_none()); - let sender = tx.recover_signer_unchecked().unwrap(); - - assert_eq!(sender, address!("7e9e359edf0dbacf96a9952fa63092d919b0842b")); - } - - #[test] - fn transaction_signed_no_hash_zstd_codec() { - // will use same signature everywhere. - // We don't need signature to match tx, just decoded to the same signature - let signature = Signature::new( - U256::from_str("0xeb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5ae") - .unwrap(), - U256::from_str("0x3a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca18") - .unwrap(), - false, - ); - - let inputs: Vec> = vec![ - vec![], - vec![0], - vec![255], - vec![1u8; 31], - vec![255u8; 31], - vec![1u8; 32], - vec![255u8; 32], - vec![1u8; 64], - vec![255u8; 64], - ]; - - for input in inputs { - let transaction = Transaction::Legacy(TxLegacy { - chain_id: Some(4u64), - nonce: 2, - gas_price: 1000000000, - gas_limit: 100000, - to: Address::from_str("d3e8763675e4c425df46cc3b5c0f6cbdac396046").unwrap().into(), - value: U256::from(1000000000000000u64), - input: Bytes::from(input), - }); - - let tx = TransactionSigned::new_unhashed(transaction, signature); - test_transaction_signed_to_from_compact(tx); - } - } - - fn test_transaction_signed_to_from_compact(tx: TransactionSigned) { - // zstd aware `to_compact` - let mut buff: Vec = Vec::new(); - let written_bytes = tx.to_compact(&mut buff); - let (decoded, _) = TransactionSigned::from_compact(&buff, written_bytes); - assert_eq!(tx, decoded); - } - - #[test] - fn create_txs_disallowed_for_eip4844() { - let data = - [3, 208, 128, 128, 123, 128, 120, 128, 129, 129, 128, 192, 129, 129, 192, 128, 128, 9]; - let res = TransactionSigned::decode_2718(&mut &data[..]); - - assert!(res.is_err()); - } -} +#[deprecated(note = "Use `Recovered` instead")] +pub type TransactionSignedEcRecovered = Recovered; diff --git a/crates/primitives/src/transaction/pooled.rs b/crates/primitives/src/transaction/pooled.rs index 4e483f1d0c14..43a1a70e001f 100644 --- a/crates/primitives/src/transaction/pooled.rs +++ b/crates/primitives/src/transaction/pooled.rs @@ -1,99 +1,9 @@ //! Defines the types for blob transactions, legacy, and other EIP-2718 transactions included in a //! response to `GetPooledTransactions`. -use crate::RecoveredTx; +use crate::Recovered; use alloy_consensus::transaction::PooledTransaction; /// A signed pooled transaction with recovered signer. -pub type PooledTransactionsElementEcRecovered = RecoveredTx; - -#[cfg(test)] -mod tests { - use super::*; - use alloy_consensus::{transaction::RlpEcdsaTx, Transaction as _, TxLegacy}; - use alloy_eips::eip2718::Decodable2718; - use alloy_primitives::{address, hex, Bytes}; - use alloy_rlp::Decodable; - use assert_matches::assert_matches; - - #[test] - fn invalid_legacy_pooled_decoding_input_too_short() { - let input_too_short = [ - // this should fail because the payload length is longer than expected - &hex!("d90b0280808bc5cd028083c5cdfd9e407c56565656")[..], - // these should fail decoding - // - // The `c1` at the beginning is a list header, and the rest is a valid legacy - // transaction, BUT the payload length of the list header is 1, and the payload is - // obviously longer than one byte. - &hex!("c10b02808083c5cd028883c5cdfd9e407c56565656"), - &hex!("c10b0280808bc5cd028083c5cdfd9e407c56565656"), - // this one is 19 bytes, and the buf is long enough, but the transaction will not - // consume that many bytes. - &hex!("d40b02808083c5cdeb8783c5acfd9e407c5656565656"), - &hex!("d30102808083c5cd02887dc5cdfd9e64fd9e407c56"), - ]; - - for hex_data in &input_too_short { - let input_rlp = &mut &hex_data[..]; - let res = PooledTransaction::decode(input_rlp); - - assert!( - res.is_err(), - "expected err after decoding rlp input: {:x?}", - Bytes::copy_from_slice(hex_data) - ); - - // this is a legacy tx so we can attempt the same test with decode_enveloped - let input_rlp = &mut &hex_data[..]; - let res = PooledTransaction::decode_2718(input_rlp); - - assert!( - res.is_err(), - "expected err after decoding enveloped rlp input: {:x?}", - Bytes::copy_from_slice(hex_data) - ); - } - } - - // - #[test] - fn decode_eip1559_enveloped() { - let data = hex!("02f903d382426882ba09832dc6c0848674742682ed9694714b6a4ea9b94a8a7d9fd362ed72630688c8898c80b90364492d24749189822d8512430d3f3ff7a2ede675ac08265c08e2c56ff6fdaa66dae1cdbe4a5d1d7809f3e99272d067364e597542ac0c369d69e22a6399c3e9bee5da4b07e3f3fdc34c32c3d88aa2268785f3e3f8086df0934b10ef92cfffc2e7f3d90f5e83302e31382e302d64657600000000000000000000000000000000000000000000569e75fc77c1a856f6daaf9e69d8a9566ca34aa47f9133711ce065a571af0cfd000000000000000000000000e1e210594771824dad216568b91c9cb4ceed361c00000000000000000000000000000000000000000000000000000000000546e00000000000000000000000000000000000000000000000000000000000e4e1c00000000000000000000000000000000000000000000000000000000065d6750c00000000000000000000000000000000000000000000000000000000000f288000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002cf600000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000000f1628e56fa6d8c50e5b984a58c0df14de31c7b857ce7ba499945b99252976a93d06dcda6776fc42167fbe71cb59f978f5ef5b12577a90b132d14d9c6efa528076f0161d7bf03643cfc5490ec5084f4a041db7f06c50bd97efa08907ba79ddcac8b890f24d12d8db31abbaaf18985d54f400449ee0559a4452afe53de5853ce090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000064ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000c080a01428023fc54a27544abc421d5d017b9a7c5936ad501cbdecd0d9d12d04c1a033a0753104bbf1c87634d6ff3f0ffa0982710612306003eb022363b57994bdef445a" -); - - let res = PooledTransaction::decode_2718(&mut &data[..]).unwrap(); - assert_eq!(res.to(), Some(address!("714b6a4ea9b94a8a7d9fd362ed72630688c8898c"))); - } - - #[test] - fn legacy_valid_pooled_decoding() { - // d3 <- payload length, d3 - c0 = 0x13 = 19 - // 0b <- nonce - // 02 <- gas_price - // 80 <- gas_limit - // 80 <- to (Create) - // 83 c5cdeb <- value - // 87 83c5acfd9e407c <- input - // 56 <- v (eip155, so modified with a chain id) - // 56 <- r - // 56 <- s - let data = &hex!("d30b02808083c5cdeb8783c5acfd9e407c565656")[..]; - - let input_rlp = &mut &data[..]; - let res = PooledTransaction::decode(input_rlp); - assert_matches!(res, Ok(_tx)); - assert!(input_rlp.is_empty()); - - // this is a legacy tx so we can attempt the same test with - // decode_rlp_legacy_transaction_tuple - let input_rlp = &mut &data[..]; - let res = TxLegacy::rlp_decode_signed(input_rlp); - assert_matches!(res, Ok(_tx)); - assert!(input_rlp.is_empty()); - - // we can also decode_enveloped - let res = PooledTransaction::decode_2718(&mut &data[..]); - assert_matches!(res, Ok(_tx)); - } -} +#[deprecated(note = "use `Recovered` instead")] +pub type PooledTransactionsElementEcRecovered = Recovered; diff --git a/crates/prune/types/src/segment.rs b/crates/prune/types/src/segment.rs index 6d44f88a881e..e0b73aab7a4d 100644 --- a/crates/prune/types/src/segment.rs +++ b/crates/prune/types/src/segment.rs @@ -42,7 +42,7 @@ pub enum PruneSegment { } impl PruneSegment { - /// Returns minimum number of blocks to left in the database for this segment. + /// Returns minimum number of blocks to keep in the database for this segment. pub const fn min_blocks(&self, purpose: PrunePurpose) -> u64 { match self { Self::SenderRecovery | Self::TransactionLookup | Self::Headers | Self::Transactions => { @@ -84,9 +84,6 @@ pub enum PruneSegmentError { /// Invalid configuration of a prune segment. #[error("the configuration provided for {0} is invalid")] Configuration(PruneSegment), - /// Receipts have been pruned - #[error("receipts have been pruned")] - ReceiptsPruned, } #[cfg(test)] diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index cc1c8edcb8db..6501538d94f0 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -15,11 +15,9 @@ workspace = true # reth reth-primitives.workspace = true reth-storage-errors.workspace = true -reth-execution-errors.workspace = true reth-prune-types.workspace = true reth-storage-api.workspace = true reth-trie = { workspace = true, optional = true } -reth-primitives-traits.workspace = true # alloy alloy-eips.workspace = true @@ -42,7 +40,6 @@ std = [ "revm/std", "alloy-eips/std", "alloy-consensus/std", - "reth-primitives-traits/std", "reth-ethereum-forks/std" ] witness = ["dep:reth-trie"] @@ -52,14 +49,12 @@ test-utils = [ "reth-trie?/test-utils", "revm/test-utils", "reth-prune-types/test-utils", - "reth-primitives-traits/test-utils", ] serde = [ "revm/serde", "alloy-eips/serde", "alloy-primitives/serde", "alloy-consensus/serde", - "reth-primitives-traits/serde", "reth-trie?/serde", "reth-ethereum-forks/serde" ] diff --git a/crates/revm/src/batch.rs b/crates/revm/src/batch.rs index c980bdc987c3..e775ebe8a1cc 100644 --- a/crates/revm/src/batch.rs +++ b/crates/revm/src/batch.rs @@ -3,11 +3,9 @@ use alloc::vec::Vec; use alloy_eips::eip7685::Requests; -use alloy_primitives::{map::HashSet, Address, BlockNumber, Log}; -use reth_execution_errors::{BlockExecutionError, InternalBlockExecutionError}; +use alloy_primitives::BlockNumber; use reth_primitives::Receipts; -use reth_primitives_traits::Receipt; -use reth_prune_types::{PruneMode, PruneModes, PruneSegmentError, MINIMUM_PRUNING_DISTANCE}; +use reth_prune_types::PruneModes; use revm::db::states::bundle_state::BundleRetention; /// Takes care of: @@ -31,11 +29,6 @@ pub struct BlockBatchRecord { /// A transaction may have zero or more requests, so the length of the inner vector is not /// guaranteed to be the same as the number of transactions. requests: Vec, - /// Memoized address pruning filter. - /// - /// Empty implies that there is going to be addresses to include in the filter in a future - /// block. None means there isn't any kind of configuration. - pruning_address_filter: Option<(u64, HashSet
)>, /// First block will be initialized to `None` /// and be set to the block number of first block executed. first_block: Option, @@ -49,7 +42,6 @@ impl Default for BlockBatchRecord { prune_modes: Default::default(), receipts: Default::default(), requests: Default::default(), - pruning_address_filter: Default::default(), first_block: Default::default(), tip: Default::default(), } @@ -58,10 +50,7 @@ impl Default for BlockBatchRecord { impl BlockBatchRecord { /// Create a new receipts recorder with the given pruning configuration. - pub fn new(prune_modes: PruneModes) -> Self - where - T: Default, - { + pub fn new(prune_modes: PruneModes) -> Self { Self { prune_modes, ..Default::default() } } @@ -129,66 +118,8 @@ impl BlockBatchRecord { } /// Save receipts to the executor. - pub fn save_receipts(&mut self, receipts: Vec) -> Result<(), BlockExecutionError> - where - T: Receipt, - { - let mut receipts = receipts.into_iter().map(Some).collect(); - // Prune receipts if necessary. - self.prune_receipts(&mut receipts).map_err(InternalBlockExecutionError::from)?; - // Save receipts. + pub fn save_receipts(&mut self, receipts: Vec) { self.receipts.push(receipts); - Ok(()) - } - - /// Prune receipts according to the pruning configuration. - fn prune_receipts(&mut self, receipts: &mut Vec>) -> Result<(), PruneSegmentError> - where - T: Receipt, - { - let (Some(first_block), Some(tip)) = (self.first_block, self.tip) else { return Ok(()) }; - - let block_number = first_block + self.receipts.len() as u64; - - // Block receipts should not be retained - if self.prune_modes.receipts == Some(PruneMode::Full) || - // [`PruneSegment::Receipts`] takes priority over [`PruneSegment::ContractLogs`] - self.prune_modes.receipts.is_some_and(|mode| mode.should_prune(block_number, tip)) - { - receipts.clear(); - return Ok(()) - } - - // All receipts from the last 128 blocks are required for blockchain tree, even with - // [`PruneSegment::ContractLogs`]. - let prunable_receipts = - PruneMode::Distance(MINIMUM_PRUNING_DISTANCE).should_prune(block_number, tip); - if !prunable_receipts { - return Ok(()) - } - - let contract_log_pruner = self.prune_modes.receipts_log_filter.group_by_block(tip, None)?; - - if !contract_log_pruner.is_empty() { - let (prev_block, filter) = - self.pruning_address_filter.get_or_insert_with(|| (0, Default::default())); - for (_, addresses) in contract_log_pruner.range(*prev_block..=block_number) { - filter.extend(addresses.iter().copied()); - } - } - - if let Some((_, filter)) = &self.pruning_address_filter { - for receipt in receipts.iter_mut() { - // If there is an address_filter, it does not contain any of the - // contract addresses, then remove this receipt. - let inner_receipt = receipt.as_ref().expect("receipts have not been pruned"); - if !inner_receipt.logs().iter().any(|log| filter.contains(&log.address)) { - receipt.take(); - } - } - } - - Ok(()) } /// Save EIP-7685 requests to the executor. @@ -200,10 +131,6 @@ impl BlockBatchRecord { #[cfg(test)] mod tests { use super::*; - use alloc::collections::BTreeMap; - use alloy_primitives::Address; - use reth_primitives::{Log, Receipt}; - use reth_prune_types::{PruneMode, ReceiptsLogPruneConfig}; #[test] fn test_save_receipts_empty() { @@ -211,175 +138,8 @@ mod tests { // Create an empty vector of receipts let receipts = vec![]; - // Verify that saving receipts completes without error - assert!(recorder.save_receipts(receipts).is_ok()); + recorder.save_receipts(receipts); // Verify that the saved receipts are equal to a nested empty vector assert_eq!(*recorder.receipts(), vec![vec![]].into()); } - - #[test] - fn test_save_receipts_non_empty_no_pruning() { - let mut recorder = BlockBatchRecord::default(); - let receipts = vec![Receipt::default()]; - - // Verify that saving receipts completes without error - assert!(recorder.save_receipts(receipts).is_ok()); - // Verify that there is one block of receipts - assert_eq!(recorder.receipts().len(), 1); - // Verify that the first block contains one receipt - assert_eq!(recorder.receipts()[0].len(), 1); - // Verify that the saved receipt is the default receipt - assert_eq!(recorder.receipts()[0][0], Some(Receipt::default())); - } - - #[test] - fn test_save_receipts_with_pruning_no_prunable_receipts() { - let mut recorder = BlockBatchRecord::default(); - - // Set the first block number - recorder.set_first_block(1); - // Set the tip (highest known block) - recorder.set_tip(130); - - // Create a vector of receipts with a default receipt - let receipts = vec![Receipt::default()]; - - // Verify that saving receipts completes without error - assert!(recorder.save_receipts(receipts).is_ok()); - // Verify that there is one block of receipts - assert_eq!(recorder.receipts().len(), 1); - // Verify that the first block contains one receipt - assert_eq!(recorder.receipts()[0].len(), 1); - // Verify that the saved receipt is the default receipt - assert_eq!(recorder.receipts()[0][0], Some(Receipt::default())); - } - - #[test] - fn test_save_receipts_with_pruning_no_tip() { - // Create a PruneModes with receipts set to PruneMode::Full - let prune_modes = PruneModes { receipts: Some(PruneMode::Full), ..Default::default() }; - - let mut recorder = BlockBatchRecord::new(prune_modes); - - // Set the first block number - recorder.set_first_block(1); - // Create a vector of receipts with a default receipt - let receipts = vec![Receipt::default()]; - - // Verify that saving receipts completes without error - assert!(recorder.save_receipts(receipts).is_ok()); - // Verify that there is one block of receipts - assert_eq!(recorder.receipts().len(), 1); - // Verify that the first block contains one receipt - assert_eq!(recorder.receipts()[0].len(), 1); - // Verify that the saved receipt is the default receipt - assert_eq!(recorder.receipts()[0][0], Some(Receipt::default())); - } - - #[test] - fn test_save_receipts_with_pruning_no_block_number() { - // Create a PruneModes with receipts set to PruneMode::Full - let prune_modes = PruneModes { receipts: Some(PruneMode::Full), ..Default::default() }; - - // Create a BlockBatchRecord with the prune_modes - let mut recorder = BlockBatchRecord::new(prune_modes); - - // Set the tip (highest known block) - recorder.set_tip(130); - - // Create a vector of receipts with a default receipt - let receipts = vec![Receipt::default()]; - - // Verify that saving receipts completes without error - assert!(recorder.save_receipts(receipts).is_ok()); - // Verify that there is one block of receipts - assert_eq!(recorder.receipts().len(), 1); - // Verify that the first block contains one receipt - assert_eq!(recorder.receipts()[0].len(), 1); - // Verify that the saved receipt is the default receipt - assert_eq!(recorder.receipts()[0][0], Some(Receipt::default())); - } - - // Test saving receipts with pruning configuration and receipts should be pruned - #[test] - fn test_save_receipts_with_pruning_should_prune() { - // Create a PruneModes with receipts set to PruneMode::Full - let prune_modes = PruneModes { receipts: Some(PruneMode::Full), ..Default::default() }; - - // Create a BlockBatchRecord with the prune_modes - let mut recorder = BlockBatchRecord::new(prune_modes); - - // Set the first block number - recorder.set_first_block(1); - // Set the tip (highest known block) - recorder.set_tip(130); - - // Create a vector of receipts with a default receipt - let receipts = vec![Receipt::default()]; - - // Verify that saving receipts completes without error - assert!(recorder.save_receipts(receipts).is_ok()); - // Verify that there is one block of receipts - assert_eq!(recorder.receipts().len(), 1); - // Verify that the receipts are pruned (empty) - assert!(recorder.receipts()[0].is_empty()); - } - - // Test saving receipts with address filter pruning - #[test] - fn test_save_receipts_with_address_filter_pruning() { - // Create a PruneModes with receipts_log_filter configuration - let prune_modes = PruneModes { - receipts_log_filter: ReceiptsLogPruneConfig(BTreeMap::from([ - (Address::with_last_byte(1), PruneMode::Before(1300001)), - (Address::with_last_byte(2), PruneMode::Before(1300002)), - (Address::with_last_byte(3), PruneMode::Distance(1300003)), - ])), - ..Default::default() - }; - - // Create a BlockBatchRecord with the prune_modes - let mut recorder = BlockBatchRecord::new(prune_modes); - - // Set the first block number - recorder.set_first_block(1); - // Set the tip (highest known block) - recorder.set_tip(1300000); - - // With a receipt that should be pruned (address 4 not in the log filter) - let mut receipt = Receipt::default(); - receipt.logs.push(Log { address: Address::with_last_byte(4), ..Default::default() }); - let receipts = vec![receipt.clone()]; - assert!(recorder.save_receipts(receipts).is_ok()); - // Verify that the receipts are pruned (empty) - assert_eq!(recorder.receipts().len(), 1); - assert_eq!(recorder.receipts()[0], vec![None]); - - // With a receipt that should not be pruned (address 1 in the log filter) - let mut receipt1 = Receipt::default(); - receipt1.logs.push(Log { address: Address::with_last_byte(1), ..Default::default() }); - let receipts = vec![receipt1.clone()]; - assert!(recorder.save_receipts(receipts).is_ok()); - // Verify that the second block of receipts contains the receipt - assert_eq!(recorder.receipts().len(), 2); - assert_eq!(recorder.receipts()[1][0], Some(receipt1)); - - // With a receipt that should not be pruned (address 2 in the log filter) - let mut receipt2 = Receipt::default(); - receipt2.logs.push(Log { address: Address::with_last_byte(2), ..Default::default() }); - let receipts = vec![receipt2.clone()]; - assert!(recorder.save_receipts(receipts).is_ok()); - // Verify that the third block of receipts contains the receipt - assert_eq!(recorder.receipts().len(), 3); - assert_eq!(recorder.receipts()[2][0], Some(receipt2)); - - // With a receipt that should not be pruned (address 3 in the log filter) - let mut receipt3 = Receipt::default(); - receipt3.logs.push(Log { address: Address::with_last_byte(3), ..Default::default() }); - let receipts = vec![receipt3.clone()]; - assert!(recorder.save_receipts(receipts).is_ok()); - // Verify that the fourth block of receipts contains the receipt - assert_eq!(recorder.receipts().len(), 4); - assert_eq!(recorder.receipts()[3][0], Some(receipt3)); - } } diff --git a/crates/revm/src/database.rs b/crates/revm/src/database.rs index db4dd554f3b0..3c161b348589 100644 --- a/crates/revm/src/database.rs +++ b/crates/revm/src/database.rs @@ -2,6 +2,7 @@ use crate::primitives::alloy_primitives::{BlockNumber, StorageKey, StorageValue} use alloy_primitives::{Address, B256, U256}; use core::ops::{Deref, DerefMut}; use reth_primitives::Account; +use reth_storage_api::{AccountReader, BlockHashReader, StateProvider}; use reth_storage_errors::provider::{ProviderError, ProviderResult}; use revm::{ db::DatabaseRef, @@ -37,20 +38,20 @@ pub trait EvmStateProvider: Send + Sync { } // Blanket implementation of EvmStateProvider for any type that implements StateProvider. -impl EvmStateProvider for T { +impl EvmStateProvider for T { fn basic_account(&self, address: &Address) -> ProviderResult> { - ::basic_account(self, address) + ::basic_account(self, address) } fn block_hash(&self, number: BlockNumber) -> ProviderResult> { - ::block_hash(self, number) + ::block_hash(self, number) } fn bytecode_by_hash( &self, code_hash: &B256, ) -> ProviderResult> { - ::bytecode_by_hash(self, code_hash) + ::bytecode_by_hash(self, code_hash) } fn storage( @@ -58,7 +59,7 @@ impl EvmStateProvider for T { account: Address, storage_key: StorageKey, ) -> ProviderResult> { - ::storage(self, account, storage_key) + ::storage(self, account, storage_key) } } diff --git a/crates/rpc/rpc-builder/src/config.rs b/crates/rpc/rpc-builder/src/config.rs index 5ca03f770316..ccb822bf66f9 100644 --- a/crates/rpc/rpc-builder/src/config.rs +++ b/crates/rpc/rpc-builder/src/config.rs @@ -106,7 +106,10 @@ impl RethRpcServerConfig for RpcServerArgs { } fn flashbots_config(&self) -> ValidationApiConfig { - ValidationApiConfig { disallow: self.builder_disallow.clone().unwrap_or_default() } + ValidationApiConfig { + disallow: self.builder_disallow.clone().unwrap_or_default(), + validation_window: self.rpc_eth_proof_window, + } } fn state_cache_config(&self) -> EthStateCacheConfig { diff --git a/crates/rpc/rpc-builder/src/lib.rs b/crates/rpc/rpc-builder/src/lib.rs index e852c39f8fc6..354f4c7664bc 100644 --- a/crates/rpc/rpc-builder/src/lib.rs +++ b/crates/rpc/rpc-builder/src/lib.rs @@ -19,6 +19,7 @@ //! use reth_consensus::{ConsensusError, FullConsensus}; //! use reth_engine_primitives::PayloadValidator; //! use reth_evm::{execute::BlockExecutorProvider, ConfigureEvm}; +//! use reth_evm_ethereum::EthEvmConfig; //! use reth_network_api::{NetworkInfo, Peers}; //! use reth_primitives::{Header, PooledTransaction, TransactionSigned}; //! use reth_provider::{AccountReader, CanonStateSubscriptions, ChangeSetReader, FullRpcProvider}; @@ -30,21 +31,12 @@ //! use reth_transaction_pool::{PoolTransaction, TransactionPool}; //! use std::sync::Arc; //! -//! pub async fn launch< -//! Provider, -//! Pool, -//! Network, -//! Events, -//! EvmConfig, -//! BlockExecutor, -//! Consensus, -//! Validator, -//! >( +//! pub async fn launch( //! provider: Provider, //! pool: Pool, //! network: Network, //! events: Events, -//! evm_config: EvmConfig, +//! evm_config: EthEvmConfig, //! block_executor: BlockExecutor, //! consensus: Consensus, //! validator: Validator, @@ -66,7 +58,6 @@ //! Network: NetworkInfo + Peers + Clone + 'static, //! Events: //! CanonStateSubscriptions + Clone + 'static, -//! EvmConfig: ConfigureEvm
, //! BlockExecutor: BlockExecutorProvider, //! Consensus: FullConsensus + Clone + 'static, //! Validator: PayloadValidator, @@ -103,6 +94,7 @@ //! use reth_consensus::{ConsensusError, FullConsensus}; //! use reth_engine_primitives::{EngineTypes, PayloadValidator}; //! use reth_evm::{execute::BlockExecutorProvider, ConfigureEvm}; +//! use reth_evm_ethereum::EthEvmConfig; //! use reth_network_api::{NetworkInfo, Peers}; //! use reth_primitives::{Header, PooledTransaction, TransactionSigned}; //! use reth_provider::{AccountReader, CanonStateSubscriptions, ChangeSetReader, FullRpcProvider}; @@ -125,7 +117,6 @@ //! Events, //! EngineApi, //! EngineT, -//! EvmConfig, //! BlockExecutor, //! Consensus, //! Validator, @@ -135,7 +126,7 @@ //! network: Network, //! events: Events, //! engine_api: EngineApi, -//! evm_config: EvmConfig, +//! evm_config: EthEvmConfig, //! block_executor: BlockExecutor, //! consensus: Consensus, //! validator: Validator, @@ -159,7 +150,6 @@ //! CanonStateSubscriptions + Clone + 'static, //! EngineApi: EngineApiServer, //! EngineT: EngineTypes, -//! EvmConfig: ConfigureEvm
, //! BlockExecutor: BlockExecutorProvider, //! Consensus: FullConsensus + Clone + 'static, //! Validator: PayloadValidator, diff --git a/crates/rpc/rpc-engine-api/Cargo.toml b/crates/rpc/rpc-engine-api/Cargo.toml index 2b4560028db5..b358480e702f 100644 --- a/crates/rpc/rpc-engine-api/Cargo.toml +++ b/crates/rpc/rpc-engine-api/Cargo.toml @@ -49,6 +49,7 @@ parking_lot.workspace = true reth-ethereum-engine-primitives.workspace = true reth-provider = { workspace = true, features = ["test-utils"] } reth-primitives.workspace = true +reth-primitives-traits.workspace = true reth-payload-builder = { workspace = true, features = ["test-utils"] } reth-tokio-util.workspace = true reth-testing-utils.workspace = true diff --git a/crates/rpc/rpc-engine-api/src/engine_api.rs b/crates/rpc/rpc-engine-api/src/engine_api.rs index 50177199cb41..a092bdbdc705 100644 --- a/crates/rpc/rpc-engine-api/src/engine_api.rs +++ b/crates/rpc/rpc-engine-api/src/engine_api.rs @@ -20,8 +20,8 @@ use reth_chainspec::{EthereumHardfork, EthereumHardforks}; use reth_engine_primitives::{BeaconConsensusEngineHandle, EngineTypes, EngineValidator}; use reth_payload_builder::PayloadStore; use reth_payload_primitives::{ - validate_execution_requests, validate_payload_timestamp, EngineApiMessageVersion, - PayloadBuilderAttributes, PayloadOrAttributes, + validate_payload_timestamp, EngineApiMessageVersion, PayloadBuilderAttributes, + PayloadOrAttributes, }; use reth_rpc_api::EngineApiServer; use reth_rpc_types_compat::engine::payload::convert_to_payload_body_v1; @@ -268,8 +268,7 @@ where .validator .validate_version_specific_fields(EngineApiMessageVersion::V4, payload_or_attrs)?; - validate_execution_requests(&execution_requests)?; - + self.inner.validator.validate_execution_requests(&execution_requests)?; Ok(self .inner .beacon_consensus diff --git a/crates/rpc/rpc-engine-api/tests/it/payload.rs b/crates/rpc/rpc-engine-api/tests/it/payload.rs index e0debe679d7d..6edfeaf162b2 100644 --- a/crates/rpc/rpc-engine-api/tests/it/payload.rs +++ b/crates/rpc/rpc-engine-api/tests/it/payload.rs @@ -8,7 +8,8 @@ use alloy_rpc_types_engine::{ PayloadError, }; use assert_matches::assert_matches; -use reth_primitives::{proofs, Block, SealedBlock, SealedHeader, TransactionSigned}; +use reth_primitives::{Block, SealedBlock, SealedHeader, TransactionSigned}; +use reth_primitives_traits::proofs; use reth_rpc_types_compat::engine::payload::{block_to_payload, block_to_payload_v1}; use reth_testing_utils::generators::{ self, random_block, random_block_range, BlockParams, BlockRangeParams, Rng, diff --git a/crates/rpc/rpc-eth-api/src/helpers/call.rs b/crates/rpc/rpc-eth-api/src/helpers/call.rs index 538c98080149..49604c10481f 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/call.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/call.rs @@ -3,8 +3,8 @@ use super::{LoadBlock, LoadPendingBlock, LoadState, LoadTransaction, SpawnBlocking, Trace}; use crate::{ - helpers::estimate::EstimateCall, FromEthApiError, FromEvmError, FullEthApiTypes, - IntoEthApiError, RpcBlock, RpcNodeCore, + helpers::estimate::EstimateCall, FromEthApiError, FromEvmError, FullEthApiTypes, RpcBlock, + RpcNodeCore, }; use alloy_consensus::BlockHeader; use alloy_eips::{eip1559::calc_next_block_base_fee, eip2930::AccessListResult}; @@ -17,16 +17,14 @@ use alloy_rpc_types_eth::{ }; use futures::Future; use reth_chainspec::EthChainSpec; -use reth_evm::{env::EvmEnv, ConfigureEvm, ConfigureEvmEnv}; +use reth_evm::{env::EvmEnv, ConfigureEvm, ConfigureEvmEnv, Evm, TransactionEnv}; use reth_node_api::BlockBody; use reth_primitives_traits::SignedTransaction; use reth_provider::{BlockIdReader, ChainSpecProvider, ProviderHeader}; use reth_revm::{ database::StateProviderDatabase, db::CacheDB, - primitives::{ - BlockEnv, CfgEnvWithHandlerCfg, EnvWithHandlerCfg, ExecutionResult, ResultAndState, TxEnv, - }, + primitives::{BlockEnv, ExecutionResult, ResultAndState}, DatabaseRef, }; use reth_rpc_eth_types::{ @@ -34,14 +32,12 @@ use reth_rpc_eth_types::{ error::ensure_success, revm_utils::{ apply_block_overrides, apply_state_overrides, caller_gas_allowance, get_precompiles, - CallFees, }, simulate::{self, EthSimulateError}, EthApiError, RevertError, RpcInvalidTransactionError, StateCacheDb, }; use revm::{Database, DatabaseCommit, GetInspector}; use revm_inspectors::{access_list::AccessListInspector, transfer::TransferInspector}; -use revm_primitives::Env; use tracing::trace; /// Result type for `eth_simulateV1` RPC method. @@ -97,9 +93,9 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA let mut parent_hash = base_block.hash(); // Only enforce base fee if validation is enabled - evm_env.cfg_env_with_handler_cfg.disable_base_fee = !validation; + evm_env.cfg_env.disable_base_fee = !validation; // Always disable EIP-3607 - evm_env.cfg_env_with_handler_cfg.disable_eip3607 = true; + evm_env.cfg_env.disable_eip3607 = true; let this = self.clone(); self.spawn_with_state_at_block(block, move |state| { @@ -108,13 +104,13 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA let mut blocks: Vec>> = Vec::with_capacity(block_state_calls.len()); let mut block_state_calls = block_state_calls.into_iter().peekable(); + let chain_spec = RpcNodeCore::provider(&this).chain_spec(); while let Some(block) = block_state_calls.next() { // Increase number and timestamp for every new block evm_env.block_env.number += U256::from(1); evm_env.block_env.timestamp += U256::from(1); if validation { - let chain_spec = RpcNodeCore::provider(&this).chain_spec(); let base_fee_params = chain_spec .base_fee_params_at_timestamp(evm_env.block_env.timestamp.to()); let base_fee = if let Some(latest) = blocks.last() { @@ -182,7 +178,7 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA call, validation, default_gas_limit, - evm_env.cfg_env_with_handler_cfg.chain_id, + evm_env.cfg_env.chain_id, &mut db, this.tx_resp_builder(), )?; @@ -209,7 +205,7 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA } transactions.push(tx); - senders.push(tx_env.caller); + senders.push(tx_env.caller()); results.push(res.result); } @@ -391,7 +387,7 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA /// [`BlockId`]. fn create_access_list_with( &self, - mut evm_env: EvmEnv, + mut evm_env: EvmEnv<::Spec>, at: BlockId, mut request: TransactionRequest, ) -> Result @@ -404,19 +400,19 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA // we want to disable this in eth_createAccessList, since this is common practice used by // other node impls and providers - evm_env.cfg_env_with_handler_cfg.disable_block_gas_limit = true; + evm_env.cfg_env.disable_block_gas_limit = true; // The basefee should be ignored for eth_createAccessList // See: // - evm_env.cfg_env_with_handler_cfg.disable_base_fee = true; + evm_env.cfg_env.disable_base_fee = true; let mut db = CacheDB::new(StateProviderDatabase::new(state)); - if request.gas.is_none() && tx_env.gas_price > U256::ZERO { + if request.gas.is_none() && tx_env.gas_price() > U256::ZERO { let cap = caller_gas_allowance(&mut db, &tx_env)?; // no gas limit was provided in the request, so we need to cap the request's gas limit - tx_env.gas_limit = cap.min(evm_env.block_env.gas_limit).saturating_to(); + tx_env.set_gas_limit(cap.min(evm_env.block_env.gas_limit).saturating_to()); } let from = request.from.unwrap_or_default(); @@ -431,17 +427,17 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA // can consume the list since we're not using the request anymore let initial = request.access_list.take().unwrap_or_default(); - let precompiles = get_precompiles(evm_env.cfg_env_with_handler_cfg.handler_cfg.spec_id); + let precompiles = get_precompiles(evm_env.spec.into()); let mut inspector = AccessListInspector::new(initial, from, to, precompiles); let (result, (evm_env, mut tx_env)) = self.inspect(&mut db, evm_env, tx_env, &mut inspector)?; let access_list = inspector.into_access_list(); - tx_env.access_list = access_list.to_vec(); + tx_env.set_access_list(access_list.clone()); match result.result { ExecutionResult::Halt { reason, gas_used } => { let error = - Some(RpcInvalidTransactionError::halt(reason, tx_env.gas_limit).to_string()); + Some(RpcInvalidTransactionError::halt(reason, tx_env.gas_limit()).to_string()); return Ok(AccessListResult { access_list, gas_used: U256::from(gas_used), error }) } ExecutionResult::Revert { output, gas_used } => { @@ -456,7 +452,7 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA let res = match result.result { ExecutionResult::Halt { reason, gas_used } => { let error = - Some(RpcInvalidTransactionError::halt(reason, tx_env.gas_limit).to_string()); + Some(RpcInvalidTransactionError::halt(reason, tx_env.gas_limit()).to_string()); AccessListResult { access_list, gas_used: U256::from(gas_used), error } } ExecutionResult::Revert { output, gas_used } => { @@ -493,66 +489,76 @@ pub trait Call: f(StateProviderTraitObjWrapper(&state)) } - /// Executes the [`EnvWithHandlerCfg`] against the given [Database] without committing state + /// Executes the `TxEnv` against the given [Database] without committing state /// changes. + #[expect(clippy::type_complexity)] fn transact( &self, db: DB, - evm_env: EvmEnv, - tx_env: TxEnv, - ) -> Result<(ResultAndState, (EvmEnv, TxEnv)), Self::Error> + evm_env: EvmEnv<::Spec>, + tx_env: ::TxEnv, + ) -> Result< + ( + ResultAndState, + (EvmEnv<::Spec>, ::TxEnv), + ), + Self::Error, + > where DB: Database, EthApiError: From, { - let mut evm = self.evm_config().evm_with_env(db, evm_env, tx_env); - let res = evm.transact().map_err(Self::Error::from_evm_err)?; - let (_, env) = evm.into_db_and_env_with_handler_cfg(); - - let EnvWithHandlerCfg { env, handler_cfg } = env; - let Env { cfg, block, tx } = *env; - let evm_env = EvmEnv { - cfg_env_with_handler_cfg: CfgEnvWithHandlerCfg { cfg_env: cfg, handler_cfg }, - block_env: block, - }; + let mut evm = self.evm_config().evm_with_env(db, evm_env.clone()); + let res = evm.transact(tx_env.clone()).map_err(Self::Error::from_evm_err)?; - Ok((res, (evm_env, tx))) + Ok((res, (evm_env, tx_env))) } - /// Executes the [`EnvWithHandlerCfg`] against the given [Database] without committing state + /// Executes the [`EvmEnv`] against the given [Database] without committing state /// changes. + #[expect(clippy::type_complexity)] fn transact_with_inspector( &self, db: DB, - evm_env: EvmEnv, - tx_env: TxEnv, + evm_env: EvmEnv<::Spec>, + tx_env: ::TxEnv, inspector: impl GetInspector, - ) -> Result<(ResultAndState, (EvmEnv, TxEnv)), Self::Error> + ) -> Result< + ( + ResultAndState, + (EvmEnv<::Spec>, ::TxEnv), + ), + Self::Error, + > where DB: Database, EthApiError: From, { - let mut evm = self.evm_config().evm_with_env_and_inspector(db, evm_env, tx_env, inspector); - let res = evm.transact().map_err(Self::Error::from_evm_err)?; - let (_, env) = evm.into_db_and_env_with_handler_cfg(); - - let EnvWithHandlerCfg { env, handler_cfg } = env; - let Env { cfg, block, tx } = *env; - let evm_env = EvmEnv { - cfg_env_with_handler_cfg: CfgEnvWithHandlerCfg { cfg_env: cfg, handler_cfg }, - block_env: block, - }; + let mut evm = self.evm_config().evm_with_env_and_inspector(db, evm_env.clone(), inspector); + let res = evm.transact(tx_env.clone()).map_err(Self::Error::from_evm_err)?; - Ok((res, (evm_env, tx))) + Ok((res, (evm_env, tx_env))) } /// Executes the call request at the given [`BlockId`]. + #[expect(clippy::type_complexity)] fn transact_call_at( &self, request: TransactionRequest, at: BlockId, overrides: EvmOverrides, - ) -> impl Future> + Send + ) -> impl Future< + Output = Result< + ( + ResultAndState, + ( + EvmEnv<::Spec>, + ::TxEnv, + ), + ), + Self::Error, + >, + > + Send where Self: LoadPendingBlock, { @@ -581,7 +587,7 @@ pub trait Call: /// Prepares the state and env for the given [`TransactionRequest`] at the given [`BlockId`] and /// executes the closure on a new task returning the result of the closure. /// - /// This returns the configured [`EnvWithHandlerCfg`] for the given [`TransactionRequest`] at + /// This returns the configured [`EvmEnv`] for the given [`TransactionRequest`] at /// the given [`BlockId`] and with configured call settings: `prepare_call_env`. /// /// This is primarily used by `eth_call`. @@ -602,7 +608,11 @@ pub trait Call: ) -> impl Future> + Send where Self: LoadPendingBlock, - F: FnOnce(StateCacheDbRefMutWrapper<'_, '_>, EvmEnv, TxEnv) -> Result + F: FnOnce( + StateCacheDbRefMutWrapper<'_, '_>, + EvmEnv<::Spec>, + ::TxEnv, + ) -> Result + Send + 'static, R: Send + 'static, @@ -686,7 +696,7 @@ pub trait Call: fn replay_transactions_until<'a, DB, I>( &self, db: &mut DB, - evm_env: EvmEnv, + evm_env: EvmEnv<::Spec>, transactions: I, target_tx_hash: B256, ) -> Result @@ -696,7 +706,7 @@ pub trait Call: I: IntoIterator::Transaction)>, ::Transaction: SignedTransaction, { - let mut evm = self.evm_config().evm_with_env(db, evm_env, Default::default()); + let mut evm = self.evm_config().evm_with_env(db, evm_env); let mut index = 0; for (sender, tx) in transactions { if *tx.tx_hash() == target_tx_hash { @@ -704,93 +714,24 @@ pub trait Call: break } - self.evm_config().fill_tx_env(evm.tx_mut(), tx, *sender); - evm.transact_commit().map_err(Self::Error::from_evm_err)?; + let tx_env = self.evm_config().tx_env(tx, *sender); + evm.transact_commit(tx_env).map_err(Self::Error::from_evm_err)?; index += 1; } Ok(index) } - /// Configures a new [`TxEnv`] for the [`TransactionRequest`] + /// Configures a new `TxEnv` for the [`TransactionRequest`] /// - /// All [`TxEnv`] fields are derived from the given [`TransactionRequest`], if fields are + /// All `TxEnv` fields are derived from the given [`TransactionRequest`], if fields are /// `None`, they fall back to the [`BlockEnv`]'s settings. fn create_txn_env( &self, block_env: &BlockEnv, request: TransactionRequest, - ) -> Result { - // Ensure that if versioned hashes are set, they're not empty - if request.blob_versioned_hashes.as_ref().is_some_and(|hashes| hashes.is_empty()) { - return Err(RpcInvalidTransactionError::BlobTransactionMissingBlobHashes.into_eth_err()) - } - - let TransactionRequest { - from, - to, - gas_price, - max_fee_per_gas, - max_priority_fee_per_gas, - gas, - value, - input, - nonce, - access_list, - chain_id, - blob_versioned_hashes, - max_fee_per_blob_gas, - authorization_list, - transaction_type: _, - sidecar: _, - } = request; - - let CallFees { max_priority_fee_per_gas, gas_price, max_fee_per_blob_gas } = - CallFees::ensure_fees( - gas_price.map(U256::from), - max_fee_per_gas.map(U256::from), - max_priority_fee_per_gas.map(U256::from), - block_env.basefee, - blob_versioned_hashes.as_deref(), - max_fee_per_blob_gas.map(U256::from), - block_env.get_blob_gasprice().map(U256::from), - )?; - - let gas_limit = gas.unwrap_or_else(|| { - // Use maximum allowed gas limit. The reason for this - // is that both Erigon and Geth use pre-configured gas cap even if - // it's possible to derive the gas limit from the block: - // - block_env.gas_limit.saturating_to() - }); - - #[allow(clippy::needless_update)] - let env = TxEnv { - gas_limit, - nonce, - caller: from.unwrap_or_default(), - gas_price, - gas_priority_fee: max_priority_fee_per_gas, - transact_to: to.unwrap_or(TxKind::Create), - value: value.unwrap_or_default(), - data: input - .try_into_unique_input() - .map_err(Self::Error::from_eth_err)? - .unwrap_or_default(), - chain_id, - access_list: access_list.unwrap_or_default().into(), - // EIP-4844 fields - blob_hashes: blob_versioned_hashes.unwrap_or_default(), - max_fee_per_blob_gas, - // EIP-7702 fields - authorization_list: authorization_list.map(Into::into), - ..Default::default() - }; - - Ok(env) - } + ) -> Result<::TxEnv, Self::Error>; - /// Prepares the [`EnvWithHandlerCfg`] for execution of calls. + /// Prepares the [`EvmEnv`] for execution of calls. /// /// Does not commit any changes to the underlying database. /// @@ -803,13 +744,17 @@ pub trait Call: /// - `nonce` is set to `None` /// /// In addition, this changes the block's gas limit to the configured [`Self::call_gas_limit`]. + #[expect(clippy::type_complexity)] fn prepare_call_env( &self, - mut evm_env: EvmEnv, + mut evm_env: EvmEnv<::Spec>, mut request: TransactionRequest, db: &mut CacheDB, overrides: EvmOverrides, - ) -> Result<(EvmEnv, TxEnv), Self::Error> + ) -> Result< + (EvmEnv<::Spec>, ::TxEnv), + Self::Error, + > where DB: DatabaseRef, EthApiError: From<::Error>, @@ -826,12 +771,12 @@ pub trait Call: // Disabled because eth_call is sometimes used with eoa senders // See - evm_env.cfg_env_with_handler_cfg.disable_eip3607 = true; + evm_env.cfg_env.disable_eip3607 = true; // The basefee should be ignored for eth_call // See: // - evm_env.cfg_env_with_handler_cfg.disable_base_fee = true; + evm_env.cfg_env.disable_base_fee = true; // set nonce to None so that the correct nonce is chosen by the EVM request.nonce = None; @@ -848,12 +793,12 @@ pub trait Call: if request_gas.is_none() { // No gas limit was provided in the request, so we need to cap the transaction gas limit - if tx_env.gas_price > U256::ZERO { + if tx_env.gas_price() > U256::ZERO { // If gas price is specified, cap transaction gas limit with caller allowance trace!(target: "rpc::eth::call", ?tx_env, "Applying gas limit cap with caller allowance"); let cap = caller_gas_allowance(db, &tx_env)?; // ensure we cap gas_limit to the block's - tx_env.gas_limit = cap.min(evm_env.block_env.gas_limit).saturating_to(); + tx_env.set_gas_limit(cap.min(evm_env.block_env.gas_limit).saturating_to()); } } diff --git a/crates/rpc/rpc-eth-api/src/helpers/estimate.rs b/crates/rpc/rpc-eth-api/src/helpers/estimate.rs index f8fc02ce3625..b64edaf16e03 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/estimate.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/estimate.rs @@ -6,19 +6,19 @@ use alloy_primitives::U256; use alloy_rpc_types_eth::{state::StateOverride, transaction::TransactionRequest, BlockId}; use futures::Future; use reth_chainspec::MIN_TRANSACTION_GAS; -use reth_evm::env::EvmEnv; +use reth_evm::{env::EvmEnv, ConfigureEvmEnv, TransactionEnv}; use reth_provider::StateProvider; use reth_revm::{ database::StateProviderDatabase, db::CacheDB, - primitives::{ExecutionResult, HaltReason, TransactTo}, + primitives::{ExecutionResult, HaltReason}, }; use reth_rpc_eth_types::{ revm_utils::{apply_state_overrides, caller_gas_allowance}, EthApiError, RevertError, RpcInvalidTransactionError, }; use reth_rpc_server_types::constants::gas_oracle::{CALL_STIPEND_GAS, ESTIMATE_GAS_ERROR_RATIO}; -use revm_primitives::{db::Database, TxEnv}; +use revm_primitives::{db::Database, TxKind}; use tracing::trace; /// Gas execution estimates @@ -36,7 +36,7 @@ pub trait EstimateCall: Call { /// - `nonce` is set to `None` fn estimate_gas_with( &self, - mut evm_env: EvmEnv, + mut evm_env: EvmEnv<::Spec>, mut request: TransactionRequest, state: S, state_override: Option, @@ -46,12 +46,12 @@ pub trait EstimateCall: Call { { // Disabled because eth_estimateGas is sometimes used with eoa senders // See - evm_env.cfg_env_with_handler_cfg.disable_eip3607 = true; + evm_env.cfg_env.disable_eip3607 = true; // The basefee should be ignored for eth_estimateGas and similar // See: // - evm_env.cfg_env_with_handler_cfg.disable_base_fee = true; + evm_env.cfg_env.disable_base_fee = true; // set nonce to None so that the correct nonce is chosen by the EVM request.nonce = None; @@ -84,8 +84,8 @@ pub trait EstimateCall: Call { } // Optimize for simple transfer transactions, potentially reducing the gas estimate. - if tx_env.data.is_empty() { - if let TransactTo::Call(to) = tx_env.transact_to { + if tx_env.input().is_empty() { + if let TxKind::Call(to) = tx_env.kind() { if let Ok(code) = db.db.account_code(&to) { let no_code_callee = code.map(|code| code.is_empty()).unwrap_or(true); if no_code_callee { @@ -95,7 +95,7 @@ pub trait EstimateCall: Call { // field combos that bump the price up, so we try executing the function // with the minimum gas limit to make sure. let mut tx_env = tx_env.clone(); - tx_env.gas_limit = MIN_TRANSACTION_GAS; + tx_env.set_gas_limit(MIN_TRANSACTION_GAS); if let Ok((res, _)) = self.transact(&mut db, evm_env.clone(), tx_env) { if res.result.is_success() { return Ok(U256::from(MIN_TRANSACTION_GAS)) @@ -109,7 +109,7 @@ pub trait EstimateCall: Call { // Check funds of the sender (only useful to check if transaction gas price is more than 0). // // The caller allowance is check by doing `(account.balance - tx.value) / tx.gas_price` - if tx_env.gas_price > U256::ZERO { + if tx_env.gas_price() > U256::ZERO { // cap the highest gas limit by max gas caller can afford with given gas price highest_gas_limit = highest_gas_limit .min(caller_gas_allowance(&mut db, &tx_env).map_err(Self::Error::from_eth_err)?); @@ -119,7 +119,7 @@ pub trait EstimateCall: Call { let mut highest_gas_limit = highest_gas_limit.saturating_to::(); // If the provided gas limit is less than computed cap, use that - tx_env.gas_limit = tx_env.gas_limit.min(highest_gas_limit); + tx_env.set_gas_limit(tx_env.gas_limit().min(highest_gas_limit)); trace!(target: "rpc::eth::estimate", ?evm_env, ?tx_env, "Starting gas estimation"); @@ -169,7 +169,7 @@ pub trait EstimateCall: Call { // we know the tx succeeded with the configured gas limit, so we can use that as the // highest, in case we applied a gas cap due to caller allowance above - highest_gas_limit = tx_env.gas_limit; + highest_gas_limit = tx_env.gas_limit(); // NOTE: this is the gas the transaction used, which is less than the // transaction requires to succeed. @@ -186,7 +186,7 @@ pub trait EstimateCall: Call { let optimistic_gas_limit = (gas_used + gas_refund + CALL_STIPEND_GAS) * 64 / 63; if optimistic_gas_limit < highest_gas_limit { // Set the transaction's gas limit to the calculated optimistic gas limit. - tx_env.gas_limit = optimistic_gas_limit; + tx_env.set_gas_limit(optimistic_gas_limit); // Re-execute the transaction with the new gas limit and update the result and // environment. (res, (evm_env, tx_env)) = self.transact(&mut db, evm_env, tx_env)?; @@ -221,7 +221,7 @@ pub trait EstimateCall: Call { break }; - tx_env.gas_limit = mid_gas_limit; + tx_env.set_gas_limit(mid_gas_limit); // Execute transaction and handle potential gas errors, adjusting limits accordingly. match self.transact(&mut db, evm_env.clone(), tx_env.clone()) { @@ -281,16 +281,16 @@ pub trait EstimateCall: Call { fn map_out_of_gas_err( &self, env_gas_limit: U256, - evm_env: EvmEnv, - mut tx_env: TxEnv, + evm_env: EvmEnv<::Spec>, + mut tx_env: ::TxEnv, db: &mut DB, ) -> Self::Error where DB: Database, EthApiError: From, { - let req_gas_limit = tx_env.gas_limit; - tx_env.gas_limit = env_gas_limit.try_into().unwrap_or(u64::MAX); + let req_gas_limit = tx_env.gas_limit(); + tx_env.set_gas_limit(env_gas_limit.try_into().unwrap_or(u64::MAX)); let (res, _) = match self.transact(db, evm_env, tx_env) { Ok(res) => res, Err(err) => return err, 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 968ef379fced..1c61ebc2cd9c 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::{EthChainSpec, EthereumHardforks}; use reth_errors::RethError; use reth_evm::{ env::EvmEnv, state_change::post_block_withdrawals_balance_increments, - system_calls::SystemCaller, ConfigureEvm, ConfigureEvmEnv, NextBlockEnvAttributes, + system_calls::SystemCaller, ConfigureEvm, ConfigureEvmEnv, Evm, NextBlockEnvAttributes, }; use reth_primitives::{InvalidTransactionError, RecoveredBlock}; use reth_primitives_traits::Receipt; @@ -69,7 +69,11 @@ pub trait LoadPendingBlock: fn pending_block_env_and_cfg( &self, ) -> Result< - PendingBlockEnv, ProviderReceipt>, + PendingBlockEnv< + ProviderBlock, + ProviderReceipt, + ::Spec, + >, Self::Error, > { if let Some(block) = @@ -83,7 +87,7 @@ pub trait LoadPendingBlock: // Note: for the PENDING block we assume it is past the known merge block and // thus this will not fail when looking up the total // difficulty value for the blockenv. - let evm_env = self.evm_config().cfg_and_block_env(block.header()); + let evm_env = self.evm_config().evm_env(block.header()); return Ok(PendingBlockEnv::new( evm_env, @@ -102,7 +106,7 @@ pub trait LoadPendingBlock: let evm_env = self .evm_config() - .next_cfg_and_block_env( + .next_evm_env( &latest, NextBlockEnvAttributes { timestamp: latest.timestamp() + 12, @@ -234,7 +238,7 @@ pub trait LoadPendingBlock: #[expect(clippy::type_complexity)] fn build_block( &self, - evm_env: EvmEnv, + evm_env: EvmEnv<::Spec>, parent_hash: B256, ) -> Result< (RecoveredBlock>, Vec>), @@ -325,9 +329,9 @@ pub trait LoadPendingBlock: } let tx_env = self.evm_config().tx_env(tx.tx(), tx.signer()); - let mut evm = self.evm_config().evm_with_env(&mut db, evm_env.clone(), tx_env); + let mut evm = self.evm_config().evm_with_env(&mut db, evm_env.clone()); - let ResultAndState { result, state } = match evm.transact() { + let ResultAndState { result, state } = match evm.transact(tx_env) { Ok(res) => res, Err(err) => { match err { diff --git a/crates/rpc/rpc-eth-api/src/helpers/state.rs b/crates/rpc/rpc-eth-api/src/helpers/state.rs index 2f33ab1122a2..4bb0f67f01ba 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/state.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/state.rs @@ -210,10 +210,13 @@ pub trait LoadState: /// for. /// If the [`BlockId`] is pending, this will return the "Pending" tag, otherwise this returns /// the hash of the exact block. + #[expect(clippy::type_complexity)] fn evm_env_at( &self, at: BlockId, - ) -> impl Future> + Send + ) -> impl Future< + Output = Result<(EvmEnv<::Spec>, BlockId), Self::Error>, + > + Send where Self: LoadPendingBlock + SpawnBlocking, { @@ -230,7 +233,7 @@ pub trait LoadState: let header = self.cache().get_header(block_hash).await.map_err(Self::Error::from_eth_err)?; - let evm_env = self.evm_config().cfg_and_block_env(&header); + let evm_env = self.evm_config().evm_env(&header); Ok((evm_env, block_hash.into())) } diff --git a/crates/rpc/rpc-eth-api/src/helpers/trace.rs b/crates/rpc/rpc-eth-api/src/helpers/trace.rs index 5e99ba134d29..85cb1ce5bdc2 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/trace.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/trace.rs @@ -7,7 +7,7 @@ use alloy_primitives::B256; use alloy_rpc_types_eth::{BlockId, TransactionInfo}; use futures::Future; use reth_chainspec::ChainSpecProvider; -use reth_evm::{env::EvmEnv, system_calls::SystemCaller, ConfigureEvm, ConfigureEvmEnv}; +use reth_evm::{env::EvmEnv, system_calls::SystemCaller, ConfigureEvm, ConfigureEvmEnv, Evm}; use reth_primitives::RecoveredBlock; use reth_primitives_traits::{BlockBody, SignedTransaction}; use reth_provider::{BlockReader, ProviderBlock, ProviderHeader, ProviderTx}; @@ -18,9 +18,7 @@ use reth_rpc_eth_types::{ }; use revm::{db::CacheDB, Database, DatabaseCommit, GetInspector, Inspector}; use revm_inspectors::tracing::{TracingInspector, TracingInspectorConfig}; -use revm_primitives::{ - CfgEnvWithHandlerCfg, Env, EnvWithHandlerCfg, EvmState, ExecutionResult, ResultAndState, TxEnv, -}; +use revm_primitives::{EvmState, ExecutionResult, ResultAndState}; use std::{fmt::Display, sync::Arc}; /// Executes CPU heavy tasks. @@ -33,43 +31,43 @@ pub trait Trace: >, > { - /// Executes the [`EnvWithHandlerCfg`] against the given [Database] without committing state + /// Executes the [`EvmEnv`] against the given [Database] without committing state /// changes. + #[expect(clippy::type_complexity)] fn inspect( &self, db: DB, - evm_env: EvmEnv, - tx_env: TxEnv, + evm_env: EvmEnv<::Spec>, + tx_env: ::TxEnv, inspector: I, - ) -> Result<(ResultAndState, (EvmEnv, TxEnv)), Self::Error> + ) -> Result< + ( + ResultAndState, + (EvmEnv<::Spec>, ::TxEnv), + ), + Self::Error, + > where DB: Database, EthApiError: From, I: GetInspector, { - let mut evm = self.evm_config().evm_with_env_and_inspector(db, evm_env, tx_env, inspector); - let res = evm.transact().map_err(Self::Error::from_evm_err)?; - let (_, env) = evm.into_db_and_env_with_handler_cfg(); - let EnvWithHandlerCfg { env, handler_cfg } = env; - let Env { cfg, block, tx } = *env; - let evm_env = EvmEnv { - cfg_env_with_handler_cfg: CfgEnvWithHandlerCfg { cfg_env: cfg, handler_cfg }, - block_env: block, - }; - Ok((res, (evm_env, tx))) + let mut evm = self.evm_config().evm_with_env_and_inspector(db, evm_env.clone(), inspector); + let res = evm.transact(tx_env.clone()).map_err(Self::Error::from_evm_err)?; + Ok((res, (evm_env, tx_env))) } /// Executes the transaction on top of the given [`BlockId`] with a tracer configured by the /// config. /// /// The callback is then called with the [`TracingInspector`] and the [`ResultAndState`] after - /// the configured [`EnvWithHandlerCfg`] was inspected. + /// the configured [`EvmEnv`] was inspected. /// /// Caution: this is blocking fn trace_at( &self, - evm_env: EvmEnv, - tx_env: TxEnv, + evm_env: EvmEnv<::Spec>, + tx_env: ::TxEnv, config: TracingInspectorConfig, at: BlockId, f: F, @@ -92,11 +90,11 @@ pub trait Trace: /// config. /// /// The callback is then called with the [`TracingInspector`] and the [`ResultAndState`] after - /// the configured [`EnvWithHandlerCfg`] was inspected. + /// the configured [`EvmEnv`] was inspected. fn spawn_trace_at_with_state( &self, - evm_env: EvmEnv, - tx_env: TxEnv, + evm_env: EvmEnv<::Spec>, + tx_env: ::TxEnv, config: TracingInspectorConfig, at: BlockId, f: F, @@ -451,7 +449,7 @@ pub trait Trace: &self, block: &RecoveredBlock>, db: &mut DB, - evm_env: &EvmEnv, + evm_env: &EvmEnv<::Spec>, ) -> Result<(), Self::Error> { let mut system_caller = SystemCaller::new(self.evm_config().clone(), self.provider().chain_spec()); diff --git a/crates/rpc/rpc-eth-api/src/helpers/transaction.rs b/crates/rpc/rpc-eth-api/src/helpers/transaction.rs index 6ab585dede7c..5c855cb8f5e9 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/transaction.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/transaction.rs @@ -485,8 +485,8 @@ pub trait LoadTransaction: SpawnBlocking + FullEthApiTypes + RpcNodeCoreExt { // part of pending block) and already. We don't need to // check for pre EIP-2 because this transaction could be pre-EIP-2. let transaction = tx - .into_ecrecovered_unchecked() - .ok_or(EthApiError::InvalidTransactionSignature)?; + .into_recovered_unchecked() + .map_err(|_| EthApiError::InvalidTransactionSignature)?; let tx = TransactionSource::Block { transaction, diff --git a/crates/rpc/rpc-eth-types/src/cache/mod.rs b/crates/rpc/rpc-eth-types/src/cache/mod.rs index ff2646cfc5b7..d4db9d9c02ab 100644 --- a/crates/rpc/rpc-eth-types/src/cache/mod.rs +++ b/crates/rpc/rpc-eth-types/src/cache/mod.rs @@ -263,9 +263,9 @@ pub(crate) struct EthStateCacheService< { /// The type used to lookup data from disk provider: Provider, - /// The LRU cache for full blocks grouped by their hash. + /// The LRU cache for full blocks grouped by their block hash. full_block_cache: BlockLruCache, - /// The LRU cache for full blocks grouped by their hash. + /// The LRU cache for block receipts grouped by the block hash. receipts_cache: ReceiptsLruCache, /// The LRU cache for headers. /// @@ -278,7 +278,9 @@ pub(crate) struct EthStateCacheService< action_rx: UnboundedReceiverStream>, /// The type that's used to spawn tasks that do the actual work action_task_spawner: Tasks, - /// Rate limiter + /// Rate limiter for spawned fetch tasks. + /// + /// This restricts the max concurrent fetch tasks at the same time. rate_limiter: Arc, } @@ -452,6 +454,12 @@ where continue } + // it's possible we have the entire block cached + if let Some(block) = this.full_block_cache.get(&block_hash) { + let _ = response_tx.send(Ok(block.clone_header())); + continue + } + // header is not in the cache, request it if this is the first // consumer if this.headers_cache.queue(block_hash, response_tx) { @@ -508,9 +516,7 @@ where for block_receipts in chain_change.receipts { this.on_new_receipts( block_receipts.block_hash, - Ok(Some(Arc::new( - block_receipts.receipts.into_iter().flatten().collect(), - ))), + Ok(Some(Arc::new(block_receipts.receipts))), ); } } @@ -522,9 +528,7 @@ where for block_receipts in chain_change.receipts { this.on_reorg_receipts( block_receipts.block_hash, - Ok(Some(Arc::new( - block_receipts.receipts.into_iter().flatten().collect(), - ))), + Ok(Some(Arc::new(block_receipts.receipts))), ); } } @@ -550,7 +554,7 @@ enum CacheAction { struct BlockReceipts { block_hash: B256, - receipts: Vec>, + receipts: Vec, } /// A change of the canonical chain diff --git a/crates/rpc/rpc-eth-types/src/error/mod.rs b/crates/rpc/rpc-eth-types/src/error/mod.rs index e93a566e26c1..d798002f33cd 100644 --- a/crates/rpc/rpc-eth-types/src/error/mod.rs +++ b/crates/rpc/rpc-eth-types/src/error/mod.rs @@ -10,6 +10,7 @@ use alloy_primitives::{Address, Bytes, U256}; use alloy_rpc_types_eth::{error::EthRpcErrorCode, request::TransactionInputError, BlockError}; use alloy_sol_types::decode_revert_reason; use reth_errors::RethError; +use reth_primitives_traits::transaction::signed::RecoveryError; use reth_rpc_server_types::result::{ block_id_to_str, internal_rpc_err, invalid_params_rpc_err, rpc_err, rpc_error_with_code, }; @@ -285,6 +286,12 @@ where } } +impl From for EthApiError { + fn from(_: RecoveryError) -> Self { + Self::InvalidTransactionSignature + } +} + /// An error due to invalid transaction. /// /// The only reason this exists is to maintain compatibility with other clients de-facto standard diff --git a/crates/rpc/rpc-eth-types/src/fee_history.rs b/crates/rpc/rpc-eth-types/src/fee_history.rs index d5c6b72f7a4d..fdf0d2730ae3 100644 --- a/crates/rpc/rpc-eth-types/src/fee_history.rs +++ b/crates/rpc/rpc-eth-types/src/fee_history.rs @@ -250,7 +250,7 @@ pub async fn fee_history_cache_new_blocks_task( let (blocks, receipts): (Vec<_>, Vec<_>) = committed .blocks_and_receipts() .map(|(block, receipts)| { - (block.clone_sealed_block(), Arc::new(receipts.iter().flatten().cloned().collect::>())) + (block.clone_sealed_block(), Arc::new(receipts.clone())) }) .unzip(); fee_history_cache.insert_blocks(blocks.iter().zip(receipts)).await; diff --git a/crates/rpc/rpc-eth-types/src/gas_oracle.rs b/crates/rpc/rpc-eth-types/src/gas_oracle.rs index 34e430313cf3..07f5fb1e07e1 100644 --- a/crates/rpc/rpc-eth-types/src/gas_oracle.rs +++ b/crates/rpc/rpc-eth-types/src/gas_oracle.rs @@ -226,7 +226,7 @@ where let parent_hash = block.parent_hash(); // sort the functions by ascending effective tip first - let sorted_transactions = block.body().transactions().iter().sorted_by_cached_key(|tx| { + let sorted_transactions = block.body().transactions_iter().sorted_by_cached_key(|tx| { if let Some(base_fee) = base_fee_per_gas { (*tx).effective_tip_per_gas(base_fee) } else { @@ -251,7 +251,7 @@ where } // check if the sender was the coinbase, if so, ignore - if let Some(sender) = tx.recover_signer() { + if let Ok(sender) = tx.recover_signer() { if sender == block.beneficiary() { continue } diff --git a/crates/rpc/rpc-eth-types/src/pending_block.rs b/crates/rpc/rpc-eth-types/src/pending_block.rs index bfd6da992729..08a0f8f3c769 100644 --- a/crates/rpc/rpc-eth-types/src/pending_block.rs +++ b/crates/rpc/rpc-eth-types/src/pending_block.rs @@ -14,9 +14,9 @@ use reth_primitives_traits::Block; /// Configured [`EvmEnv`] for a pending block. #[derive(Debug, Clone, Constructor)] -pub struct PendingBlockEnv { +pub struct PendingBlockEnv { /// Configured [`EvmEnv`] for the pending block. - pub evm_env: EvmEnv, + pub evm_env: EvmEnv, /// Origin block for the config pub origin: PendingBlockEnvOrigin, } diff --git a/crates/rpc/rpc-eth-types/src/receipt.rs b/crates/rpc/rpc-eth-types/src/receipt.rs index c207eb1bc03e..60d92acd3489 100644 --- a/crates/rpc/rpc-eth-types/src/receipt.rs +++ b/crates/rpc/rpc-eth-types/src/receipt.rs @@ -1,6 +1,6 @@ //! RPC receipt response builder, extends a layer one receipt with layer two data. -use super::{EthApiError, EthResult}; +use super::EthResult; use alloy_consensus::{transaction::TransactionMeta, ReceiptEnvelope, TxReceipt}; use alloy_primitives::{Address, TxKind}; use alloy_rpc_types_eth::{Log, ReceiptWithBloom, TransactionReceipt}; @@ -21,8 +21,7 @@ where { // Note: we assume this transaction is valid, because it's mined (or part of pending block) // and we don't need to check for pre EIP-2 - let from = - transaction.recover_signer_unchecked().ok_or(EthApiError::InvalidTransactionSignature)?; + let from = transaction.recover_signer_unchecked()?; // get the previous transaction cumulative gas used let gas_used = if meta.index == 0 { diff --git a/crates/rpc/rpc-eth-types/src/revm_utils.rs b/crates/rpc/rpc-eth-types/src/revm_utils.rs index 9d6c2c26cdfe..3fe8e8237add 100644 --- a/crates/rpc/rpc-eth-types/src/revm_utils.rs +++ b/crates/rpc/rpc-eth-types/src/revm_utils.rs @@ -5,10 +5,11 @@ use alloy_rpc_types_eth::{ state::{AccountOverride, StateOverride}, BlockOverrides, }; +use reth_evm::TransactionEnv; use revm::{ db::CacheDB, precompile::{PrecompileSpecId, Precompiles}, - primitives::{db::DatabaseRef, Bytecode, SpecId, TxEnv}, + primitives::{db::DatabaseRef, Bytecode, SpecId}, Database, }; use revm_primitives::BlockEnv; @@ -32,26 +33,26 @@ pub fn get_precompiles(spec_id: SpecId) -> impl IntoIterator { /// /// Note: this takes the mut [Database] trait because the loaded sender can be reused for the /// following operation like `eth_call`. -pub fn caller_gas_allowance(db: &mut DB, env: &TxEnv) -> EthResult +pub fn caller_gas_allowance(db: &mut DB, env: &impl TransactionEnv) -> EthResult where DB: Database, EthApiError: From<::Error>, { // Get the caller account. - let caller = db.basic(env.caller)?; + let caller = db.basic(env.caller())?; // Get the caller balance. let balance = caller.map(|acc| acc.balance).unwrap_or_default(); // Get transaction value. - let value = env.value; + let value = env.value(); // Subtract transferred value from the caller balance. Return error if the caller has // insufficient funds. let balance = balance - .checked_sub(env.value) + .checked_sub(env.value()) .ok_or_else(|| RpcInvalidTransactionError::InsufficientFunds { cost: value, balance })?; Ok(balance // Calculate the amount of gas the caller can afford with the specified gas price. - .checked_div(env.gas_price) + .checked_div(env.gas_price()) // This will be 0 if gas price is 0. It is fine, because we check it before. .unwrap_or_default()) } diff --git a/crates/rpc/rpc-eth-types/src/transaction.rs b/crates/rpc/rpc-eth-types/src/transaction.rs index 549ca0e8bb9e..c9a2196bfc69 100644 --- a/crates/rpc/rpc-eth-types/src/transaction.rs +++ b/crates/rpc/rpc-eth-types/src/transaction.rs @@ -4,7 +4,7 @@ use alloy_primitives::B256; use alloy_rpc_types_eth::TransactionInfo; -use reth_primitives::{RecoveredTx, TransactionSigned}; +use reth_primitives::{Recovered, TransactionSigned}; use reth_primitives_traits::SignedTransaction; use reth_rpc_types_compat::TransactionCompat; @@ -12,13 +12,13 @@ use reth_rpc_types_compat::TransactionCompat; #[derive(Debug, Clone, Eq, PartialEq)] pub enum TransactionSource { /// Transaction exists in the pool (Pending) - Pool(RecoveredTx), + Pool(Recovered), /// Transaction already included in a block /// /// This can be a historical block or a pending block (received from the CL) Block { /// Transaction fetched via provider - transaction: RecoveredTx, + transaction: Recovered, /// Index of the transaction in the block index: u64, /// Hash of the block. @@ -34,7 +34,7 @@ pub enum TransactionSource { impl TransactionSource { /// Consumes the type and returns the wrapped transaction. - pub fn into_recovered(self) -> RecoveredTx { + pub fn into_recovered(self) -> Recovered { self.into() } @@ -60,7 +60,7 @@ impl TransactionSource { } /// Returns the transaction and block related info, if not pending - pub fn split(self) -> (RecoveredTx, TransactionInfo) { + pub fn split(self) -> (Recovered, TransactionInfo) { match self { Self::Pool(tx) => { let hash = tx.trie_hash(); @@ -83,7 +83,7 @@ impl TransactionSource { } } -impl From> for RecoveredTx { +impl From> for Recovered { fn from(value: TransactionSource) -> Self { match value { TransactionSource::Pool(tx) => tx, diff --git a/crates/rpc/rpc-eth-types/src/utils.rs b/crates/rpc/rpc-eth-types/src/utils.rs index f12c819aea3f..5d498ffafd45 100644 --- a/crates/rpc/rpc-eth-types/src/utils.rs +++ b/crates/rpc/rpc-eth-types/src/utils.rs @@ -1,7 +1,7 @@ //! Commonly used code snippets use super::{EthApiError, EthResult}; -use reth_primitives::{transaction::SignedTransactionIntoRecoveredExt, RecoveredTx}; +use reth_primitives::{transaction::SignedTransactionIntoRecoveredExt, Recovered}; use reth_primitives_traits::SignedTransaction; use std::future::Future; @@ -11,7 +11,7 @@ use std::future::Future; /// malformed. /// /// See [`alloy_eips::eip2718::Decodable2718::decode_2718`] -pub fn recover_raw_transaction(mut data: &[u8]) -> EthResult> { +pub fn recover_raw_transaction(mut data: &[u8]) -> EthResult> { if data.is_empty() { return Err(EthApiError::EmptyRawTransactionData) } @@ -19,7 +19,7 @@ pub fn recover_raw_transaction(mut data: &[u8]) -> EthResu let transaction = T::decode_2718(&mut data).map_err(|_| EthApiError::FailedToDecodeSignedTransaction)?; - transaction.try_into_ecrecovered().or(Err(EthApiError::InvalidTransactionSignature)) + transaction.try_into_recovered().or(Err(EthApiError::InvalidTransactionSignature)) } /// Performs a binary search within a given block range to find the desired block number. diff --git a/crates/rpc/rpc-types-compat/src/engine/payload.rs b/crates/rpc/rpc-types-compat/src/engine/payload.rs index 0584c3087291..b0262dbd8dc9 100644 --- a/crates/rpc/rpc-types-compat/src/engine/payload.rs +++ b/crates/rpc/rpc-types-compat/src/engine/payload.rs @@ -108,7 +108,7 @@ pub fn convert_block_to_payload_field_v2( pub fn convert_to_payload_body_v1( value: impl reth_primitives_traits::Block, ) -> ExecutionPayloadBodyV1 { - let transactions = value.body().transactions().iter().map(|tx| tx.encoded_2718().into()); + let transactions = value.body().transactions_iter().map(|tx| tx.encoded_2718().into()); ExecutionPayloadBodyV1 { transactions: transactions.collect(), withdrawals: value.body().withdrawals().cloned().map(Withdrawals::into_inner), diff --git a/crates/rpc/rpc-types-compat/src/transaction.rs b/crates/rpc/rpc-types-compat/src/transaction.rs index f2c641d2ffcf..d1274494ae9e 100644 --- a/crates/rpc/rpc-types-compat/src/transaction.rs +++ b/crates/rpc/rpc-types-compat/src/transaction.rs @@ -4,7 +4,7 @@ use core::error; use std::fmt; use alloy_rpc_types_eth::{request::TransactionRequest, TransactionInfo}; -use reth_primitives::{RecoveredTx, TransactionSigned}; +use reth_primitives::{Recovered, TransactionSigned}; use serde::{Deserialize, Serialize}; /// Builds RPC transaction w.r.t. network. @@ -26,7 +26,7 @@ pub trait TransactionCompat: /// Wrapper for `fill()` with default `TransactionInfo` /// Create a new rpc transaction result for a _pending_ signed transaction, setting block /// environment related fields to `None`. - fn fill_pending(&self, tx: RecoveredTx) -> Result { + fn fill_pending(&self, tx: Recovered) -> Result { self.fill(tx, TransactionInfo::default()) } @@ -37,7 +37,7 @@ pub trait TransactionCompat: /// transaction was mined. fn fill( &self, - tx: RecoveredTx, + tx: Recovered, tx_inf: TransactionInfo, ) -> Result; @@ -51,9 +51,9 @@ pub trait TransactionCompat: fn otterscan_api_truncate_input(tx: &mut Self::Transaction); } -/// Convert [`RecoveredTx`] to [`TransactionRequest`] +/// Convert [`Recovered`] to [`TransactionRequest`] pub fn transaction_to_call_request( - tx: RecoveredTx, + tx: Recovered, ) -> TransactionRequest { let from = tx.signer(); TransactionRequest::from_transaction_with_sender(tx.into_tx(), from) diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index 59637ce81b0e..fa0da3deca24 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -18,7 +18,7 @@ use reth_chainspec::EthereumHardforks; use reth_evm::{ env::EvmEnv, execute::{BlockExecutorProvider, Executor}, - ConfigureEvmEnv, + ConfigureEvmEnv, TransactionEnv, }; use reth_primitives::{NodePrimitives, ReceiptWithBloom, RecoveredBlock}; use reth_primitives_traits::{Block as _, BlockBody, SignedTransaction}; @@ -42,7 +42,6 @@ use revm::{ use revm_inspectors::tracing::{ FourByteInspector, MuxInspector, TracingInspector, TracingInspectorConfig, TransactionContext, }; -use revm_primitives::TxEnv; use std::sync::Arc; use tokio::sync::{AcquireError, OwnedSemaphorePermit}; @@ -96,7 +95,7 @@ where async fn trace_block( &self, block: Arc>>, - evm_env: EvmEnv, + evm_env: EvmEnv<::Spec>, opts: GethDebugTracingOptions, ) -> Result, Eth::Error> { // replay all transactions of the block @@ -157,7 +156,7 @@ where .map_err(BlockError::RlpDecodeRawBlock) .map_err(Eth::Error::from_eth_err)?; - let evm_env = self.eth_api().evm_config().cfg_and_block_env(block.header()); + let evm_env = self.eth_api().evm_config().evm_env(block.header()); // Depending on EIP-2 we need to recover the transactions differently let senders = @@ -166,26 +165,22 @@ where .body() .transactions() .iter() - .map(|tx| { - tx.recover_signer() - .ok_or(EthApiError::InvalidTransactionSignature) - .map_err(Eth::Error::from_eth_err) - }) - .collect::, Eth::Error>>()? + .map(|tx| tx.recover_signer().map_err(Eth::Error::from_eth_err)) + .collect::, _>>()? + .into_iter() + .collect() } else { block .body() .transactions() .iter() - .map(|tx| { - tx.recover_signer_unchecked() - .ok_or(EthApiError::InvalidTransactionSignature) - .map_err(Eth::Error::from_eth_err) - }) - .collect::, Eth::Error>>()? + .map(|tx| tx.recover_signer_unchecked().map_err(Eth::Error::from_eth_err)) + .collect::, _>>()? + .into_iter() + .collect() }; - self.trace_block(Arc::new(block.with_senders_unchecked(senders)), evm_env, opts).await + self.trace_block(Arc::new(block.into_recovered_with_signers(senders)), evm_env, opts).await } /// Replays a block and returns the trace of each transaction. @@ -315,7 +310,7 @@ where let (res, (_, tx_env)) = this.eth_api().inspect(db, evm_env, tx_env, &mut inspector)?; let frame = inspector - .with_transaction_gas_limit(tx_env.gas_limit) + .with_transaction_gas_limit(tx_env.gas_limit()) .into_geth_builder() .geth_call_traces(call_config, res.result.gas_used()); Ok(frame.into()) @@ -345,7 +340,7 @@ where &mut inspector, )?; let frame = inspector - .with_transaction_gas_limit(tx_env.gas_limit) + .with_transaction_gas_limit(tx_env.gas_limit()) .into_geth_builder() .geth_prestate_traces(&res, &prestate_config, db) .map_err(Eth::Error::from_eth_err)?; @@ -414,7 +409,7 @@ where this.eth_api().inspect(db, evm_env, tx_env, &mut inspector)?; let tx_info = TransactionInfo::default(); let frame: FlatCallFrame = inspector - .with_transaction_gas_limit(tx_env.gas_limit) + .with_transaction_gas_limit(tx_env.gas_limit()) .into_parity_builder() .into_localized_transaction_traces(tx_info); Ok(frame) @@ -451,9 +446,9 @@ where &mut inspector, )?; let env = revm_primitives::Env::boxed( - evm_env.cfg_env_with_handler_cfg.cfg_env, + evm_env.cfg_env, evm_env.block_env, - tx_env, + tx_env.into(), ); inspector.json_result(res, &env, db).map_err(Eth::Error::from_eth_err) }) @@ -474,7 +469,7 @@ where .spawn_with_call_at(call, at, overrides, move |db, evm_env, tx_env| { let (res, (_, tx_env)) = this.eth_api().inspect(db, evm_env, tx_env, &mut inspector)?; - Ok((res, tx_env.gas_limit, inspector)) + Ok((res, tx_env.gas_limit(), inspector)) }) .await?; let gas_used = res.result.gas_used(); @@ -655,8 +650,8 @@ where fn trace_transaction( &self, opts: &GethDebugTracingOptions, - evm_env: EvmEnv, - tx_env: TxEnv, + evm_env: EvmEnv<::Spec>, + tx_env: ::TxEnv, db: &mut StateCacheDb<'_>, transaction_context: Option, fused_inspector: &mut Option, @@ -698,7 +693,7 @@ where let (res, (_, tx_env)) = self.eth_api().inspect(db, evm_env, tx_env, &mut inspector)?; - inspector.set_transaction_gas_limit(tx_env.gas_limit); + inspector.set_transaction_gas_limit(tx_env.gas_limit()); let frame = inspector .geth_builder() @@ -720,7 +715,7 @@ where let (res, (_, tx_env)) = self.eth_api().inspect(&mut *db, evm_env, tx_env, &mut inspector)?; - inspector.set_transaction_gas_limit(tx_env.gas_limit); + inspector.set_transaction_gas_limit(tx_env.gas_limit()); let frame = inspector .geth_builder() .geth_prestate_traces(&res, &prestate_config, db) @@ -760,7 +755,7 @@ where let (res, (_, tx_env)) = self.eth_api().inspect(db, evm_env, tx_env, &mut inspector)?; let frame: FlatCallFrame = inspector - .with_transaction_gas_limit(tx_env.gas_limit) + .with_transaction_gas_limit(tx_env.gas_limit()) .into_parity_builder() .into_localized_transaction_traces(tx_info); @@ -786,9 +781,9 @@ where let state = res.state.clone(); let env = revm_primitives::Env::boxed( - evm_env.cfg_env_with_handler_cfg.cfg_env, + evm_env.cfg_env, evm_env.block_env, - tx_env, + tx_env.into(), ); let result = inspector.json_result(res, &env, db).map_err(Eth::Error::from_eth_err)?; @@ -805,7 +800,7 @@ where let (res, (_, tx_env)) = self.eth_api().inspect(db, evm_env, tx_env, &mut inspector)?; let gas_used = res.result.gas_used(); let return_value = res.result.into_output().unwrap_or_default(); - inspector.set_transaction_gas_limit(tx_env.gas_limit); + inspector.set_transaction_gas_limit(tx_env.gas_limit()); let frame = inspector.geth_builder().geth_traces(gas_used, return_value, *config); Ok((frame.into(), res.state)) @@ -872,7 +867,7 @@ where .block_with_senders_by_id(block_id, TransactionVariant::NoHash) .to_rpc_result()? .unwrap_or_default(); - Ok(block.into_transactions_ecrecovered().map(|tx| tx.encoded_2718().into()).collect()) + Ok(block.into_transactions_recovered().map(|tx| tx.encoded_2718().into()).collect()) } /// Handler for `debug_getRawReceipts` diff --git a/crates/rpc/rpc/src/eth/bundle.rs b/crates/rpc/rpc/src/eth/bundle.rs index be431f603ab2..93db0b00548b 100644 --- a/crates/rpc/rpc/src/eth/bundle.rs +++ b/crates/rpc/rpc/src/eth/bundle.rs @@ -1,18 +1,16 @@ //! `Eth` bundle implementation and helpers. -use alloy_consensus::{BlockHeader, EnvKzgSettings, Transaction as _}; +use alloy_consensus::{EnvKzgSettings, Transaction as _}; use alloy_eips::eip4844::MAX_DATA_GAS_PER_BLOCK; use alloy_primitives::{Keccak256, U256}; use alloy_rpc_types_mev::{EthCallBundle, EthCallBundleResponse, EthCallBundleTransactionResult}; use jsonrpsee::core::RpcResult; -use reth_chainspec::EthChainSpec; -use reth_evm::{ConfigureEvm, ConfigureEvmEnv}; +use reth_evm::{ConfigureEvm, ConfigureEvmEnv, Evm}; use reth_primitives_traits::SignedTransaction; -use reth_provider::{ChainSpecProvider, HeaderProvider}; use reth_revm::database::StateProviderDatabase; use reth_rpc_eth_api::{ helpers::{Call, EthTransactions, LoadPendingBlock}, - EthCallBundleApiServer, FromEthApiError, FromEvmError, RpcNodeCore, + EthCallBundleApiServer, FromEthApiError, FromEvmError, }; use reth_rpc_eth_types::{utils::recover_raw_transaction, EthApiError, RpcInvalidTransactionError}; use reth_tasks::pool::BlockingTaskGuard; @@ -23,7 +21,6 @@ use revm::{ db::{CacheDB, DatabaseCommit, DatabaseRef}, primitives::ResultAndState, }; -use revm_primitives::SpecId; use std::sync::Arc; /// `Eth` bundle implementation. @@ -132,21 +129,6 @@ where if let Some(base_fee) = base_fee { evm_env.block_env.basefee = U256::from(base_fee); - } else if evm_env.cfg_env_with_handler_cfg.handler_cfg.spec_id.is_enabled_in(SpecId::LONDON) - { - let parent_block = evm_env.block_env.number.saturating_to::(); - // here we need to fetch the _next_ block's basefee based on the parent block - let parent = RpcNodeCore::provider(self.eth_api()) - .header_by_number(parent_block) - .map_err(Eth::Error::from_eth_err)? - .ok_or(EthApiError::HeaderNotFound(parent_block.into()))?; - if let Some(base_fee) = parent.next_block_base_fee( - RpcNodeCore::provider(self.eth_api()) - .chain_spec() - .base_fee_params_at_block(parent_block), - ) { - evm_env.block_env.basefee = U256::from(base_fee); - } } let state_block_number = evm_env.block_env.number; @@ -172,7 +154,7 @@ where let mut total_gas_fess = U256::ZERO; let mut hasher = Keccak256::new(); - let mut evm = eth_api.evm_config().evm_with_env(db, evm_env, Default::default()); + let mut evm = eth_api.evm_config().evm_with_env(db, evm_env); let mut results = Vec::with_capacity(transactions.len()); let mut transactions = transactions.into_iter().peekable(); @@ -197,9 +179,9 @@ where hasher.update(*tx.tx_hash()); let gas_price = tx.effective_gas_price(basefee); - eth_api.evm_config().fill_tx_env(evm.tx_mut(), &tx, signer); - let ResultAndState { result, state } = - evm.transact().map_err(Eth::Error::from_evm_err)?; + let ResultAndState { result, state } = evm + .transact(eth_api.evm_config().tx_env(&tx, signer)) + .map_err(Eth::Error::from_evm_err)?; let gas_used = result.gas_used(); total_gas_used += gas_used; @@ -245,7 +227,7 @@ where if transactions.peek().is_some() { // need to apply the state changes of this call before executing // the next call - evm.context.evm.db.commit(state) + evm.db_mut().commit(state) } } diff --git a/crates/rpc/rpc/src/eth/helpers/call.rs b/crates/rpc/rpc/src/eth/helpers/call.rs index 2620165b9079..898ca5d5aaa4 100644 --- a/crates/rpc/rpc/src/eth/helpers/call.rs +++ b/crates/rpc/rpc/src/eth/helpers/call.rs @@ -2,12 +2,15 @@ use crate::EthApi; use alloy_consensus::Header; +use alloy_rpc_types::TransactionRequest; use reth_evm::ConfigureEvm; use reth_provider::{BlockReader, ProviderHeader}; use reth_rpc_eth_api::{ helpers::{estimate::EstimateCall, Call, EthCall, LoadPendingBlock, LoadState, SpawnBlocking}, - FullEthApiTypes, + FromEthApiError, FullEthApiTypes, IntoEthApiError, }; +use reth_rpc_eth_types::{revm_utils::CallFees, RpcInvalidTransactionError}; +use revm_primitives::{BlockEnv, TxEnv, TxKind, U256}; impl EthCall for EthApi where @@ -18,7 +21,8 @@ where impl Call for EthApi where - Self: LoadState>> + SpawnBlocking, + Self: LoadState>> + + SpawnBlocking, EvmConfig: ConfigureEvm
, Provider: BlockReader, { @@ -31,6 +35,81 @@ where fn max_simulate_blocks(&self) -> u64 { self.inner.max_simulate_blocks() } + + fn create_txn_env( + &self, + block_env: &BlockEnv, + request: TransactionRequest, + ) -> Result { + // Ensure that if versioned hashes are set, they're not empty + if request.blob_versioned_hashes.as_ref().is_some_and(|hashes| hashes.is_empty()) { + return Err(RpcInvalidTransactionError::BlobTransactionMissingBlobHashes.into_eth_err()) + } + + let TransactionRequest { + from, + to, + gas_price, + max_fee_per_gas, + max_priority_fee_per_gas, + gas, + value, + input, + nonce, + access_list, + chain_id, + blob_versioned_hashes, + max_fee_per_blob_gas, + authorization_list, + transaction_type: _, + sidecar: _, + } = request; + + let CallFees { max_priority_fee_per_gas, gas_price, max_fee_per_blob_gas } = + CallFees::ensure_fees( + gas_price.map(U256::from), + max_fee_per_gas.map(U256::from), + max_priority_fee_per_gas.map(U256::from), + block_env.basefee, + blob_versioned_hashes.as_deref(), + max_fee_per_blob_gas.map(U256::from), + block_env.get_blob_gasprice().map(U256::from), + )?; + + let gas_limit = gas.unwrap_or_else(|| { + // Use maximum allowed gas limit. The reason for this + // is that both Erigon and Geth use pre-configured gas cap even if + // it's possible to derive the gas limit from the block: + // + block_env.gas_limit.saturating_to() + }); + + #[allow(clippy::needless_update)] + let env = TxEnv { + gas_limit, + nonce, + caller: from.unwrap_or_default(), + gas_price, + gas_priority_fee: max_priority_fee_per_gas, + transact_to: to.unwrap_or(TxKind::Create), + value: value.unwrap_or_default(), + data: input + .try_into_unique_input() + .map_err(Self::Error::from_eth_err)? + .unwrap_or_default(), + chain_id, + access_list: access_list.unwrap_or_default().into(), + // EIP-4844 fields + blob_hashes: blob_versioned_hashes.unwrap_or_default(), + max_fee_per_blob_gas, + // EIP-7702 fields + authorization_list: authorization_list.map(Into::into), + ..Default::default() + }; + + Ok(env) + } } impl EstimateCall for EthApi diff --git a/crates/rpc/rpc/src/eth/helpers/pending_block.rs b/crates/rpc/rpc/src/eth/helpers/pending_block.rs index b40fff97f343..cabc0d0119bb 100644 --- a/crates/rpc/rpc/src/eth/helpers/pending_block.rs +++ b/crates/rpc/rpc/src/eth/helpers/pending_block.rs @@ -5,11 +5,8 @@ use alloy_eips::{eip7685::EMPTY_REQUESTS_HASH, merge::BEACON_NONCE}; use alloy_primitives::U256; use reth_chainspec::{EthChainSpec, EthereumHardforks}; use reth_evm::ConfigureEvm; -use reth_primitives::{ - logs_bloom, - proofs::{calculate_receipt_root_no_memo, calculate_transaction_root}, - BlockBody, Receipt, -}; +use reth_primitives::{logs_bloom, BlockBody, Receipt}; +use reth_primitives_traits::proofs::calculate_transaction_root; use reth_provider::{ BlockReader, BlockReaderIdExt, ChainSpecProvider, ProviderBlock, ProviderReceipt, ProviderTx, StateProviderFactory, @@ -64,7 +61,8 @@ where let chain_spec = self.provider().chain_spec(); let transactions_root = calculate_transaction_root(&transactions); - let receipts_root = calculate_receipt_root_no_memo(&receipts.iter().collect::>()); + let receipts_root = + Receipt::calculate_receipt_root_no_memo(&receipts.iter().collect::>()); let logs_bloom = logs_bloom(receipts.iter().flat_map(|r| &r.logs)); diff --git a/crates/rpc/rpc/src/eth/helpers/types.rs b/crates/rpc/rpc/src/eth/helpers/types.rs index 4678a3bdcf40..8384e1dd5579 100644 --- a/crates/rpc/rpc/src/eth/helpers/types.rs +++ b/crates/rpc/rpc/src/eth/helpers/types.rs @@ -5,7 +5,7 @@ use alloy_network::{Ethereum, Network}; use alloy_primitives::PrimitiveSignature as Signature; use alloy_rpc_types::TransactionRequest; use alloy_rpc_types_eth::{Transaction, TransactionInfo}; -use reth_primitives::{RecoveredTx, TransactionSigned}; +use reth_primitives::{Recovered, TransactionSigned}; use reth_primitives_traits::SignedTransaction; use reth_rpc_eth_api::EthApiTypes; use reth_rpc_eth_types::EthApiError; @@ -40,7 +40,7 @@ where fn fill( &self, - tx: RecoveredTx, + tx: Recovered, tx_info: TransactionInfo, ) -> Result { let from = tx.signer(); diff --git a/crates/rpc/rpc/src/eth/sim_bundle.rs b/crates/rpc/rpc/src/eth/sim_bundle.rs index 1c24a5f4a4b8..940fa48f9992 100644 --- a/crates/rpc/rpc/src/eth/sim_bundle.rs +++ b/crates/rpc/rpc/src/eth/sim_bundle.rs @@ -1,6 +1,5 @@ //! `Eth` Sim bundle implementation and helpers. -use alloy_consensus::BlockHeader; use alloy_eips::BlockNumberOrTag; use alloy_primitives::U256; use alloy_rpc_types_eth::BlockId; @@ -9,21 +8,20 @@ use alloy_rpc_types_mev::{ SimBundleOverrides, SimBundleResponse, Validity, }; use jsonrpsee::core::RpcResult; -use reth_chainspec::EthChainSpec; -use reth_evm::{ConfigureEvm, ConfigureEvmEnv}; -use reth_provider::{ChainSpecProvider, HeaderProvider, ProviderTx}; +use reth_evm::{ConfigureEvm, ConfigureEvmEnv, Evm}; +use reth_provider::ProviderTx; use reth_revm::database::StateProviderDatabase; use reth_rpc_api::MevSimApiServer; use reth_rpc_eth_api::{ helpers::{Call, EthTransactions, LoadPendingBlock}, - FromEthApiError, RpcNodeCore, + FromEthApiError, }; use reth_rpc_eth_types::{utils::recover_raw_transaction, EthApiError}; use reth_tasks::pool::BlockingTaskGuard; use reth_transaction_pool::{PoolConsensusTx, PoolPooledTx, PoolTransaction, TransactionPool}; use revm::{ db::CacheDB, - primitives::{Address, ResultAndState, SpecId}, + primitives::{Address, ResultAndState}, DatabaseCommit, DatabaseRef, }; use std::{sync::Arc, time::Duration}; @@ -246,15 +244,6 @@ where let block_id = parent_block.unwrap_or(BlockId::Number(BlockNumberOrTag::Pending)); let (mut evm_env, current_block) = self.eth_api().evm_env_at(block_id).await?; - let parent_header = RpcNodeCore::provider(&self.inner.eth_api) - .header_by_number(evm_env.block_env.number.saturating_to::()) - .map_err(EthApiError::from_eth_err)? // Explicitly map the error - .ok_or_else(|| { - EthApiError::HeaderNotFound( - (evm_env.block_env.number.saturating_to::()).into(), - ) - })?; - // apply overrides if let Some(block_number) = block_number { evm_env.block_env.number = U256::from(block_number); @@ -274,15 +263,6 @@ where if let Some(base_fee) = base_fee { evm_env.block_env.basefee = U256::from(base_fee); - } else if evm_env.cfg_env_with_handler_cfg.handler_cfg.spec_id.is_enabled_in(SpecId::LONDON) - { - if let Some(base_fee) = parent_header.next_block_base_fee( - RpcNodeCore::provider(&self.inner.eth_api) - .chain_spec() - .base_fee_params_at_block(evm_env.block_env.number.saturating_to::()), - ) { - evm_env.block_env.basefee = U256::from(base_fee); - } } let eth_api = self.inner.eth_api.clone(); @@ -308,7 +288,7 @@ where let mut refundable_value = U256::ZERO; let mut body_logs: Vec = Vec::new(); - let mut evm = eth_api.evm_config().evm_with_env(db, evm_env, Default::default()); + let mut evm = eth_api.evm_config().evm_with_env(db, evm_env); for item in &flattened_bundle { // Check inclusion constraints @@ -324,10 +304,10 @@ where ) .into()); } - eth_api.evm_config().fill_tx_env(evm.tx_mut(), &item.tx, item.signer); - let ResultAndState { result, state } = - evm.transact().map_err(EthApiError::from_eth_err)?; + let ResultAndState { result, state } = evm + .transact(eth_api.evm_config().tx_env(&item.tx, item.signer)) + .map_err(EthApiError::from_eth_err)?; if !result.is_success() && !item.can_revert { return Err(EthApiError::InvalidParams( @@ -366,7 +346,7 @@ where } // Apply state changes - evm.context.evm.db.commit(state); + evm.db_mut().commit(state); } // After processing all transactions, process refunds diff --git a/crates/rpc/rpc/src/validation.rs b/crates/rpc/rpc/src/validation.rs index c621c8b9790c..cbf240dd72b7 100644 --- a/crates/rpc/rpc/src/validation.rs +++ b/crates/rpc/rpc/src/validation.rs @@ -51,7 +51,7 @@ where dyn PayloadValidator::Block>, >, ) -> Self { - let ValidationApiConfig { disallow } = config; + let ValidationApiConfig { disallow, validation_window } = config; let inner = Arc::new(ValidationApiInner { provider, @@ -59,6 +59,7 @@ where payload_validator, executor_provider, disallow, + validation_window, cached_state: Default::default(), task_spawner, }); @@ -130,11 +131,13 @@ where let latest_header = self.provider.latest_header()?.ok_or_else(|| ValidationApiError::MissingLatestBlock)?; - if latest_header.hash() != block.parent_hash() { - return Err(ConsensusError::ParentHashMismatch( - GotExpected { got: block.parent_hash(), expected: latest_header.hash() }.into(), - ) - .into()) + let parent_header = self + .provider + .header(&block.parent_hash())? + .ok_or_else(|| ValidationApiError::MissingParentBlock)?; + + if latest_header.number().saturating_sub(parent_header.number()) > self.validation_window { + return Err(ValidationApiError::BlockTooOld) } self.consensus.validate_header_against_parent(block.sealed_header(), &latest_header)?; self.validate_gas_limit(registered_gas_limit, &latest_header, block.sealed_header())?; @@ -466,6 +469,8 @@ pub struct ValidationApiInner { executor_provider: E, /// Set of disallowed addresses disallow: HashSet
, + /// The maximum block distance - parent to latest - allowed for validation + validation_window: u64, /// Cached state reads to avoid redundant disk I/O across multiple validation attempts /// targeting the same state. Stores a tuple of (`block_hash`, `cached_reads`) for the /// latest head block state. Uses async `RwLock` to safely handle concurrent validation @@ -476,10 +481,23 @@ pub struct ValidationApiInner { } /// Configuration for validation API. -#[derive(Debug, Clone, Default, Eq, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] pub struct ValidationApiConfig { /// Disallowed addresses. pub disallow: HashSet
, + /// The maximum block distance - parent to latest - allowed for validation + pub validation_window: u64, +} + +impl ValidationApiConfig { + /// Default validation blocks window of 3 blocks + pub const DEFAULT_VALIDATION_WINDOW: u64 = 3; +} + +impl Default for ValidationApiConfig { + fn default() -> Self { + Self { disallow: Default::default(), validation_window: Self::DEFAULT_VALIDATION_WINDOW } + } } /// Errors thrown by the validation API. @@ -495,6 +513,10 @@ pub enum ValidationApiError { BlockHashMismatch(GotExpected), #[error("missing latest block in database")] MissingLatestBlock, + #[error("parent block not found")] + MissingParentBlock, + #[error("block is too old, outside validation window")] + BlockTooOld, #[error("could not verify proposer payment")] ProposerPayment, #[error("invalid blobs bundle")] diff --git a/crates/stages/stages/Cargo.toml b/crates/stages/stages/Cargo.toml index e7114eeb16ac..2b519558c078 100644 --- a/crates/stages/stages/Cargo.toml +++ b/crates/stages/stages/Cargo.toml @@ -22,6 +22,7 @@ reth-db-api.workspace = true reth-etl.workspace = true reth-evm.workspace = true reth-exex.workspace = true +reth-fs-util.workspace = true reth-network-p2p.workspace = true reth-primitives = { workspace = true, features = ["secp256k1"] } reth-primitives-traits = { workspace = true, features = [ @@ -57,6 +58,12 @@ rayon.workspace = true num-traits = "0.2.15" tempfile = { workspace = true, optional = true } bincode.workspace = true +blake3.workspace = true +reqwest = { workspace = true, default-features = false, features = [ + "rustls-tls-native-roots", + "blocking" +] } +serde = { workspace = true, features = ["derive"] } [dev-dependencies] # reth @@ -75,6 +82,7 @@ reth-testing-utils.workspace = true reth-trie = { workspace = true, features = ["test-utils"] } reth-provider = { workspace = true, features = ["test-utils"] } reth-network-peers.workspace = true +reth-tracing.workspace = true alloy-rlp.workspace = true itertools.workspace = true diff --git a/crates/stages/stages/src/stages/execution.rs b/crates/stages/stages/src/stages/execution.rs index afa493b4d999..b157ce31564e 100644 --- a/crates/stages/stages/src/stages/execution.rs +++ b/crates/stages/stages/src/stages/execution.rs @@ -874,8 +874,10 @@ 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 random_filter = - ReceiptsLogPruneConfig(BTreeMap::from([(Address::random(), PruneMode::Full)])); + let random_filter = ReceiptsLogPruneConfig(BTreeMap::from([( + Address::random(), + PruneMode::Distance(100000), + )])); // Tests node with database and node with static files for mut mode in modes { @@ -1011,8 +1013,10 @@ 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 random_filter = - ReceiptsLogPruneConfig(BTreeMap::from([(Address::random(), PruneMode::Full)])); + let random_filter = ReceiptsLogPruneConfig(BTreeMap::from([( + Address::random(), + PruneMode::Before(100000), + )])); // Tests node with database and node with static files for mut mode in modes { diff --git a/crates/stages/stages/src/stages/mod.rs b/crates/stages/stages/src/stages/mod.rs index cdc84e77270e..9a8b594f836d 100644 --- a/crates/stages/stages/src/stages/mod.rs +++ b/crates/stages/stages/src/stages/mod.rs @@ -17,6 +17,8 @@ mod index_storage_history; /// Stage for computing state root. mod merkle; mod prune; +/// The s3 download stage +mod s3; /// The sender recovery stage. mod sender_recovery; /// The transaction lookup stage @@ -32,6 +34,7 @@ pub use index_account_history::*; pub use index_storage_history::*; pub use merkle::*; pub use prune::*; +pub use s3::*; pub use sender_recovery::*; pub use tx_lookup::*; diff --git a/crates/stages/stages/src/stages/s3/downloader/error.rs b/crates/stages/stages/src/stages/s3/downloader/error.rs new file mode 100644 index 000000000000..49f4b418aad2 --- /dev/null +++ b/crates/stages/stages/src/stages/s3/downloader/error.rs @@ -0,0 +1,31 @@ +use alloy_primitives::B256; +use reth_fs_util::FsPathError; + +/// Possible downloader error variants. +#[derive(Debug, thiserror::Error)] +pub enum DownloaderError { + /// Requires a valid `total_size` {0} + #[error("requires a valid total_size")] + InvalidMetadataTotalSize(Option), + #[error("tried to access chunk on index {0}, but there's only {1} chunks")] + /// Invalid chunk access + InvalidChunk(usize, usize), + // File hash mismatch. + #[error("file hash does not match the expected one {0} != {1} ")] + InvalidFileHash(B256, B256), + // Empty content length returned from the server. + #[error("metadata got an empty content length from server")] + EmptyContentLength, + /// Reqwest error + #[error(transparent)] + FsPath(#[from] FsPathError), + /// Reqwest error + #[error(transparent)] + Reqwest(#[from] reqwest::Error), + /// Std Io error + #[error(transparent)] + StdIo(#[from] std::io::Error), + /// Bincode error + #[error(transparent)] + Bincode(#[from] bincode::Error), +} diff --git a/crates/stages/stages/src/stages/s3/downloader/fetch.rs b/crates/stages/stages/src/stages/s3/downloader/fetch.rs new file mode 100644 index 000000000000..b9ae5774753a --- /dev/null +++ b/crates/stages/stages/src/stages/s3/downloader/fetch.rs @@ -0,0 +1,184 @@ +use crate::stages::s3::downloader::{worker::spawn_workers, RemainingChunkRange}; + +use super::{ + error::DownloaderError, + meta::Metadata, + worker::{WorkerRequest, WorkerResponse}, +}; +use alloy_primitives::B256; +use reqwest::{header::CONTENT_LENGTH, Client}; +use std::{ + collections::HashMap, + fs::{File, OpenOptions}, + io::BufReader, + path::Path, +}; +use tracing::{debug, error, info}; + +/// Downloads file from url to data file path. +/// +/// If a `file_hash` is passed, it will verify it at the end. +/// +/// ## Details +/// +/// 1) A [`Metadata`] file is created or opened in `{target_dir}/download/{filename}.metadata`. It +/// tracks the download progress including total file size, downloaded bytes, chunk sizes, and +/// ranges that still need downloading. Allows for resumability. +/// 2) The target file is preallocated with the total size of the file in +/// `{target_dir}/download/{filename}`. +/// 3) Multiple `workers` are spawned for downloading of specific chunks of the file. +/// 4) `Orchestrator` manages workers, distributes chunk ranges, and ensures the download progresses +/// efficiently by dynamically assigning tasks to workers as they become available. +/// 5) Once the file is downloaded: +/// * If `file_hash` is `Some`, verifies its blake3 hash. +/// * Deletes the metadata file +/// * Moves downloaded file to target directory. +pub async fn fetch( + filename: &str, + target_dir: &Path, + url: &str, + mut concurrent: u64, + file_hash: Option, +) -> Result<(), DownloaderError> { + // Create a temporary directory to download files to, before moving them to target_dir. + let download_dir = target_dir.join("download"); + reth_fs_util::create_dir_all(&download_dir)?; + + let data_file = download_dir.join(filename); + let mut metadata = metadata(&data_file, url).await?; + if metadata.is_done() { + return Ok(()) + } + + // Ensure the file is preallocated so we can download it concurrently + { + let file = OpenOptions::new() + .create(true) + .truncate(true) + .read(true) + .write(true) + .open(&data_file)?; + + if file.metadata()?.len() as usize != metadata.total_size { + info!(target: "sync::stages::s3::downloader", ?filename, length = metadata.total_size, "Preallocating space."); + file.set_len(metadata.total_size as u64)?; + } + } + + while !metadata.is_done() { + info!(target: "sync::stages::s3::downloader", ?filename, "Downloading."); + + // Find the missing file chunks and the minimum number of workers required + let missing_chunks = metadata.needed_ranges(); + concurrent = concurrent + .min(std::thread::available_parallelism()?.get() as u64) + .min(missing_chunks.len() as u64); + + let mut orchestrator_rx = spawn_workers(url, concurrent, &data_file); + + let mut workers = HashMap::new(); + let mut missing_chunks = missing_chunks.into_iter(); + + // Distribute chunk ranges to workers when they free up + while let Some(worker_msg) = orchestrator_rx.recv().await { + debug!(target: "sync::stages::s3::downloader", ?worker_msg, "received message from worker"); + + let available_worker = match worker_msg { + WorkerResponse::Ready { worker_id, tx } => { + debug!(target: "sync::stages::s3::downloader", ?worker_id, "Worker ready."); + workers.insert(worker_id, tx); + worker_id + } + WorkerResponse::DownloadedChunk { worker_id, chunk_index, written_bytes } => { + metadata.update_chunk(chunk_index, written_bytes)?; + worker_id + } + WorkerResponse::Err { worker_id, error } => { + error!(target: "sync::stages::s3::downloader", ?worker_id, "Worker found an error: {:?}", error); + return Err(error) + } + }; + + let msg = if let Some(RemainingChunkRange { index, start, end }) = missing_chunks.next() + { + debug!(target: "sync::stages::s3::downloader", ?available_worker, start, end, "Worker download request."); + WorkerRequest::Download { chunk_index: index, start, end } + } else { + debug!(target: "sync::stages::s3::downloader", ?available_worker, "Sent Finish command to worker."); + WorkerRequest::Finish + }; + + let _ = workers.get(&available_worker).expect("should exist").send(msg); + } + } + + if let Some(file_hash) = file_hash { + info!(target: "sync::stages::s3::downloader", ?filename, "Checking file integrity."); + check_file_hash(&data_file, &file_hash)?; + } + + // No longer need the metadata file. + metadata.delete()?; + + // Move downloaded file to desired directory. + let file_directory = target_dir.join(filename); + reth_fs_util::rename(data_file, &file_directory)?; + info!(target: "sync::stages::s3::downloader", ?file_directory, "Moved file from temporary to target directory."); + + Ok(()) +} + +/// Creates a metadata file used to keep track of the downloaded chunks. Useful on resuming after a +/// shutdown. +async fn metadata(data_file: &Path, url: &str) -> Result { + if Metadata::file_path(data_file).exists() { + debug!(target: "sync::stages::s3::downloader", ?data_file, "Loading metadata "); + return Metadata::load(data_file) + } + + let client = Client::new(); + let resp = client.head(url).send().await?; + let total_length: usize = resp + .headers() + .get(CONTENT_LENGTH) + .and_then(|v| v.to_str().ok()) + .and_then(|s| s.parse().ok()) + .ok_or(DownloaderError::EmptyContentLength)?; + + debug!(target: "sync::stages::s3::downloader", ?data_file, "Creating metadata "); + + Metadata::builder(data_file).with_total_size(total_length).build() +} + +/// Ensures the file on path has the expected blake3 hash. +fn check_file_hash(path: &Path, expected: &B256) -> Result<(), DownloaderError> { + let mut reader = BufReader::new(File::open(path)?); + let mut hasher = blake3::Hasher::new(); + std::io::copy(&mut reader, &mut hasher)?; + + let file_hash = hasher.finalize(); + if file_hash.as_bytes() != expected { + return Err(DownloaderError::InvalidFileHash(file_hash.as_bytes().into(), *expected)) + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use alloy_primitives::b256; + + #[tokio::test] + async fn test_download() { + reth_tracing::init_test_tracing(); + + let b3sum = b256!("81a7318f69fc1d6bb0a58a24af302f3b978bc75a435e4ae5d075f999cd060cfd"); + let url = "https://link.testfile.org/500MB"; + + let file = tempfile::NamedTempFile::new().unwrap(); + let filename = file.path().file_name().unwrap().to_str().unwrap(); + let target_dir = file.path().parent().unwrap(); + fetch(filename, target_dir, url, 4, Some(b3sum)).await.unwrap(); + } +} diff --git a/crates/stages/stages/src/stages/s3/downloader/meta.rs b/crates/stages/stages/src/stages/s3/downloader/meta.rs new file mode 100644 index 000000000000..7ff4213fffc3 --- /dev/null +++ b/crates/stages/stages/src/stages/s3/downloader/meta.rs @@ -0,0 +1,195 @@ +use super::{error::DownloaderError, RemainingChunkRange}; +use serde::{Deserialize, Serialize}; +use std::{ + fs::File, + ops::RangeInclusive, + path::{Path, PathBuf}, +}; +use tracing::info; + +/// Tracks download progress and manages chunked downloads for resumable file transfers. +#[derive(Debug)] +pub struct Metadata { + /// Total file size + pub total_size: usize, + /// Total file size + pub downloaded: usize, + /// Download chunk size. Default 150MB. + pub chunk_size: usize, + /// Remaining download ranges for each chunk. + /// - `Some(RangeInclusive)`: range to be downloaded. + /// - `None`: Chunk fully downloaded. + chunks: Vec>>, + /// Path with the stored metadata. + path: PathBuf, +} + +impl Metadata { + /// Build a [`Metadata`] using a builder. + pub fn builder(data_file: &Path) -> MetadataBuilder { + MetadataBuilder::new(Self::file_path(data_file)) + } + + /// Returns the metadata file path of a data file: `{data_file}.metadata` + pub fn file_path(data_file: &Path) -> PathBuf { + data_file.with_file_name(format!( + "{}.metadata", + data_file.file_name().unwrap_or_default().to_string_lossy() + )) + } + + /// Returns a list of all chunks with their remaining ranges to be downloaded: + /// `RemainingChunkRange`. + pub fn needed_ranges(&self) -> Vec { + self.chunks + .iter() + .enumerate() + .filter(|(_, remaining)| remaining.is_some()) + .map(|(index, remaining)| { + let range = remaining.as_ref().expect("qed"); + RemainingChunkRange { index, start: *range.start(), end: *range.end() } + }) + .collect() + } + + /// Updates a downloaded chunk. + pub fn update_chunk( + &mut self, + index: usize, + downloaded_bytes: usize, + ) -> Result<(), DownloaderError> { + self.downloaded += downloaded_bytes; + + let num_chunks = self.chunks.len(); + if index >= self.chunks.len() { + return Err(DownloaderError::InvalidChunk(index, num_chunks)) + } + + // Update chunk with downloaded range + if let Some(range) = &self.chunks[index] { + let start = range.start() + downloaded_bytes; + if start > *range.end() { + self.chunks[index] = None; + } else { + self.chunks[index] = Some(start..=*range.end()); + } + } + + let file = self.path.file_stem().unwrap_or_default().to_string_lossy().into_owned(); + info!( + target: "sync::stages::s3::downloader", + file, + "{}/{}", self.downloaded / 1024 / 1024, self.total_size / 1024 / 1024); + + self.commit() + } + + /// Commits the [`Metadata`] to file. + pub fn commit(&self) -> Result<(), DownloaderError> { + Ok(reth_fs_util::atomic_write_file(&self.path, |file| { + bincode::serialize_into(file, &MetadataFile::from(self)) + })?) + } + + /// Loads a [`Metadata`] file from disk using the target data file. + pub fn load(data_file: &Path) -> Result { + let metadata_file_path = Self::file_path(data_file); + let MetadataFile { total_size, downloaded, chunk_size, chunks } = + bincode::deserialize_from(File::open(&metadata_file_path)?)?; + + Ok(Self { total_size, downloaded, chunk_size, chunks, path: metadata_file_path }) + } + + /// Returns true if we have downloaded all chunks. + pub fn is_done(&self) -> bool { + !self.chunks.iter().any(|c| c.is_some()) + } + + /// Deletes [`Metadata`] file from disk. + pub fn delete(self) -> Result<(), DownloaderError> { + Ok(reth_fs_util::remove_file(&self.path)?) + } +} + +/// A builder that can configure [Metadata] +#[derive(Debug)] +pub struct MetadataBuilder { + /// Path with the stored metadata. + metadata_path: PathBuf, + /// Total file size + total_size: Option, + /// Download chunk size. Default 150MB. + chunk_size: usize, +} + +impl MetadataBuilder { + const fn new(metadata_path: PathBuf) -> Self { + Self { + metadata_path, + total_size: None, + chunk_size: 150 * (1024 * 1024), // 150MB + } + } + + pub const fn with_total_size(mut self, total_size: usize) -> Self { + self.total_size = Some(total_size); + self + } + + pub const fn with_chunk_size(mut self, chunk_size: usize) -> Self { + self.chunk_size = chunk_size; + self + } + + /// Returns a [Metadata] if + pub fn build(&self) -> Result { + match &self.total_size { + Some(total_size) if *total_size > 0 => { + let chunks = (0..*total_size) + .step_by(self.chunk_size) + .map(|start| { + Some(start..=(start + self.chunk_size).min(*total_size).saturating_sub(1)) + }) + .collect(); + + let metadata = Metadata { + path: self.metadata_path.clone(), + total_size: *total_size, + downloaded: 0, + chunk_size: self.chunk_size, + chunks, + }; + metadata.commit()?; + + Ok(metadata) + } + _ => Err(DownloaderError::InvalidMetadataTotalSize(self.total_size)), + } + } +} + +/// Helper type that can serialize and deserialize [`Metadata`] to disk. +#[derive(Debug, Serialize, Deserialize)] +struct MetadataFile { + /// Total file size + total_size: usize, + /// Total file size + downloaded: usize, + /// Download chunk size. Default 150MB. + chunk_size: usize, + /// Remaining download ranges for each chunk. + /// - `Some(RangeInclusive)`: range to be downloaded. + /// - `None`: Chunk fully downloaded. + chunks: Vec>>, +} + +impl From<&Metadata> for MetadataFile { + fn from(metadata: &Metadata) -> Self { + Self { + total_size: metadata.total_size, + downloaded: metadata.downloaded, + chunk_size: metadata.chunk_size, + chunks: metadata.chunks.clone(), + } + } +} diff --git a/crates/stages/stages/src/stages/s3/downloader/mod.rs b/crates/stages/stages/src/stages/s3/downloader/mod.rs new file mode 100644 index 000000000000..d42c8251a079 --- /dev/null +++ b/crates/stages/stages/src/stages/s3/downloader/mod.rs @@ -0,0 +1,38 @@ +//! Provides functionality for downloading files in chunks from a remote source. It supports +//! concurrent downloads, resuming interrupted downloads, and file integrity verification. + +mod error; +mod fetch; +mod meta; +mod worker; + +pub(crate) use error::DownloaderError; +pub use fetch::fetch; +pub use meta::Metadata; + +/// Response sent by the fetch task to `S3Stage` once it has downloaded all files of a block +/// range. +pub(crate) enum S3DownloaderResponse { + /// A new block range was downloaded. + AddedNewRange, + /// The last requested block range was downloaded. + Done, +} + +impl S3DownloaderResponse { + /// Whether the downloaded block range is the last requested one. + pub(crate) const fn is_done(&self) -> bool { + matches!(self, Self::Done) + } +} + +/// Chunk nth remaining range to be downloaded. +#[derive(Debug)] +pub struct RemainingChunkRange { + /// The nth chunk + pub index: usize, + /// Start of range + pub start: usize, + /// End of range + pub end: usize, +} diff --git a/crates/stages/stages/src/stages/s3/downloader/worker.rs b/crates/stages/stages/src/stages/s3/downloader/worker.rs new file mode 100644 index 000000000000..a5300a0b2471 --- /dev/null +++ b/crates/stages/stages/src/stages/s3/downloader/worker.rs @@ -0,0 +1,110 @@ +use super::error::DownloaderError; +use reqwest::{header::RANGE, Client}; +use std::path::{Path, PathBuf}; +use tokio::{ + fs::OpenOptions, + io::{AsyncSeekExt, AsyncWriteExt, BufWriter}, + sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, +}; +use tracing::debug; + +/// Responses sent by a worker. +#[derive(Debug)] +pub(crate) enum WorkerResponse { + /// Worker has been spawned and awaiting work. + Ready { worker_id: u64, tx: UnboundedSender }, + /// Worker has downloaded + DownloadedChunk { worker_id: u64, chunk_index: usize, written_bytes: usize }, + /// Worker has encountered an error. + Err { worker_id: u64, error: DownloaderError }, +} + +/// Requests sent to a worker. +#[derive(Debug)] +pub(crate) enum WorkerRequest { + /// Requests a range to be downloaded. + Download { chunk_index: usize, start: usize, end: usize }, + /// Signals a worker exit. + Finish, +} + +/// Spawns the requested number of workers and returns a `UnboundedReceiver` that all of them will +/// respond to. +pub(crate) fn spawn_workers( + url: &str, + worker_count: u64, + data_file: &Path, +) -> UnboundedReceiver { + // Create channels for communication between workers and orchestrator + let (orchestrator_tx, orchestrator_rx) = unbounded_channel(); + + // Initiate workers + for worker_id in 0..worker_count { + let orchestrator_tx = orchestrator_tx.clone(); + let data_file = data_file.to_path_buf(); + let url = url.to_string(); + debug!(target: "sync::stages::s3::downloader", ?worker_id, "Spawning."); + + tokio::spawn(async move { + if let Err(error) = worker_fetch(worker_id, &orchestrator_tx, data_file, url).await { + let _ = orchestrator_tx.send(WorkerResponse::Err { worker_id, error }); + } + }); + } + + orchestrator_rx +} + +/// Downloads requested chunk ranges to the data file. +async fn worker_fetch( + worker_id: u64, + orchestrator_tx: &UnboundedSender, + data_file: PathBuf, + url: String, +) -> Result<(), DownloaderError> { + let client = Client::new(); + let mut data_file = BufWriter::new(OpenOptions::new().write(true).open(data_file).await?); + + // Signals readiness to download + let (tx, mut rx) = unbounded_channel::(); + orchestrator_tx.send(WorkerResponse::Ready { worker_id, tx }).unwrap_or_else(|_| { + debug!("Failed to notify orchestrator of readiness"); + }); + + while let Some(req) = rx.recv().await { + debug!( + target: "sync::stages::s3::downloader", + worker_id, + ?req, + "received from orchestrator" + ); + + match req { + WorkerRequest::Download { chunk_index, start, end } => { + data_file.seek(tokio::io::SeekFrom::Start(start as u64)).await?; + + let mut response = client + .get(&url) + .header(RANGE, format!("bytes={}-{}", start, end)) + .send() + .await?; + + let mut written_bytes = 0; + while let Some(chunk) = response.chunk().await? { + written_bytes += chunk.len(); + data_file.write_all(&chunk).await?; + } + data_file.flush().await?; + + let _ = orchestrator_tx.send(WorkerResponse::DownloadedChunk { + worker_id, + chunk_index, + written_bytes, + }); + } + WorkerRequest::Finish => break, + } + } + + Ok(()) +} diff --git a/crates/stages/stages/src/stages/s3/filelist.rs b/crates/stages/stages/src/stages/s3/filelist.rs new file mode 100644 index 000000000000..683c4a208862 --- /dev/null +++ b/crates/stages/stages/src/stages/s3/filelist.rs @@ -0,0 +1,21 @@ +use alloy_primitives::B256; + +/// File list to be downloaded with their hashes. +pub(crate) static DOWNLOAD_FILE_LIST: [[(&str, B256); 3]; 2] = [ + [ + ("static_file_transactions_0_499999", B256::ZERO), + ("static_file_transactions_0_499999.off", B256::ZERO), + ("static_file_transactions_0_499999.conf", B256::ZERO), + // ("static_file_blockmeta_0_499999", B256::ZERO), + // ("static_file_blockmeta_0_499999.off", B256::ZERO), + // ("static_file_blockmeta_0_499999.conf", B256::ZERO), + ], + [ + ("static_file_transactions_500000_999999", B256::ZERO), + ("static_file_transactions_500000_999999.off", B256::ZERO), + ("static_file_transactions_500000_999999.conf", B256::ZERO), + // ("static_file_blockmeta_500000_999999", B256::ZERO), + // ("static_file_blockmeta_500000_999999.off", B256::ZERO), + // ("static_file_blockmeta_500000_999999.conf", B256::ZERO), + ], +]; diff --git a/crates/stages/stages/src/stages/s3/mod.rs b/crates/stages/stages/src/stages/s3/mod.rs new file mode 100644 index 000000000000..18e8a99c7162 --- /dev/null +++ b/crates/stages/stages/src/stages/s3/mod.rs @@ -0,0 +1,294 @@ +mod downloader; +pub use downloader::{fetch, Metadata}; +use downloader::{DownloaderError, S3DownloaderResponse}; + +mod filelist; +use filelist::DOWNLOAD_FILE_LIST; + +use reth_db::transaction::DbTxMut; +use reth_primitives::StaticFileSegment; +use reth_provider::{ + DBProvider, StageCheckpointReader, StageCheckpointWriter, StaticFileProviderFactory, +}; +use reth_stages_api::{ + ExecInput, ExecOutput, Stage, StageCheckpoint, StageError, StageId, UnwindInput, UnwindOutput, +}; +use std::{ + path::PathBuf, + task::{ready, Context, Poll}, +}; +use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver}; + +/// S3 `StageId` +const S3_STAGE_ID: StageId = StageId::Other("S3"); + +/// The S3 stage. +#[derive(Default, Debug)] +#[non_exhaustive] +pub struct S3Stage { + /// Static file directory. + static_file_directory: PathBuf, + /// Remote server URL. + url: String, + /// Maximum number of connections per download. + max_concurrent_requests: u64, + /// Channel to receive the downloaded ranges from the fetch task. + fetch_rx: Option>>, +} + +impl Stage for S3Stage +where + Provider: DBProvider + + StaticFileProviderFactory + + StageCheckpointReader + + StageCheckpointWriter, +{ + fn id(&self) -> StageId { + S3_STAGE_ID + } + + fn poll_execute_ready( + &mut self, + cx: &mut Context<'_>, + input: ExecInput, + ) -> Poll> { + loop { + // We are currently fetching and may have downloaded ranges that we can process. + if let Some(rx) = &mut self.fetch_rx { + // Whether we have downloaded all the required files. + let mut is_done = false; + + let response = match ready!(rx.poll_recv(cx)) { + Some(Ok(response)) => { + is_done = response.is_done(); + Ok(()) + } + Some(Err(_)) => todo!(), // TODO: DownloaderError -> StageError + None => Err(StageError::ChannelClosed), + }; + + if is_done { + self.fetch_rx = None; + } + + return Poll::Ready(response) + } + + // Spawns the downloader task if there are any missing files + if let Some(fetch_rx) = self.maybe_spawn_fetch(input) { + self.fetch_rx = Some(fetch_rx); + + // Polls fetch_rx & registers waker + continue + } + + // No files to be downloaded + return Poll::Ready(Ok(())) + } + } + + fn execute(&mut self, provider: &Provider, input: ExecInput) -> Result + where + Provider: DBProvider + + StaticFileProviderFactory + + StageCheckpointReader + + StageCheckpointWriter, + { + // Re-initializes the provider to detect the new additions + provider.static_file_provider().initialize_index()?; + + // TODO logic for appending tx_block + + // let (_, _to_block) = input.next_block_range().into_inner(); + // let static_file_provider = provider.static_file_provider(); + // let mut _tx_block_cursor = + // provider.tx_ref().cursor_write::()?; + + // tx_block_cursor.append(indice.last_tx_num(), &block_number)?; + + // let checkpoint = StageCheckpoint { block_number: highest_block, stage_checkpoint: None }; + // provider.save_stage_checkpoint(StageId::Bodies, checkpoint)?; + // provider.save_stage_checkpoint(S3_STAGE_ID, checkpoint)?; + + // // TODO: verify input.target according to s3 stage specifications + // let done = highest_block == to_block; + + Ok(ExecOutput { checkpoint: StageCheckpoint::new(input.target()), done: true }) + } + + fn unwind( + &mut self, + _provider: &Provider, + input: UnwindInput, + ) -> Result { + // TODO + Ok(UnwindOutput { checkpoint: StageCheckpoint::new(input.unwind_to) }) + } +} + +impl S3Stage { + /// It will only spawn a task to fetch files from the remote server, it there are any missing + /// static files. + /// + /// Every time a block range is ready with all the necessary files, it sends a + /// [`S3DownloaderResponse`] to `self.fetch_rx`. If it's the last requested block range, the + /// response will have `is_done` set to true. + fn maybe_spawn_fetch( + &self, + input: ExecInput, + ) -> Option>> { + let checkpoint = input.checkpoint(); + // TODO: input target can only be certain numbers. eg. 499_999 , 999_999 etc. + + // Create a list of all the missing files per block range that need to be downloaded. + let mut requests = vec![]; + for block_range_files in &DOWNLOAD_FILE_LIST { + let (_, block_range) = + StaticFileSegment::parse_filename(block_range_files[0].0).expect("qed"); + + if block_range.end() <= checkpoint.block_number { + continue + } + + let mut block_range_requests = vec![]; + for (filename, file_hash) in block_range_files { + // If the file already exists, then we are resuming a previously interrupted stage + // run. + if self.static_file_directory.join(filename).exists() { + // TODO: check hash if the file already exists + continue + } + + block_range_requests.push((filename, file_hash)); + } + + requests.push((block_range, block_range_requests)); + } + + // Return None, if we have downloaded all the files that are required. + if requests.is_empty() { + return None + } + + let static_file_directory = self.static_file_directory.clone(); + let url = self.url.clone(); + let max_concurrent_requests = self.max_concurrent_requests; + + let (fetch_tx, fetch_rx) = unbounded_channel(); + tokio::spawn(async move { + let mut requests_iter = requests.into_iter().peekable(); + + while let Some((_, file_requests)) = requests_iter.next() { + for (filename, file_hash) in file_requests { + if let Err(err) = fetch( + filename, + &static_file_directory, + &format!("{}/{filename}", url), + max_concurrent_requests, + Some(*file_hash), + ) + .await + { + let _ = fetch_tx.send(Err(err)); + return + } + } + + let response = if requests_iter.peek().is_none() { + S3DownloaderResponse::Done + } else { + S3DownloaderResponse::AddedNewRange + }; + + let _ = fetch_tx.send(Ok(response)); + } + }); + + Some(fetch_rx) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_utils::{ + ExecuteStageTestRunner, StageTestRunner, TestRunnerError, TestStageDB, + UnwindStageTestRunner, + }; + use reth_primitives::SealedHeader; + use reth_testing_utils::{ + generators, + generators::{random_header, random_header_range}, + }; + + // stage_test_suite_ext!(S3TestRunner, s3); + + #[derive(Default)] + struct S3TestRunner { + db: TestStageDB, + } + + impl StageTestRunner for S3TestRunner { + type S = S3Stage; + + fn db(&self) -> &TestStageDB { + &self.db + } + + fn stage(&self) -> Self::S { + S3Stage::default() + } + } + + impl ExecuteStageTestRunner for S3TestRunner { + type Seed = Vec; + + fn seed_execution(&mut self, input: ExecInput) -> Result { + let start = input.checkpoint().block_number; + let mut rng = generators::rng(); + let head = random_header(&mut rng, start, None); + self.db.insert_headers_with_td(std::iter::once(&head))?; + + // use previous progress as seed size + let end = input.target.unwrap_or_default() + 1; + + if start + 1 >= end { + return Ok(Vec::default()) + } + + let mut headers = random_header_range(&mut rng, start + 1..end, head.hash()); + self.db.insert_headers_with_td(headers.iter())?; + headers.insert(0, head); + Ok(headers) + } + + fn validate_execution( + &self, + input: ExecInput, + output: Option, + ) -> Result<(), TestRunnerError> { + if let Some(output) = output { + assert!(output.done, "stage should always be done"); + assert_eq!( + output.checkpoint.block_number, + input.target(), + "stage progress should always match progress of previous stage" + ); + } + Ok(()) + } + } + + impl UnwindStageTestRunner for S3TestRunner { + fn validate_unwind(&self, _input: UnwindInput) -> Result<(), TestRunnerError> { + Ok(()) + } + } + + #[test] + fn parse_files() { + for block_range_files in &DOWNLOAD_FILE_LIST { + let (_, _) = StaticFileSegment::parse_filename(block_range_files[0].0).expect("qed"); + } + } +} diff --git a/crates/stages/stages/src/stages/sender_recovery.rs b/crates/stages/stages/src/stages/sender_recovery.rs index 34598714a18b..05e0230e0b17 100644 --- a/crates/stages/stages/src/stages/sender_recovery.rs +++ b/crates/stages/stages/src/stages/sender_recovery.rs @@ -313,9 +313,9 @@ fn recover_sender( // value is greater than `secp256k1n / 2` if past EIP-2. There are transactions // pre-homestead which have large `s` values, so using [Signature::recover_signer] here // would not be backwards-compatible. - let sender = tx - .recover_signer_unchecked_with_buf(rlp_buf) - .ok_or(SenderRecoveryStageError::FailedRecovery(FailedSenderRecoveryError { tx: tx_id }))?; + let sender = tx.recover_signer_unchecked_with_buf(rlp_buf).map_err(|_| { + SenderRecoveryStageError::FailedRecovery(FailedSenderRecoveryError { tx: tx_id }) + })?; Ok((tx_id, sender)) } diff --git a/crates/storage/db-api/src/database_metrics.rs b/crates/storage/db-api/src/database_metrics.rs index 8ca6b35bc4e9..88a2f4b5bd38 100644 --- a/crates/storage/db-api/src/database_metrics.rs +++ b/crates/storage/db-api/src/database_metrics.rs @@ -40,35 +40,3 @@ impl DatabaseMetrics for Arc { ::report_metrics(self) } } - -/// The type used to store metadata about the database. -#[derive(Debug, Default)] -pub struct DatabaseMetadataValue { - /// The freelist size - freelist_size: Option, -} - -impl DatabaseMetadataValue { - /// Creates a new [`DatabaseMetadataValue`] with the given freelist size. - pub const fn new(freelist_size: Option) -> Self { - Self { freelist_size } - } - - /// Returns the freelist size, if available. - pub const fn freelist_size(&self) -> Option { - self.freelist_size - } -} - -/// Includes a method to return a [`DatabaseMetadataValue`] type, which can be used to dynamically -/// retrieve information about the database. -pub trait DatabaseMetadata { - /// Returns a metadata type, [`DatabaseMetadataValue`] for the database. - fn metadata(&self) -> DatabaseMetadataValue; -} - -impl DatabaseMetadata for Arc { - fn metadata(&self) -> DatabaseMetadataValue { - ::metadata(self) - } -} diff --git a/crates/storage/db-api/src/models/mod.rs b/crates/storage/db-api/src/models/mod.rs index 232e257a1dc8..19e3600dbfec 100644 --- a/crates/storage/db-api/src/models/mod.rs +++ b/crates/storage/db-api/src/models/mod.rs @@ -25,7 +25,8 @@ pub use accounts::*; pub use blocks::*; pub use integer_list::IntegerList; pub use reth_db_models::{ - AccountBeforeTx, ClientVersion, StoredBlockBodyIndices, StoredBlockWithdrawals, + blocks::StaticFileBlockWithdrawals, AccountBeforeTx, ClientVersion, StoredBlockBodyIndices, + StoredBlockWithdrawals, }; pub use sharded_key::ShardedKey; @@ -224,6 +225,7 @@ impl_compression_for_compact!( StoredBlockBodyIndices, StoredBlockOmmers, StoredBlockWithdrawals, + StaticFileBlockWithdrawals, Bytecode, AccountBeforeTx, TransactionSigned, diff --git a/crates/storage/db-models/src/blocks.rs b/crates/storage/db-models/src/blocks.rs index be7661c8b123..c87316d0e4e8 100644 --- a/crates/storage/db-models/src/blocks.rs +++ b/crates/storage/db-models/src/blocks.rs @@ -1,9 +1,9 @@ -use std::ops::Range; - use alloy_eips::eip4895::Withdrawals; use alloy_primitives::TxNumber; +use bytes::Buf; use reth_codecs::{add_arbitrary_tests, Compact}; use serde::{Deserialize, Serialize}; +use std::ops::Range; /// Total number of transactions. pub type NumTransactions = u64; @@ -76,6 +76,37 @@ pub struct StoredBlockWithdrawals { pub withdrawals: Withdrawals, } +/// A storage representation of block withdrawals that is static file friendly. An inner `None` +/// represents a pre-merge block. +#[derive(Debug, Default, Eq, PartialEq, Clone, Serialize, Deserialize)] +#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))] +#[add_arbitrary_tests(compact)] +pub struct StaticFileBlockWithdrawals { + /// The block withdrawals. A `None` value represents a pre-merge block. + pub withdrawals: Option, +} + +impl Compact for StaticFileBlockWithdrawals { + fn to_compact(&self, buf: &mut B) -> usize + where + B: bytes::BufMut + AsMut<[u8]>, + { + buf.put_u8(self.withdrawals.is_some() as u8); + if let Some(withdrawals) = &self.withdrawals { + return 1 + withdrawals.to_compact(buf); + } + 1 + } + fn from_compact(mut buf: &[u8], _: usize) -> (Self, &[u8]) { + if buf.get_u8() == 1 { + let (w, buf) = Withdrawals::from_compact(buf, buf.len()); + (Self { withdrawals: Some(w) }, buf) + } else { + (Self { withdrawals: None }, buf) + } + } +} + #[cfg(test)] mod tests { use crate::StoredBlockBodyIndices; diff --git a/crates/storage/db-models/src/lib.rs b/crates/storage/db-models/src/lib.rs index b8595362afc3..fd97574a646c 100644 --- a/crates/storage/db-models/src/lib.rs +++ b/crates/storage/db-models/src/lib.rs @@ -6,7 +6,7 @@ pub use accounts::AccountBeforeTx; /// Blocks pub mod blocks; -pub use blocks::{StoredBlockBodyIndices, StoredBlockWithdrawals}; +pub use blocks::{StaticFileBlockWithdrawals, StoredBlockBodyIndices, StoredBlockWithdrawals}; /// Client Version pub mod client_version; diff --git a/crates/storage/db/Cargo.toml b/crates/storage/db/Cargo.toml index 3dc1bb138d89..f09f1cbddad8 100644 --- a/crates/storage/db/Cargo.toml +++ b/crates/storage/db/Cargo.toml @@ -15,7 +15,7 @@ workspace = true # reth reth-db-api.workspace = true reth-primitives = { workspace = true, features = ["reth-codec"] } -reth-primitives-traits = { workspace = true, features = ["serde", "reth-codec"] } +reth-primitives-traits = { workspace = true, features = ["reth-codec"] } reth-fs-util.workspace = true reth-storage-errors.workspace = true reth-nippy-jar.workspace = true @@ -73,8 +73,6 @@ criterion.workspace = true arbitrary = { workspace = true, features = ["derive"] } proptest.workspace = true -paste.workspace = true - assert_matches.workspace = true [features] diff --git a/crates/storage/db/src/implementation/mdbx/mod.rs b/crates/storage/db/src/implementation/mdbx/mod.rs index d2e0d91b1d23..a41388b572cb 100644 --- a/crates/storage/db/src/implementation/mdbx/mod.rs +++ b/crates/storage/db/src/implementation/mdbx/mod.rs @@ -12,7 +12,7 @@ use metrics::{gauge, Label}; use reth_db_api::{ cursor::{DbCursorRO, DbCursorRW}, database::Database, - database_metrics::{DatabaseMetadata, DatabaseMetadataValue, DatabaseMetrics}, + database_metrics::DatabaseMetrics, models::ClientVersion, transaction::{DbTx, DbTxMut}, }; @@ -50,7 +50,7 @@ const DEFAULT_MAX_READERS: u64 = 32_000; const MAX_SAFE_READER_SPACE: usize = 10 * GIGABYTE; /// Environment used when opening a MDBX environment. RO/RW. -#[derive(Debug)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum DatabaseEnvKind { /// Read-only MDBX environment. RO, @@ -276,12 +276,6 @@ impl DatabaseMetrics for DatabaseEnv { } } -impl DatabaseMetadata for DatabaseEnv { - fn metadata(&self) -> DatabaseMetadataValue { - DatabaseMetadataValue::new(self.freelist().ok()) - } -} - impl DatabaseEnv { /// Opens the database at the specified path with the given `EnvKind`. /// diff --git a/crates/storage/db/src/lib.rs b/crates/storage/db/src/lib.rs index 7e6a6932bdd7..ff740375e95f 100644 --- a/crates/storage/db/src/lib.rs +++ b/crates/storage/db/src/lib.rs @@ -46,9 +46,7 @@ pub mod test_utils { use crate::mdbx::DatabaseArguments; use parking_lot::RwLock; use reth_db_api::{ - database::Database, - database_metrics::{DatabaseMetadata, DatabaseMetadataValue, DatabaseMetrics}, - models::ClientVersion, + database::Database, database_metrics::DatabaseMetrics, models::ClientVersion, }; use reth_fs_util; use reth_libmdbx::MaxReadTransactionDuration; @@ -155,12 +153,6 @@ pub mod test_utils { } } - impl DatabaseMetadata for TempDatabase { - fn metadata(&self) -> DatabaseMetadataValue { - self.db().metadata() - } - } - /// Create `static_files` path for testing #[track_caller] pub fn create_test_static_files_dir() -> (TempDir, PathBuf) { diff --git a/crates/storage/db/src/mdbx.rs b/crates/storage/db/src/mdbx.rs index c0e11079f3ae..9042299afdc6 100644 --- a/crates/storage/db/src/mdbx.rs +++ b/crates/storage/db/src/mdbx.rs @@ -1,4 +1,4 @@ -//! Bindings for [MDBX](https://libmdbx.dqdkfa.ru/). +//! Helper functions for initializing and opening a database. use crate::{is_database_empty, TableSet, Tables}; use eyre::Context; @@ -48,16 +48,24 @@ pub fn init_db_for, TS: TableSet>( } /// Opens up an existing database. Read only mode. It doesn't create it or create tables if missing. -pub fn open_db_read_only(path: &Path, args: DatabaseArguments) -> eyre::Result { +pub fn open_db_read_only( + path: impl AsRef, + args: DatabaseArguments, +) -> eyre::Result { + let path = path.as_ref(); DatabaseEnv::open(path, DatabaseEnvKind::RO, args) .with_context(|| format!("Could not open database at path: {}", path.display())) } /// Opens up an existing database. Read/Write mode with `WriteMap` enabled. It doesn't create it or /// create tables if missing. -pub fn open_db(path: &Path, args: DatabaseArguments) -> eyre::Result { - let db = DatabaseEnv::open(path, DatabaseEnvKind::RW, args.clone()) - .with_context(|| format!("Could not open database at path: {}", path.display()))?; - db.record_client_version(args.client_version().clone())?; - Ok(db) +pub fn open_db(path: impl AsRef, args: DatabaseArguments) -> eyre::Result { + fn open(path: &Path, args: DatabaseArguments) -> eyre::Result { + let client_version = args.client_version().clone(); + let db = DatabaseEnv::open(path, DatabaseEnvKind::RW, args) + .with_context(|| format!("Could not open database at path: {}", path.display()))?; + db.record_client_version(client_version)?; + Ok(db) + } + open(path.as_ref(), args) } diff --git a/crates/storage/db/src/static_file/masks.rs b/crates/storage/db/src/static_file/masks.rs index f89a0eac1d4e..6dbc384fab86 100644 --- a/crates/storage/db/src/static_file/masks.rs +++ b/crates/storage/db/src/static_file/masks.rs @@ -1,10 +1,13 @@ use crate::{ add_static_file_mask, static_file::mask::{ColumnSelectorOne, ColumnSelectorTwo}, - BlockBodyIndices, BlockWithdrawals, HeaderTerminalDifficulties, + BlockBodyIndices, HeaderTerminalDifficulties, }; use alloy_primitives::BlockHash; -use reth_db_api::{models::StoredBlockOmmers, table::Table}; +use reth_db_api::{ + models::{StaticFileBlockWithdrawals, StoredBlockOmmers}, + table::Table, +}; // HEADER MASKS add_static_file_mask! { @@ -53,6 +56,6 @@ add_static_file_mask! { OmmersMask, StoredBlockOmmers, 0b010 } add_static_file_mask! { - #[doc = "Mask for a `StoredBlockWithdrawals` from BlockMeta static file segment"] - WithdrawalsMask, ::Value, 0b100 + #[doc = "Mask for a `StaticFileBlockWithdrawals` from BlockMeta static file segment"] + WithdrawalsMask, StaticFileBlockWithdrawals, 0b100 } diff --git a/crates/storage/errors/Cargo.toml b/crates/storage/errors/Cargo.toml index 10e4a1446fc0..b37c76ac5fa6 100644 --- a/crates/storage/errors/Cargo.toml +++ b/crates/storage/errors/Cargo.toml @@ -12,8 +12,9 @@ workspace = true [dependencies] # reth -reth-primitives-traits.workspace = true reth-fs-util.workspace = true +reth-primitives-traits.workspace = true +reth-prune-types.workspace = true reth-static-file-types.workspace = true # ethereum diff --git a/crates/storage/errors/src/provider.rs b/crates/storage/errors/src/provider.rs index b06e758e457d..81961a9ec222 100644 --- a/crates/storage/errors/src/provider.rs +++ b/crates/storage/errors/src/provider.rs @@ -3,7 +3,8 @@ use alloc::{boxed::Box, string::String}; use alloy_eips::{BlockHashOrNumber, HashOrNumber}; use alloy_primitives::{Address, BlockHash, BlockNumber, TxNumber, B256}; use derive_more::Display; -use reth_primitives_traits::GotExpected; +use reth_primitives_traits::{transaction::signed::RecoveryError, GotExpected}; +use reth_prune_types::PruneSegmentError; use reth_static_file_types::StaticFileSegment; /// Provider result type. @@ -15,6 +16,9 @@ pub enum ProviderError { /// Database error. #[error(transparent)] Database(#[from] DatabaseError), + /// Pruning error. + #[error(transparent)] + Pruning(#[from] PruneSegmentError), /// RLP error. #[error("{_0}")] Rlp(alloy_rlp::Error), @@ -147,6 +151,12 @@ impl From for ProviderError { } } +impl From for ProviderError { + fn from(_: RecoveryError) -> Self { + Self::SenderRecoveryError + } +} + /// A root mismatch error at a given block height. #[derive(Clone, Debug, PartialEq, Eq, Display)] #[display("root mismatch at #{block_number} ({block_hash}): {root}")] diff --git a/crates/storage/libmdbx-rs/src/txn_manager.rs b/crates/storage/libmdbx-rs/src/txn_manager.rs index 817f178cda4c..4fdaddc82081 100644 --- a/crates/storage/libmdbx-rs/src/txn_manager.rs +++ b/crates/storage/libmdbx-rs/src/txn_manager.rs @@ -110,6 +110,7 @@ mod read_transactions { }; use dashmap::{DashMap, DashSet}; use std::{ + backtrace::Backtrace, sync::{mpsc::sync_channel, Arc}, time::{Duration, Instant}, }; @@ -148,11 +149,10 @@ mod read_transactions { } /// Removes a transaction from the list of active read transactions. - pub(crate) fn remove_active_read_transaction( - &self, - ptr: *mut ffi::MDBX_txn, - ) -> Option<(usize, (TransactionPtr, Instant))> { - self.read_transactions.as_ref()?.remove_active(ptr) + /// + /// Returns `true` if the transaction was found and removed. + pub(crate) fn remove_active_read_transaction(&self, ptr: *mut ffi::MDBX_txn) -> bool { + self.read_transactions.as_ref().is_some_and(|txs| txs.remove_active(ptr)) } /// Returns the number of timed out transactions that were not aborted by the user yet. @@ -172,7 +172,10 @@ mod read_transactions { /// /// We store `usize` instead of a raw pointer as a key, because pointers are not /// comparable. The time of transaction opening is stored as a value. - active: DashMap, + /// + /// The backtrace of the transaction opening is recorded only when debug assertions are + /// enabled. + active: DashMap>)>, /// List of timed out transactions that were not aborted by the user yet, hence have a /// dangling read transaction pointer. timed_out_not_aborted: DashSet, @@ -185,16 +188,20 @@ mod read_transactions { /// Adds a new transaction to the list of active read transactions. pub(super) fn add_active(&self, ptr: *mut ffi::MDBX_txn, tx: TransactionPtr) { - let _ = self.active.insert(ptr as usize, (tx, Instant::now())); + let _ = self.active.insert( + ptr as usize, + ( + tx, + Instant::now(), + cfg!(debug_assertions).then(|| Arc::new(Backtrace::force_capture())), + ), + ); } /// Removes a transaction from the list of active read transactions. - pub(super) fn remove_active( - &self, - ptr: *mut ffi::MDBX_txn, - ) -> Option<(usize, (TransactionPtr, Instant))> { + pub(super) fn remove_active(&self, ptr: *mut ffi::MDBX_txn) -> bool { self.timed_out_not_aborted.remove(&(ptr as usize)); - self.active.remove(&(ptr as usize)) + self.active.remove(&(ptr as usize)).is_some() } /// Returns the number of timed out transactions that were not aborted by the user yet. @@ -215,7 +222,7 @@ mod read_transactions { // Iterate through active read transactions and time out those that's open for // longer than `self.max_duration`. for entry in &self.active { - let (tx, start) = entry.value(); + let (tx, start, backtrace) = entry.value(); let duration = now - *start; if duration > self.max_duration { @@ -241,10 +248,15 @@ mod read_transactions { // Add the transaction to `timed_out_active`. We can't remove it // instantly from the list of active transactions, because we // iterate through it. - timed_out_active.push((txn_ptr, duration, error)); + timed_out_active.push(( + txn_ptr, + duration, + backtrace.clone(), + error, + )); } Err(err) => { - error!(target: "libmdbx", %err, "Failed to abort the long-lived read transaction") + error!(target: "libmdbx", %err, ?backtrace, "Failed to abort the long-lived read transaction") } } } else { @@ -256,18 +268,18 @@ mod read_transactions { // Walk through timed out transactions, and delete them from the list of active // transactions. - for (ptr, open_duration, err) in timed_out_active.iter().copied() { + for (ptr, open_duration, backtrace, err) in timed_out_active.iter().cloned() { // Try deleting the transaction from the list of active transactions. - let was_in_active = self.remove_active(ptr).is_some(); + let was_in_active = self.remove_active(ptr); if let Err(err) = err { if was_in_active { // If the transaction was in the list of active transactions, // then user didn't abort it and we failed to do so. - error!(target: "libmdbx", %err, ?open_duration, "Failed to time out the long-lived read transaction"); + error!(target: "libmdbx", %err, ?open_duration, ?backtrace, "Failed to time out the long-lived read transaction"); } } else { // Happy path, the transaction has been timed out by us with no errors. - warn!(target: "libmdbx", ?open_duration, "Long-lived read transaction has been timed out"); + warn!(target: "libmdbx", ?open_duration, ?backtrace, "Long-lived read transaction has been timed out"); // Add transaction to the list of timed out transactions that were not // aborted by the user yet. self.timed_out_not_aborted.insert(ptr as usize); @@ -283,7 +295,7 @@ mod read_transactions { target: "libmdbx", elapsed = ?now.elapsed(), active = ?self.active.iter().map(|entry| { - let (tx, start) = entry.value(); + let (tx, start, _) = entry.value(); (tx.clone(), start.elapsed()) }).collect::>(), "Read transactions" diff --git a/crates/storage/provider/src/providers/blockchain_provider.rs b/crates/storage/provider/src/providers/blockchain_provider.rs index 92451fab15e7..6ef8ab5a9ae2 100644 --- a/crates/storage/provider/src/providers/blockchain_provider.rs +++ b/crates/storage/provider/src/providers/blockchain_provider.rs @@ -787,7 +787,7 @@ mod tests { use rand::Rng; use reth_chain_state::{ test_utils::TestBlockBuilder, CanonStateNotification, CanonStateSubscriptions, - CanonicalInMemoryState, ExecutedBlock, NewCanonicalChain, + CanonicalInMemoryState, ExecutedBlock, ExecutedBlockWithTrieUpdates, NewCanonicalChain, }; use reth_chainspec::{ ChainSpec, ChainSpecBuilder, ChainSpecProvider, EthereumHardfork, MAINNET, @@ -930,7 +930,7 @@ mod tests { let execution_outcome = ExecutionOutcome { receipts: block_receipts.into(), ..Default::default() }; - ExecutedBlock::new( + ExecutedBlockWithTrieUpdates::new( Arc::new(RecoveredBlock::new_sealed(block.clone(), senders)), execution_outcome.into(), Default::default(), @@ -1059,7 +1059,7 @@ mod tests { let in_memory_block_senders = first_in_mem_block.senders().expect("failed to recover senders"); let chain = NewCanonicalChain::Commit { - new: vec![ExecutedBlock::new( + new: vec![ExecutedBlockWithTrieUpdates::new( Arc::new(RecoveredBlock::new_sealed( first_in_mem_block.clone(), in_memory_block_senders, @@ -1095,13 +1095,15 @@ 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(ExecutedBlock { - recovered_block: Arc::new(RecoveredBlock::new_sealed( - last_in_mem_block.clone(), - Default::default(), - )), - execution_output: Default::default(), - hashed_state: Default::default(), + 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: Default::default(), }); @@ -1153,7 +1155,7 @@ mod tests { let in_memory_block_senders = first_in_mem_block.senders().expect("failed to recover senders"); let chain = NewCanonicalChain::Commit { - new: vec![ExecutedBlock::new( + new: vec![ExecutedBlockWithTrieUpdates::new( Arc::new(RecoveredBlock::new_sealed( first_in_mem_block.clone(), in_memory_block_senders, @@ -1207,13 +1209,15 @@ mod tests { ); // Set the block as pending - provider.canonical_in_memory_state.set_pending_block(ExecutedBlock { - recovered_block: Arc::new(RecoveredBlock::new_sealed( - block.clone(), - block.senders().unwrap(), - )), - execution_output: Default::default(), - hashed_state: Default::default(), + 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: Default::default(), }); @@ -1290,7 +1294,7 @@ mod tests { let in_memory_block_senders = first_in_mem_block.senders().expect("failed to recover senders"); let chain = NewCanonicalChain::Commit { - new: vec![ExecutedBlock::new( + new: vec![ExecutedBlockWithTrieUpdates::new( Arc::new(RecoveredBlock::new_sealed( first_in_mem_block.clone(), in_memory_block_senders, @@ -1855,7 +1859,7 @@ mod tests { .first() .map(|block| { let senders = block.senders().expect("failed to recover senders"); - ExecutedBlock::new( + ExecutedBlockWithTrieUpdates::new( Arc::new(RecoveredBlock::new_sealed(block.clone(), senders)), Arc::new(ExecutionOutcome { bundle: BundleState::new( @@ -1990,15 +1994,19 @@ 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(ExecutedBlock { - recovered_block: Arc::new(RecoveredBlock::new_sealed( - pending_block.clone(), - Default::default(), - )), - execution_output: Default::default(), - hashed_state: Default::default(), - trie: Default::default(), - }); + 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: Default::default(), + }, + ); assert_eq!( pending_block.hash(), @@ -2113,13 +2121,15 @@ mod tests { // Set the pending block in memory let pending_block = in_memory_blocks.last().unwrap(); - provider.canonical_in_memory_state.set_pending_block(ExecutedBlock { - recovered_block: Arc::new(RecoveredBlock::new_sealed( - pending_block.clone(), - Default::default(), - )), - execution_output: Default::default(), - hashed_state: Default::default(), + 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: Default::default(), }); @@ -2640,7 +2650,7 @@ mod tests { transaction_sender, |block: &SealedBlock, tx_num: TxNumber, _: B256, _: &Vec>| ( tx_num, - block.body().transactions[test_tx_index].recover_signer() + block.body().transactions[test_tx_index].recover_signer().ok() ), u64::MAX ), diff --git a/crates/storage/provider/src/providers/consistent.rs b/crates/storage/provider/src/providers/consistent.rs index 3082faf45b51..2378b5d5f530 100644 --- a/crates/storage/provider/src/providers/consistent.rs +++ b/crates/storage/provider/src/providers/consistent.rs @@ -198,7 +198,7 @@ impl ConsistentProvider { let receipt = receipt_iter .next() .ok_or_else(|| ProviderError::ReceiptNotFound(tx_num.into()))?; - block_receipts.push(Some(receipt)); + block_receipts.push(receipt); } receipts.push(block_receipts); } @@ -1065,7 +1065,7 @@ impl ReceiptProvider for ConsistentProvider { ); if let Some(tx_index) = - block.body().transactions().iter().position(|tx| tx.trie_hash() == hash) + block.body().transactions_iter().position(|tx| tx.trie_hash() == hash) { // safe to use tx_index for receipts due to 1:1 correspondence return Ok(receipts.get(tx_index).cloned()); @@ -1479,7 +1479,7 @@ mod tests { use alloy_primitives::B256; use itertools::Itertools; use rand::Rng; - use reth_chain_state::{ExecutedBlock, NewCanonicalChain}; + use reth_chain_state::{ExecutedBlock, ExecutedBlockWithTrieUpdates, NewCanonicalChain}; use reth_db::models::AccountBeforeTx; use reth_execution_types::ExecutionOutcome; use reth_primitives::{RecoveredBlock, SealedBlock}; @@ -1581,7 +1581,7 @@ mod tests { let in_memory_block_senders = first_in_mem_block.senders().expect("failed to recover senders"); let chain = NewCanonicalChain::Commit { - new: vec![ExecutedBlock::new( + new: vec![ExecutedBlockWithTrieUpdates::new( Arc::new(RecoveredBlock::new_sealed( first_in_mem_block.clone(), in_memory_block_senders, @@ -1623,13 +1623,15 @@ mod tests { ); // Insert the last block into the pending state - provider.canonical_in_memory_state.set_pending_block(ExecutedBlock { - recovered_block: Arc::new(RecoveredBlock::new_sealed( - last_in_mem_block.clone(), - Default::default(), - )), - execution_output: Default::default(), - hashed_state: Default::default(), + 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: Default::default(), }); @@ -1689,7 +1691,7 @@ mod tests { let in_memory_block_senders = first_in_mem_block.senders().expect("failed to recover senders"); let chain = NewCanonicalChain::Commit { - new: vec![ExecutedBlock::new( + new: vec![ExecutedBlockWithTrieUpdates::new( Arc::new(RecoveredBlock::new_sealed( first_in_mem_block.clone(), in_memory_block_senders, @@ -1795,7 +1797,7 @@ mod tests { .first() .map(|block| { let senders = block.senders().expect("failed to recover senders"); - ExecutedBlock::new( + ExecutedBlockWithTrieUpdates::new( Arc::new(RecoveredBlock::new_sealed(block.clone(), senders)), Arc::new(ExecutionOutcome { bundle: BundleState::new( diff --git a/crates/storage/provider/src/providers/database/chain.rs b/crates/storage/provider/src/providers/database/chain.rs index 24f4888ec397..b20741784212 100644 --- a/crates/storage/provider/src/providers/database/chain.rs +++ b/crates/storage/provider/src/providers/database/chain.rs @@ -23,6 +23,7 @@ where T: FullSignedTx, N: FullNodePrimitives< Block = reth_primitives::Block, + BlockHeader = alloy_consensus::Header, BlockBody = reth_primitives::BlockBody, SignedTx = T, >, diff --git a/crates/storage/provider/src/providers/database/mod.rs b/crates/storage/provider/src/providers/database/mod.rs index 12e33146c913..157922b50362 100644 --- a/crates/storage/provider/src/providers/database/mod.rs +++ b/crates/storage/provider/src/providers/database/mod.rs @@ -562,14 +562,29 @@ impl BlockBodyIndicesProvider for ProviderFactory { &self, number: BlockNumber, ) -> ProviderResult> { - self.provider()?.block_body_indices(number) + self.static_file_provider.get_with_static_file_or_database( + StaticFileSegment::BlockMeta, + number, + |static_file| static_file.block_body_indices(number), + || self.provider()?.block_body_indices(number), + ) } fn block_body_indices_range( &self, range: RangeInclusive, ) -> ProviderResult> { - self.provider()?.block_body_indices_range(range) + self.static_file_provider.get_range_with_static_file_or_database( + StaticFileSegment::BlockMeta, + *range.start()..*range.end() + 1, + |static_file, range, _| { + static_file.block_body_indices_range(range.start..=range.end.saturating_sub(1)) + }, + |range, _| { + self.provider()?.block_body_indices_range(range.start..=range.end.saturating_sub(1)) + }, + |_| true, + ) } } diff --git a/crates/storage/provider/src/providers/database/provider.rs b/crates/storage/provider/src/providers/database/provider.rs index 6ac93454c865..f5b5ed455bfb 100644 --- a/crates/storage/provider/src/providers/database/provider.rs +++ b/crates/storage/provider/src/providers/database/provider.rs @@ -19,7 +19,7 @@ use crate::{ StorageReader, StorageTrieWriter, TransactionVariant, TransactionsProvider, TransactionsProviderExt, TrieWriter, WithdrawalsProvider, }; -use alloy_consensus::{transaction::TransactionMeta, BlockHeader, Header}; +use alloy_consensus::{transaction::TransactionMeta, BlockHeader, Header, TxReceipt}; use alloy_eips::{eip2718::Encodable2718, eip4895::Withdrawals, BlockHashOrNumber}; use alloy_primitives::{ keccak256, @@ -51,7 +51,9 @@ use reth_primitives::{ StaticFileSegment, StorageEntry, }; use reth_primitives_traits::{Block as _, BlockBody as _, SignedTransaction}; -use reth_prune_types::{PruneCheckpoint, PruneModes, PruneSegment}; +use reth_prune_types::{ + PruneCheckpoint, PruneMode, PruneModes, PruneSegment, MINIMUM_PRUNING_DISTANCE, +}; use reth_stages_types::{StageCheckpoint, StageId}; use reth_storage_api::{ BlockBodyIndicesProvider, BlockBodyReader, NodePrimitivesProvider, OmmersProvider, @@ -691,9 +693,7 @@ impl DatabaseProvider { match known_senders.get(&tx_num) { None => { // recover the sender from the transaction if not found - let sender = tx - .recover_signer_unchecked() - .ok_or(ProviderError::SenderRecoveryError)?; + let sender = tx.recover_signer_unchecked()?; senders.push(sender); } Some(sender) => senders.push(*sender), @@ -1229,7 +1229,7 @@ impl BlockReader for DatabaseProvid // Note: we're using unchecked here because we know the block contains valid txs // wrt to its height and can ignore the s value check so pre // EIP-2 txs are allowed - .try_with_senders_unchecked(senders) + .try_into_recovered_unchecked(senders) .map(Some) .map_err(|_| ProviderError::SenderRecoveryError) }, @@ -1274,7 +1274,7 @@ impl BlockReader for DatabaseProvid |range| self.headers_range(range), |header, body, senders| { Self::Block::new(header, body) - .try_with_senders_unchecked(senders) + .try_into_recovered_unchecked(senders) .map_err(|_| ProviderError::SenderRecoveryError) }, ) @@ -1574,14 +1574,21 @@ impl> Withdrawals ) -> ProviderResult> { if self.chain_spec.is_shanghai_active_at_timestamp(timestamp) { if let Some(number) = self.convert_hash_or_number(id)? { - // If we are past shanghai, then all blocks should have a withdrawal list, even if - // empty - let withdrawals = self - .tx - .get::(number) - .map(|w| w.map(|w| w.withdrawals))? - .unwrap_or_default(); - return Ok(Some(withdrawals)) + return self.static_file_provider.get_with_static_file_or_database( + StaticFileSegment::BlockMeta, + number, + |static_file| static_file.withdrawals_by_block(number.into(), timestamp), + || { + // If we are past shanghai, then all blocks should have a withdrawal list, + // even if empty + let withdrawals = self + .tx + .get::(number) + .map(|w| w.map(|w| w.withdrawals))? + .unwrap_or_default(); + Ok(Some(withdrawals)) + }, + ) } } Ok(None) @@ -1601,9 +1608,12 @@ impl OmmersProvider for DatabasePro return Ok(Some(Vec::new())) } - let ommers = - self.tx.get::>(number)?.map(|o| o.ommers); - return Ok(ommers) + return self.static_file_provider.get_with_static_file_or_database( + StaticFileSegment::BlockMeta, + number, + |static_file| static_file.ommers(id), + || Ok(self.tx.get::>(number)?.map(|o| o.ommers)), + ) } Ok(None) @@ -1614,19 +1624,27 @@ impl BlockBodyIndicesProvider for DatabaseProvider { fn block_body_indices(&self, num: u64) -> ProviderResult> { - Ok(self.tx.get::(num)?) + self.static_file_provider.get_with_static_file_or_database( + StaticFileSegment::BlockMeta, + num, + |static_file| static_file.block_body_indices(num), + || Ok(self.tx.get::(num)?), + ) } fn block_body_indices_range( &self, range: RangeInclusive, ) -> ProviderResult> { - Ok(self - .tx_ref() - .cursor_read::()? - .walk_range(range)? - .map(|r| r.map(|(_, b)| b)) - .collect::>()?) + self.static_file_provider.get_range_with_static_file_or_database( + StaticFileSegment::BlockMeta, + *range.start()..*range.end() + 1, + |static_file, range, _| { + static_file.block_body_indices_range(range.start..=range.end.saturating_sub(1)) + }, + |range, _| self.cursor_read_collect::(range), + |_| true, + ) } } @@ -1765,9 +1783,11 @@ impl StateWriter write_receipts_to: StorageLocation, ) -> ProviderResult<()> { let first_block = execution_outcome.first_block(); - let block_count = execution_outcome.receipts.len() as u64; - let block_range = first_block..=first_block.saturating_add(block_count).saturating_sub(1); - let last_block = *block_range.end(); + let block_count = execution_outcome.len() as u64; + let last_block = execution_outcome.last_block(); + let block_range = first_block..=last_block; + + let tip = self.last_block_number()?.max(last_block); let (plain_state, reverts) = execution_outcome.bundle.to_plain_state_and_reverts(is_value_known); @@ -1790,8 +1810,7 @@ impl StateWriter )); } - let has_receipts_pruning = self.prune_modes.has_receipts_pruning() || - execution_outcome.receipts.iter().flatten().any(|receipt| receipt.is_none()); + let has_receipts_pruning = self.prune_modes.has_receipts_pruning(); // Prepare receipts cursor if we are going to write receipts to the database // @@ -1809,6 +1828,20 @@ impl StateWriter .then(|| self.static_file_provider.get_writer(first_block, StaticFileSegment::Receipts)) .transpose()?; + let has_contract_log_filter = !self.prune_modes.receipts_log_filter.is_empty(); + let contract_log_pruner = self.prune_modes.receipts_log_filter.group_by_block(tip, None)?; + + // All receipts from the last 128 blocks are required for blockchain tree, even with + // [`PruneSegment::ContractLogs`]. + let prunable_receipts = + PruneMode::Distance(MINIMUM_PRUNING_DISTANCE).should_prune(first_block, tip); + + // Prepare set of addresses which logs should not be pruned. + let mut allowed_addresses: HashSet = HashSet::new(); + for (_, addresses) in contract_log_pruner.range(..first_block) { + allowed_addresses.extend(addresses.iter().copied()); + } + for (idx, (receipts, first_tx_index)) in execution_outcome.receipts.iter().zip(block_indices).enumerate() { @@ -1819,16 +1852,37 @@ impl StateWriter writer.increment_block(block_number)?; } + // Skip writing receipts if pruning configuration requires us to. + if prunable_receipts && + self.prune_modes + .receipts + .is_some_and(|mode| mode.should_prune(block_number, tip)) + { + continue + } + + // If there are new addresses to retain after this block number, track them + if let Some(new_addresses) = contract_log_pruner.get(&block_number) { + allowed_addresses.extend(new_addresses.iter().copied()); + } + for (idx, receipt) in receipts.iter().enumerate() { let receipt_idx = first_tx_index + idx as u64; - if let Some(receipt) = receipt { - if let Some(writer) = &mut receipts_static_writer { - writer.append_receipt(receipt_idx, receipt)?; - } + // Skip writing receipt if log filter is active and it does not have any logs to + // retain + if prunable_receipts && + has_contract_log_filter && + !receipt.logs().iter().any(|log| allowed_addresses.contains(&log.address)) + { + continue + } - if let Some(cursor) = &mut receipts_cursor { - cursor.append(receipt_idx, receipt)?; - } + if let Some(writer) = &mut receipts_static_writer { + writer.append_receipt(receipt_idx, receipt)?; + } + + if let Some(cursor) = &mut receipts_cursor { + cursor.append(receipt_idx, receipt)?; } } } @@ -2223,9 +2277,7 @@ impl StateWriter let mut block_receipts = Vec::with_capacity(block_body.tx_count as usize); for num in block_body.tx_num_range() { if receipts_iter.peek().is_some_and(|(n, _)| *n == num) { - block_receipts.push(receipts_iter.next().map(|(_, r)| r)); - } else { - block_receipts.push(None); + block_receipts.push(receipts_iter.next().unwrap().1); } } receipts.push(block_receipts); @@ -2818,7 +2870,7 @@ impl BlockWrite let tx_count = block.body().transaction_count() as u64; // Ensures we have all the senders for the block's transactions. - for (transaction, sender) in block.body().transactions().iter().zip(block.senders_iter()) { + for (transaction, sender) in block.body().transactions_iter().zip(block.senders_iter()) { let hash = transaction.tx_hash(); if self.prune_modes.sender_recovery.as_ref().is_none_or(|m| !m.is_full()) { diff --git a/crates/storage/provider/src/providers/mod.rs b/crates/storage/provider/src/providers/mod.rs index 7ac5bde40741..3d8193a5ee27 100644 --- a/crates/storage/provider/src/providers/mod.rs +++ b/crates/storage/provider/src/providers/mod.rs @@ -3,7 +3,6 @@ use reth_chainspec::EthereumHardforks; use reth_db::table::Value; use reth_node_types::{FullNodePrimitives, NodeTypes, NodeTypesWithDB, NodeTypesWithEngine}; -use reth_primitives::EthPrimitives; mod database; pub use database::*; @@ -61,23 +60,7 @@ where } impl ProviderNodeTypes for T where T: NodeTypesForProvider + NodeTypesWithDB {} -/// A helper trait with requirements for [`NodeTypesForProvider`] to be used within legacy -/// blockchain tree. -pub trait NodeTypesForTree: - NodeTypesForProvider + NodeTypesWithEngine -{ -} -impl NodeTypesForTree for T where - T: NodeTypesForProvider + NodeTypesWithEngine -{ -} - /// Helper trait expressing requirements for node types to be used in engine. pub trait EngineNodeTypes: ProviderNodeTypes + NodeTypesWithEngine {} impl EngineNodeTypes for T where T: ProviderNodeTypes + NodeTypesWithEngine {} - -/// Helper trait with requirements for [`ProviderNodeTypes`] to be used within legacy blockchain -/// tree. -pub trait TreeNodeTypes: ProviderNodeTypes + NodeTypesForTree {} -impl TreeNodeTypes for T where T: ProviderNodeTypes + NodeTypesForTree {} diff --git a/crates/storage/provider/src/providers/static_file/jar.rs b/crates/storage/provider/src/providers/static_file/jar.rs index 4b6525c1d1dc..c87fc45d7983 100644 --- a/crates/storage/provider/src/providers/static_file/jar.rs +++ b/crates/storage/provider/src/providers/static_file/jar.rs @@ -299,15 +299,14 @@ impl> TransactionsPr range: impl RangeBounds, ) -> ProviderResult> { let txs = self.transactions_by_tx_range(range)?; - reth_primitives_traits::transaction::recover::recover_signers(&txs) - .ok_or(ProviderError::SenderRecoveryError) + Ok(reth_primitives_traits::transaction::recover::recover_signers(&txs)?) } fn transaction_sender(&self, num: TxNumber) -> ProviderResult> { Ok(self .cursor()? .get_one::>(num.into())? - .and_then(|tx| tx.recover_signer())) + .and_then(|tx| tx.recover_signer().ok())) } } @@ -362,7 +361,10 @@ impl WithdrawalsProvider for StaticFileJarProvider<'_, N> { _: u64, ) -> ProviderResult> { if let Some(num) = id.as_number() { - return Ok(self.cursor()?.get_one::(num.into())?.map(|s| s.withdrawals)) + return Ok(self + .cursor()? + .get_one::(num.into())? + .and_then(|s| s.withdrawals)) } // Only accepts block number queries Err(ProviderError::UnsupportedProvider) diff --git a/crates/storage/provider/src/providers/static_file/manager.rs b/crates/storage/provider/src/providers/static_file/manager.rs index 11f768a07642..77583f0da926 100644 --- a/crates/storage/provider/src/providers/static_file/manager.rs +++ b/crates/storage/provider/src/providers/static_file/manager.rs @@ -17,8 +17,8 @@ use reth_chainspec::{ChainInfo, ChainSpecProvider}; use reth_db::{ lockfile::StorageLock, static_file::{ - iter_static_files, BlockHashMask, HeaderMask, HeaderWithHashMask, ReceiptMask, - StaticFileCursor, TDWithHashMask, TransactionMask, + iter_static_files, BlockHashMask, BodyIndicesMask, HeaderMask, HeaderWithHashMask, + ReceiptMask, StaticFileCursor, TDWithHashMask, TransactionMask, }, table::{Decompress, Value}, tables, @@ -1027,7 +1027,7 @@ impl StaticFileProvider { "Could not find block or tx number on a range request" ); - let err = if segment.is_headers() { + let err = if segment.is_block_based() { ProviderError::MissingStaticFileBlock(segment, number) } else { ProviderError::MissingStaticFileTx(segment, number) @@ -1568,12 +1568,14 @@ impl> TransactionsPr range: impl RangeBounds, ) -> ProviderResult> { let txes = self.transactions_by_tx_range(range)?; - reth_primitives_traits::transaction::recover::recover_signers(&txes) - .ok_or(ProviderError::SenderRecoveryError) + Ok(reth_primitives_traits::transaction::recover::recover_signers(&txes)?) } fn transaction_sender(&self, id: TxNumber) -> ProviderResult> { - Ok(self.transaction_by_id_unhashed(id)?.and_then(|tx| tx.recover_signer())) + match self.transaction_by_id_unhashed(id)? { + Some(tx) => Ok(tx.recover_signer().ok()), + None => Ok(None), + } } } @@ -1732,10 +1734,14 @@ impl BlockBodyIndicesProvider for StaticFileProvider { fn block_body_indices_range( &self, - _range: RangeInclusive, + range: RangeInclusive, ) -> ProviderResult> { - // Required data not present in static_files - Err(ProviderError::UnsupportedProvider) + self.fetch_range_with_predicate( + StaticFileSegment::BlockMeta, + *range.start()..*range.end() + 1, + |cursor, number| cursor.get_one::(number.into()), + |_| true, + ) } } diff --git a/crates/storage/provider/src/test_utils/blocks.rs b/crates/storage/provider/src/test_utils/blocks.rs index fec124ad12ab..f722e8c54a1e 100644 --- a/crates/storage/provider/src/test_utils/blocks.rs +++ b/crates/storage/provider/src/test_utils/blocks.rs @@ -207,7 +207,7 @@ fn block1(number: BlockNumber) -> (RecoveredBlock, Execu .revert_account_info(number, account2, Some(None)) .state_storage(account1, HashMap::from_iter([(slot, (U256::ZERO, U256::from(10)))])) .build(), - vec![vec![Some( + vec![vec![ #[allow(clippy::needless_update)] // side-effect of optimism fields Receipt { tx_type: TxType::Eip2930, @@ -220,7 +220,7 @@ fn block1(number: BlockNumber) -> (RecoveredBlock, Execu )], ..Default::default() }, - )]] + ]] .into(), number, Vec::new(), @@ -266,7 +266,7 @@ fn block2( ) .revert_storage(number, account, Vec::from([(slot, U256::from(10))])) .build(), - vec![vec![Some( + vec![vec![ #[allow(clippy::needless_update)] // side-effect of optimism fields Receipt { tx_type: TxType::Eip1559, @@ -279,7 +279,7 @@ fn block2( )], ..Default::default() }, - )]] + ]] .into(), number, Vec::new(), @@ -334,7 +334,7 @@ fn block3( } let execution_outcome = ExecutionOutcome::new( bundle_state_builder.build(), - vec![vec![Some( + vec![vec![ #[allow(clippy::needless_update)] // side-effect of optimism fields Receipt { tx_type: TxType::Eip1559, @@ -347,7 +347,7 @@ fn block3( )], ..Default::default() }, - )]] + ]] .into(), number, Vec::new(), @@ -422,7 +422,7 @@ fn block4( } let execution_outcome = ExecutionOutcome::new( bundle_state_builder.build(), - vec![vec![Some( + vec![vec![ #[allow(clippy::needless_update)] // side-effect of optimism fields Receipt { tx_type: TxType::Eip1559, @@ -435,7 +435,7 @@ fn block4( )], ..Default::default() }, - )]] + ]] .into(), number, Vec::new(), @@ -507,7 +507,7 @@ fn block5( } let execution_outcome = ExecutionOutcome::new( bundle_state_builder.build(), - vec![vec![Some( + vec![vec![ #[allow(clippy::needless_update)] // side-effect of optimism fields Receipt { tx_type: TxType::Eip1559, @@ -520,7 +520,7 @@ fn block5( )], ..Default::default() }, - )]] + ]] .into(), number, Vec::new(), diff --git a/crates/storage/provider/src/test_utils/mock.rs b/crates/storage/provider/src/test_utils/mock.rs index e6efe8012492..bf8d9affbd3e 100644 --- a/crates/storage/provider/src/test_utils/mock.rs +++ b/crates/storage/provider/src/test_utils/mock.rs @@ -42,9 +42,9 @@ use std::{ /// A mock implementation for Provider interfaces. #[derive(Debug, Clone)] -pub struct MockEthProvider { +pub struct MockEthProvider { /// Local block store - pub blocks: Arc>>, + pub blocks: Arc>>>, /// Local header store pub headers: Arc>>, /// Local account store @@ -55,7 +55,52 @@ pub struct MockEthProvider { pub state_roots: Arc>>, } -impl Default for MockEthProvider { +impl MockEthProvider { + /// Add block to local block store + pub fn add_block(&self, hash: B256, block: Block) { + self.add_header(hash, block.header.clone()); + self.blocks.lock().insert(hash, block); + } + + /// Add multiple blocks to local block store + pub fn extend_blocks(&self, iter: impl IntoIterator)>) { + for (hash, block) in iter { + self.add_header(hash, block.header.clone()); + self.add_block(hash, block) + } + } + + /// Add header to local header store + pub fn add_header(&self, hash: B256, header: Header) { + self.headers.lock().insert(hash, header); + } + + /// Add multiple headers to local header store + pub fn extend_headers(&self, iter: impl IntoIterator) { + for (hash, header) in iter { + self.add_header(hash, header) + } + } + + /// Add account to local account store + pub fn add_account(&self, address: Address, account: ExtendedAccount) { + self.accounts.lock().insert(address, account); + } + + /// Add account to local account store + pub fn extend_accounts(&self, iter: impl IntoIterator) { + for (address, account) in iter { + self.add_account(address, account) + } + } + + /// Add state root to local state root store + pub fn add_state_root(&self, state_root: B256) { + self.state_roots.lock().push(state_root); + } +} + +impl Default for MockEthProvider { fn default() -> Self { Self { blocks: Default::default(), @@ -104,51 +149,6 @@ impl ExtendedAccount { } } -impl MockEthProvider { - /// Add block to local block store - pub fn add_block(&self, hash: B256, block: Block) { - self.add_header(hash, block.header.clone()); - self.blocks.lock().insert(hash, block); - } - - /// Add multiple blocks to local block store - pub fn extend_blocks(&self, iter: impl IntoIterator) { - for (hash, block) in iter { - self.add_header(hash, block.header.clone()); - self.add_block(hash, block) - } - } - - /// Add header to local header store - pub fn add_header(&self, hash: B256, header: Header) { - self.headers.lock().insert(hash, header); - } - - /// Add multiple headers to local header store - pub fn extend_headers(&self, iter: impl IntoIterator) { - for (hash, header) in iter { - self.add_header(hash, header) - } - } - - /// Add account to local account store - pub fn add_account(&self, address: Address, account: ExtendedAccount) { - self.accounts.lock().insert(address, account); - } - - /// Add account to local account store - pub fn extend_accounts(&self, iter: impl IntoIterator) { - for (address, account) in iter { - self.add_account(address, account) - } - } - - /// Add state root to local state root store - pub fn add_state_root(&self, state_root: B256) { - self.state_roots.lock().push(state_root); - } -} - /// Mock node. #[derive(Debug)] pub struct MockNode; @@ -368,7 +368,11 @@ impl TransactionsProvider for MockEthProvider { .flat_map(|block| &block.body.transactions) .enumerate() .filter_map(|(tx_number, tx)| { - range.contains(&(tx_number as TxNumber)).then(|| tx.recover_signer()).flatten() + if range.contains(&(tx_number as TxNumber)) { + tx.recover_signer().ok() + } else { + None + } }) .collect(); diff --git a/crates/storage/provider/src/traits/block.rs b/crates/storage/provider/src/traits/block.rs index 6116cfdd0434..12ae006d8882 100644 --- a/crates/storage/provider/src/traits/block.rs +++ b/crates/storage/provider/src/traits/block.rs @@ -3,6 +3,7 @@ use reth_db_api::models::StoredBlockBodyIndices; use reth_execution_types::{Chain, ExecutionOutcome}; use reth_node_types::NodePrimitives; use reth_primitives::RecoveredBlock; +use reth_primitives_traits::Block; use reth_storage_api::{NodePrimitivesProvider, StorageLocation}; use reth_storage_errors::provider::ProviderResult; use reth_trie::{updates::TrieUpdates, HashedPostStateSorted}; @@ -71,7 +72,7 @@ pub trait StateReader: Send + Sync { #[auto_impl::auto_impl(&, Arc, Box)] pub trait BlockWriter: Send + Sync { /// The body this writer can write. - type Block: reth_primitives_traits::Block; + type Block: Block; /// The receipt type for [`ExecutionOutcome`]. type Receipt: Send + Sync; @@ -96,7 +97,7 @@ pub trait BlockWriter: Send + Sync { /// Bodies are passed as [`Option`]s, if body is `None` the corresponding block is empty. fn append_block_bodies( &self, - bodies: Vec<(BlockNumber, Option<::Body>)>, + bodies: Vec<(BlockNumber, Option<::Body>)>, write_to: StorageLocation, ) -> ProviderResult<()>; diff --git a/crates/storage/provider/src/writer/mod.rs b/crates/storage/provider/src/writer/mod.rs index d1cc61600db8..e88c1dfb5ca5 100644 --- a/crates/storage/provider/src/writer/mod.rs +++ b/crates/storage/provider/src/writer/mod.rs @@ -4,7 +4,7 @@ use crate::{ StorageLocation, TrieWriter, }; use alloy_consensus::BlockHeader; -use reth_chain_state::ExecutedBlock; +use reth_chain_state::{ExecutedBlock, ExecutedBlockWithTrieUpdates}; use reth_db::transaction::{DbTx, DbTxMut}; use reth_errors::ProviderResult; use reth_primitives::{NodePrimitives, StaticFileSegment}; @@ -132,7 +132,7 @@ where + StaticFileProviderFactory, { /// Writes executed blocks and receipts to storage. - pub fn save_blocks(&self, blocks: Vec>) -> ProviderResult<()> + pub fn save_blocks(&self, blocks: Vec>) -> ProviderResult<()> where N: NodePrimitives, ProviderDB: BlockWriter + StateWriter, @@ -160,7 +160,11 @@ where // * trie updates (cannot naively extend, need helper) // * indices (already done basically) // Insert the blocks - for ExecutedBlock { recovered_block, execution_output, hashed_state, trie } in blocks { + for ExecutedBlockWithTrieUpdates { + block: ExecutedBlock { recovered_block, execution_output, hashed_state }, + trie, + } in blocks + { self.database() .insert_block(Arc::unwrap_or_clone(recovered_block), StorageLocation::Both)?; @@ -1055,7 +1059,7 @@ mod tests { fn revert_to_indices() { let base: ExecutionOutcome = ExecutionOutcome { bundle: BundleState::default(), - receipts: vec![vec![Some(Receipt::default()); 2]; 7].into(), + receipts: vec![vec![Receipt::default(); 2]; 7].into(), first_block: 10, requests: Vec::new(), }; @@ -1266,7 +1270,7 @@ mod tests { let mut test: ExecutionOutcome = ExecutionOutcome { bundle: present_state, - receipts: vec![vec![Some(Receipt::default()); 2]; 1].into(), + receipts: vec![vec![Receipt::default(); 2]; 1].into(), first_block: 2, requests: Vec::new(), }; diff --git a/crates/storage/storage-api/src/chain.rs b/crates/storage/storage-api/src/chain.rs index 6306f418fee0..cb2b38dc7b41 100644 --- a/crates/storage/storage-api/src/chain.rs +++ b/crates/storage/storage-api/src/chain.rs @@ -1,4 +1,5 @@ -use crate::{DBProvider, StorageLocation}; +use crate::{DBProvider, OmmersProvider, StorageLocation}; +use alloy_consensus::Header; use alloy_primitives::BlockNumber; use reth_chainspec::{ChainSpecProvider, EthereumHardforks}; use reth_db::{ @@ -138,7 +139,9 @@ where impl BlockBodyReader for EthStorage where - Provider: DBProvider + ChainSpecProvider, + Provider: DBProvider + + ChainSpecProvider + + OmmersProvider
, T: SignedTransaction, { type Block = reth_primitives::Block; @@ -151,7 +154,6 @@ where // TODO: Ideally storage should hold its own copy of chain spec let chain_spec = provider.chain_spec(); - let mut ommers_cursor = provider.tx_ref().cursor_read::()?; let mut withdrawals_cursor = provider.tx_ref().cursor_read::()?; let mut bodies = Vec::with_capacity(inputs.len()); @@ -171,7 +173,7 @@ where let ommers = if chain_spec.final_paris_total_difficulty(header.number).is_some() { Vec::new() } else { - ommers_cursor.seek_exact(header.number)?.map(|(_, o)| o.ommers).unwrap_or_default() + provider.ommers(header.number.into())?.unwrap_or_default() }; bodies.push(reth_primitives::BlockBody { transactions, ommers, withdrawals }); diff --git a/crates/transaction-pool/src/lib.rs b/crates/transaction-pool/src/lib.rs index c85d84a80ab7..19f44df3b6ec 100644 --- a/crates/transaction-pool/src/lib.rs +++ b/crates/transaction-pool/src/lib.rs @@ -176,7 +176,7 @@ use alloy_primitives::{Address, TxHash, B256, U256}; use aquamarine as _; use reth_eth_wire_types::HandleMempoolData; use reth_execution_types::ChangedAccount; -use reth_primitives::RecoveredTx; +use reth_primitives::Recovered; use reth_primitives_traits::Block; use reth_storage_api::StateProviderFactory; use std::{collections::HashSet, sync::Arc}; @@ -423,7 +423,7 @@ where fn get_pooled_transaction_element( &self, tx_hash: TxHash, - ) -> Option::Transaction as PoolTransaction>::Pooled>> + ) -> Option::Transaction as PoolTransaction>::Pooled>> { self.pool.get_pooled_transaction_element(tx_hash) } diff --git a/crates/transaction-pool/src/maintain.rs b/crates/transaction-pool/src/maintain.rs index af8e8da33c68..7e968ce19433 100644 --- a/crates/transaction-pool/src/maintain.rs +++ b/crates/transaction-pool/src/maintain.rs @@ -577,7 +577,7 @@ where let pool_transactions = txs_signed .into_iter() - .filter_map(|tx| tx.try_ecrecovered()) + .filter_map(|tx| tx.try_clone_into_recovered().ok()) .filter_map(|tx| { // Filter out errors ::try_from_consensus(tx).ok() @@ -695,7 +695,7 @@ mod tests { let tx_bytes = hex!("02f87201830655c2808505ef61f08482565f94388c818ca8b9251b393131c08a736a67ccb192978801049e39c4b5b1f580c001a01764ace353514e8abdfb92446de356b260e3c1225b73fc4c8876a6258d12a129a04f02294aa61ca7676061cd99f29275491218b4754b46a0248e5e42bc5091f507"); let tx = PooledTransaction::decode_2718(&mut &tx_bytes[..]).unwrap(); let provider = MockEthProvider::default(); - let transaction: EthPooledTransaction = tx.try_into_ecrecovered().unwrap().into(); + let transaction: EthPooledTransaction = tx.try_into_recovered().unwrap().into(); let tx_to_cmp = transaction.clone(); let sender = hex!("1f9090aaE28b8a3dCeaDf281B0F12828e676c326").into(); provider.add_account(sender, ExtendedAccount::new(42, U256::MAX)); diff --git a/crates/transaction-pool/src/noop.rs b/crates/transaction-pool/src/noop.rs index 8d880994aa9e..9a064c56eb4d 100644 --- a/crates/transaction-pool/src/noop.rs +++ b/crates/transaction-pool/src/noop.rs @@ -22,7 +22,7 @@ use alloy_eips::{ }; use alloy_primitives::{Address, TxHash, B256, U256}; use reth_eth_wire_types::HandleMempoolData; -use reth_primitives::RecoveredTx; +use reth_primitives::Recovered; use std::{collections::HashSet, marker::PhantomData, sync::Arc}; use tokio::sync::{mpsc, mpsc::Receiver}; @@ -143,7 +143,7 @@ impl TransactionPool for NoopTransactionPool { fn get_pooled_transaction_element( &self, _tx_hash: TxHash, - ) -> Option::Pooled>> { + ) -> Option::Pooled>> { None } diff --git a/crates/transaction-pool/src/pool/best.rs b/crates/transaction-pool/src/pool/best.rs index aa9e7ec121dc..7d58e3984416 100644 --- a/crates/transaction-pool/src/pool/best.rs +++ b/crates/transaction-pool/src/pool/best.rs @@ -7,7 +7,7 @@ use crate::{ use alloy_primitives::Address; use core::fmt; use reth_payload_util::PayloadTransactions; -use reth_primitives::{InvalidTransactionError, RecoveredTx}; +use reth_primitives::{InvalidTransactionError, Recovered}; use std::{ collections::{BTreeMap, BTreeSet, HashSet, VecDeque}, sync::Arc, @@ -251,7 +251,7 @@ where { type Transaction = T::Consensus; - fn next(&mut self, _ctx: ()) -> Option> { + fn next(&mut self, _ctx: ()) -> Option> { loop { let tx = self.best.next()?; if self.invalid.contains(&tx.sender()) { diff --git a/crates/transaction-pool/src/pool/mod.rs b/crates/transaction-pool/src/pool/mod.rs index 983a86306c01..348e76947e75 100644 --- a/crates/transaction-pool/src/pool/mod.rs +++ b/crates/transaction-pool/src/pool/mod.rs @@ -88,7 +88,7 @@ use reth_eth_wire_types::HandleMempoolData; use reth_execution_types::ChangedAccount; use alloy_eips::eip4844::BlobTransactionSidecar; -use reth_primitives::RecoveredTx; +use reth_primitives::Recovered; use rustc_hash::FxHashMap; use std::{collections::HashSet, fmt, sync::Arc, time::Instant}; use tokio::sync::mpsc; @@ -316,7 +316,7 @@ where fn to_pooled_transaction( &self, transaction: Arc>, - ) -> Option::Transaction as PoolTransaction>::Pooled>> + ) -> Option::Transaction as PoolTransaction>::Pooled>> where ::Transaction: EthPoolTransaction, { @@ -371,7 +371,7 @@ where pub fn get_pooled_transaction_element( &self, tx_hash: TxHash, - ) -> Option::Transaction as PoolTransaction>::Pooled>> + ) -> Option::Transaction as PoolTransaction>::Pooled>> where ::Transaction: EthPoolTransaction, { diff --git a/crates/transaction-pool/src/test_utils/gen.rs b/crates/transaction-pool/src/test_utils/gen.rs index feb50050c10d..4944dd110c16 100644 --- a/crates/transaction-pool/src/test_utils/gen.rs +++ b/crates/transaction-pool/src/test_utils/gen.rs @@ -101,12 +101,12 @@ impl TransactionGenerator { /// Generates and returns a pooled EIP-1559 transaction with a random signer. pub fn gen_eip1559_pooled(&mut self) -> EthPooledTransaction { - self.gen_eip1559().try_into_ecrecovered().unwrap().try_into().unwrap() + self.gen_eip1559().try_into_recovered().unwrap().try_into().unwrap() } /// Generates and returns a pooled EIP-4844 transaction with a random signer. pub fn gen_eip4844_pooled(&mut self) -> EthPooledTransaction { - let tx = self.gen_eip4844().try_into_ecrecovered().unwrap(); + let tx = self.gen_eip4844().try_into_recovered().unwrap(); let encoded_length = tx.encode_2718_len(); EthPooledTransaction::new(tx, encoded_length) } diff --git a/crates/transaction-pool/src/test_utils/mock.rs b/crates/transaction-pool/src/test_utils/mock.rs index 1cd4445e8d1c..e766cbc90f23 100644 --- a/crates/transaction-pool/src/test_utils/mock.rs +++ b/crates/transaction-pool/src/test_utils/mock.rs @@ -30,7 +30,7 @@ use rand::{ }; use reth_primitives::{ transaction::{SignedTransactionIntoRecoveredExt, TryFromRecoveredTransactionError}, - PooledTransaction, RecoveredTx, Transaction, TransactionSigned, TxType, + PooledTransaction, Recovered, Transaction, TransactionSigned, TxType, }; use reth_primitives_traits::{InMemorySize, SignedTransaction}; use std::{ops::Range, sync::Arc, time::Instant, vec::IntoIter}; @@ -668,22 +668,22 @@ impl PoolTransaction for MockTransaction { type Pooled = PooledTransaction; fn try_from_consensus( - tx: RecoveredTx, + tx: Recovered, ) -> Result { tx.try_into() } - fn into_consensus(self) -> RecoveredTx { + fn into_consensus(self) -> Recovered { self.into() } - fn from_pooled(pooled: RecoveredTx) -> Self { + fn from_pooled(pooled: Recovered) -> Self { pooled.into() } fn try_consensus_into_pooled( - tx: RecoveredTx, - ) -> Result, Self::TryFromConsensusError> { + tx: Recovered, + ) -> Result, Self::TryFromConsensusError> { let (tx, signer) = tx.into_parts(); Self::Pooled::try_from(tx) .map(|tx| tx.with_signer(signer)) @@ -869,7 +869,7 @@ impl EthPoolTransaction for MockTransaction { fn try_into_pooled_eip4844( self, sidecar: Arc, - ) -> Option> { + ) -> Option> { let (tx, signer) = self.into_consensus().into_parts(); tx.try_into_pooled_eip4844(Arc::unwrap_or_clone(sidecar)) .map(|tx| tx.with_signer(signer)) @@ -877,7 +877,7 @@ impl EthPoolTransaction for MockTransaction { } fn try_from_eip4844( - tx: RecoveredTx, + tx: Recovered, sidecar: BlobTransactionSidecar, ) -> Option { let (tx, signer) = tx.into_parts(); @@ -903,10 +903,10 @@ impl EthPoolTransaction for MockTransaction { } } -impl TryFrom> for MockTransaction { +impl TryFrom> for MockTransaction { type Error = TryFromRecoveredTransactionError; - fn try_from(tx: RecoveredTx) -> Result { + fn try_from(tx: Recovered) -> Result { let sender = tx.signer(); let transaction = tx.into_tx(); let hash = *transaction.tx_hash(); @@ -1044,16 +1044,16 @@ impl TryFrom> for MockTransaction { } } -impl From> for MockTransaction { - fn from(tx: RecoveredTx) -> Self { +impl From> for MockTransaction { + fn from(tx: Recovered) -> Self { let (tx, signer) = tx.into_parts(); - RecoveredTx::::new_unchecked(tx.into(), signer).try_into().expect( + Recovered::::new_unchecked(tx.into(), signer).try_into().expect( "Failed to convert from PooledTransactionsElementEcRecovered to MockTransaction", ) } } -impl From for RecoveredTx { +impl From for Recovered { fn from(tx: MockTransaction) -> Self { let signed_tx = TransactionSigned::new(tx.clone().into(), Signature::test_signature(), *tx.hash()); @@ -1180,9 +1180,9 @@ impl proptest::arbitrary::Arbitrary for MockTransaction { arb::<(TransactionSigned, Address)>() .prop_map(|(signed_transaction, signer)| { - RecoveredTx::new_unchecked(signed_transaction, signer) + Recovered::new_unchecked(signed_transaction, signer) .try_into() - .expect("Failed to create an Arbitrary MockTransaction via RecoveredTx") + .expect("Failed to create an Arbitrary MockTransaction from a Recovered tx") }) .boxed() } diff --git a/crates/transaction-pool/src/traits.rs b/crates/transaction-pool/src/traits.rs index 38f3c7564b25..8ac0e6c79cd2 100644 --- a/crates/transaction-pool/src/traits.rs +++ b/crates/transaction-pool/src/traits.rs @@ -21,7 +21,7 @@ use reth_execution_types::ChangedAccount; use reth_primitives::{ kzg::KzgSettings, transaction::{SignedTransactionIntoRecoveredExt, TryFromRecoveredTransactionError}, - PooledTransaction, RecoveredTx, SealedBlock, Transaction, TransactionSigned, + PooledTransaction, Recovered, SealedBlock, Transaction, TransactionSigned, }; use reth_primitives_traits::{Block, SignedTransaction}; #[cfg(feature = "serde")] @@ -257,7 +257,7 @@ pub trait TransactionPool: Send + Sync + Clone { fn get_pooled_transaction_element( &self, tx_hash: TxHash, - ) -> Option::Pooled>>; + ) -> Option::Pooled>>; /// Returns an iterator that yields transactions that are ready for block production. /// @@ -570,18 +570,18 @@ pub struct AllPoolTransactions { // === impl AllPoolTransactions === impl AllPoolTransactions { - /// Returns an iterator over all pending [`RecoveredTx`] transactions. - pub fn pending_recovered(&self) -> impl Iterator> + '_ { + /// Returns an iterator over all pending [`Recovered`] transactions. + pub fn pending_recovered(&self) -> impl Iterator> + '_ { self.pending.iter().map(|tx| tx.transaction.clone().into()) } - /// Returns an iterator over all queued [`RecoveredTx`] transactions. - pub fn queued_recovered(&self) -> impl Iterator> + '_ { + /// Returns an iterator over all queued [`Recovered`] transactions. + pub fn queued_recovered(&self) -> impl Iterator> + '_ { self.queued.iter().map(|tx| tx.transaction.clone().into()) } /// Returns an iterator over all transactions, both pending and queued. - pub fn all(&self) -> impl Iterator> + '_ { + pub fn all(&self) -> impl Iterator> + '_ { self.pending.iter().chain(self.queued.iter()).map(|tx| tx.transaction.clone().into()) } } @@ -968,9 +968,9 @@ pub trait PoolTransaction: + Send + Sync + Clone - + TryFrom, Error = Self::TryFromConsensusError> - + Into> - + From> + + TryFrom, Error = Self::TryFromConsensusError> + + Into> + + From> { /// Associated error type for the `try_from_consensus` method. type TryFromConsensusError: fmt::Display; @@ -983,7 +983,7 @@ pub trait PoolTransaction: /// Define a method to convert from the `Consensus` type to `Self` fn try_from_consensus( - tx: RecoveredTx, + tx: Recovered, ) -> Result { tx.try_into() } @@ -991,29 +991,29 @@ pub trait PoolTransaction: /// Clone the transaction into a consensus variant. /// /// This method is preferred when the [`PoolTransaction`] already wraps the consensus variant. - fn clone_into_consensus(&self) -> RecoveredTx { + fn clone_into_consensus(&self) -> Recovered { self.clone().into_consensus() } /// Define a method to convert from the `Self` type to `Consensus` - fn into_consensus(self) -> RecoveredTx { + fn into_consensus(self) -> Recovered { self.into() } /// Define a method to convert from the `Pooled` type to `Self` - fn from_pooled(pooled: RecoveredTx) -> Self { + fn from_pooled(pooled: Recovered) -> Self { pooled.into() } /// Tries to convert the `Consensus` type into the `Pooled` type. - fn try_into_pooled(self) -> Result, Self::TryFromConsensusError> { + fn try_into_pooled(self) -> Result, Self::TryFromConsensusError> { Self::try_consensus_into_pooled(self.into_consensus()) } /// Tries to convert the `Consensus` type into the `Pooled` type. fn try_consensus_into_pooled( - tx: RecoveredTx, - ) -> Result, Self::TryFromConsensusError>; + tx: Recovered, + ) -> Result, Self::TryFromConsensusError>; /// Converts the `Pooled` type into the `Consensus` type. fn pooled_into_consensus(tx: Self::Pooled) -> Self::Consensus { @@ -1160,13 +1160,13 @@ pub trait EthPoolTransaction: PoolTransaction { fn try_into_pooled_eip4844( self, sidecar: Arc, - ) -> Option>; + ) -> Option>; /// Tries to convert the `Consensus` type with a blob sidecar into the `Pooled` type. /// /// Returns `None` if passed transaction is not a blob transaction. fn try_from_eip4844( - tx: RecoveredTx, + tx: Recovered, sidecar: BlobTransactionSidecar, ) -> Option; @@ -1183,12 +1183,12 @@ pub trait EthPoolTransaction: PoolTransaction { /// The default [`PoolTransaction`] for the [Pool](crate::Pool) for Ethereum. /// -/// This type is essentially a wrapper around [`RecoveredTx`] with additional +/// This type is essentially a wrapper around [`Recovered`] with additional /// fields derived from the transaction that are frequently used by the pools for ordering. #[derive(Debug, Clone, PartialEq, Eq)] pub struct EthPooledTransaction { /// `EcRecovered` transaction, the consensus format. - pub transaction: RecoveredTx, + pub transaction: Recovered, /// For EIP-1559 transactions: `max_fee_per_gas * gas_limit + tx_value`. /// For legacy transactions: `gas_price * gas_limit + tx_value`. @@ -1209,7 +1209,7 @@ impl EthPooledTransaction { /// /// Caution: In case of blob transactions, this does marks the blob sidecar as /// [`EthBlobTransactionSidecar::Missing`] - pub fn new(transaction: RecoveredTx, encoded_length: usize) -> Self { + pub fn new(transaction: Recovered, encoded_length: usize) -> Self { let mut blob_sidecar = EthBlobTransactionSidecar::None; let gas_cost = U256::from(transaction.max_fee_per_gas()) @@ -1234,14 +1234,14 @@ impl EthPooledTransaction { } /// Return the reference to the underlying transaction. - pub const fn transaction(&self) -> &RecoveredTx { + pub const fn transaction(&self) -> &Recovered { &self.transaction } } /// Conversion from the network transaction type to the pool transaction type. -impl From> for EthPooledTransaction { - fn from(tx: RecoveredTx) -> Self { +impl From> for EthPooledTransaction { + fn from(tx: Recovered) -> Self { let encoded_length = tx.encode_2718_len(); let (tx, signer) = tx.into_parts(); match tx { @@ -1251,14 +1251,14 @@ impl From> for EthPooledTransaction { let (tx, blob) = tx.into_parts(); let tx = Signed::new_unchecked(tx, sig, hash); let tx = TransactionSigned::from(tx); - let tx = RecoveredTx::new_unchecked(tx, signer); + let tx = Recovered::new_unchecked(tx, signer); let mut pooled = Self::new(tx, encoded_length); pooled.blob_sidecar = EthBlobTransactionSidecar::Present(blob); pooled } tx => { // no blob sidecar - let tx = RecoveredTx::new_unchecked(tx.into(), signer); + let tx = Recovered::new_unchecked(tx.into(), signer); Self::new(tx, encoded_length) } } @@ -1272,17 +1272,17 @@ impl PoolTransaction for EthPooledTransaction { type Pooled = PooledTransaction; - fn clone_into_consensus(&self) -> RecoveredTx { + fn clone_into_consensus(&self) -> Recovered { self.transaction().clone() } fn try_consensus_into_pooled( - tx: RecoveredTx, - ) -> Result, Self::TryFromConsensusError> { + tx: Recovered, + ) -> Result, Self::TryFromConsensusError> { let (tx, signer) = tx.into_parts(); let pooled = tx.try_into().map_err(|_| TryFromRecoveredTransactionError::BlobSidecarMissing)?; - Ok(RecoveredTx::new_unchecked(pooled, signer)) + Ok(Recovered::new_unchecked(pooled, signer)) } /// Returns hash of the transaction. @@ -1413,16 +1413,16 @@ impl EthPoolTransaction for EthPooledTransaction { fn try_into_pooled_eip4844( self, sidecar: Arc, - ) -> Option> { + ) -> Option> { let (signed_transaction, signer) = self.into_consensus().into_parts(); let pooled_transaction = signed_transaction.try_into_pooled_eip4844(Arc::unwrap_or_clone(sidecar)).ok()?; - Some(RecoveredTx::new_unchecked(pooled_transaction, signer)) + Some(Recovered::new_unchecked(pooled_transaction, signer)) } fn try_from_eip4844( - tx: RecoveredTx, + tx: Recovered, sidecar: BlobTransactionSidecar, ) -> Option { let (tx, signer) = tx.into_parts(); @@ -1451,10 +1451,10 @@ impl EthPoolTransaction for EthPooledTransaction { } } -impl TryFrom> for EthPooledTransaction { +impl TryFrom> for EthPooledTransaction { type Error = TryFromRecoveredTransactionError; - fn try_from(tx: RecoveredTx) -> Result { + fn try_from(tx: Recovered) -> Result { // ensure we can handle the transaction type and its format match tx.ty() { 0..=EIP1559_TX_TYPE_ID | EIP7702_TX_TYPE_ID => { @@ -1478,7 +1478,7 @@ impl TryFrom> for EthPooledTransaction { } } -impl From for RecoveredTx { +impl From for Recovered { fn from(tx: EthPooledTransaction) -> Self { tx.transaction } @@ -1690,7 +1690,7 @@ mod tests { }); let signature = Signature::test_signature(); let signed_tx = TransactionSigned::new_unhashed(tx, signature); - let transaction = RecoveredTx::new_unchecked(signed_tx, Default::default()); + let transaction = Recovered::new_unchecked(signed_tx, Default::default()); let pooled_tx = EthPooledTransaction::new(transaction.clone(), 200); // Check that the pooled transaction is created correctly @@ -1711,7 +1711,7 @@ mod tests { }); let signature = Signature::test_signature(); let signed_tx = TransactionSigned::new_unhashed(tx, signature); - let transaction = RecoveredTx::new_unchecked(signed_tx, Default::default()); + let transaction = Recovered::new_unchecked(signed_tx, Default::default()); let pooled_tx = EthPooledTransaction::new(transaction.clone(), 200); // Check that the pooled transaction is created correctly @@ -1732,7 +1732,7 @@ mod tests { }); let signature = Signature::test_signature(); let signed_tx = TransactionSigned::new_unhashed(tx, signature); - let transaction = RecoveredTx::new_unchecked(signed_tx, Default::default()); + let transaction = Recovered::new_unchecked(signed_tx, Default::default()); let pooled_tx = EthPooledTransaction::new(transaction.clone(), 200); // Check that the pooled transaction is created correctly @@ -1755,7 +1755,7 @@ mod tests { }); let signature = Signature::test_signature(); let signed_tx = TransactionSigned::new_unhashed(tx, signature); - let transaction = RecoveredTx::new_unchecked(signed_tx, Default::default()); + let transaction = Recovered::new_unchecked(signed_tx, Default::default()); let pooled_tx = EthPooledTransaction::new(transaction.clone(), 300); // Check that the pooled transaction is created correctly @@ -1778,7 +1778,7 @@ mod tests { }); let signature = Signature::test_signature(); let signed_tx = TransactionSigned::new_unhashed(tx, signature); - let transaction = RecoveredTx::new_unchecked(signed_tx, Default::default()); + let transaction = Recovered::new_unchecked(signed_tx, Default::default()); let pooled_tx = EthPooledTransaction::new(transaction.clone(), 200); // Check that the pooled transaction is created correctly diff --git a/crates/transaction-pool/src/validate/eth.rs b/crates/transaction-pool/src/validate/eth.rs index ba5b38c48449..d659c5930182 100644 --- a/crates/transaction-pool/src/validate/eth.rs +++ b/crates/transaction-pool/src/validate/eth.rs @@ -892,7 +892,7 @@ mod tests { let data = hex::decode(raw).unwrap(); let tx = PooledTransaction::decode_2718(&mut data.as_ref()).unwrap(); - tx.try_into_ecrecovered().unwrap().into() + tx.try_into_recovered().unwrap().into() } // diff --git a/crates/transaction-pool/src/validate/mod.rs b/crates/transaction-pool/src/validate/mod.rs index a567bdbe5863..0ba716e0bffe 100644 --- a/crates/transaction-pool/src/validate/mod.rs +++ b/crates/transaction-pool/src/validate/mod.rs @@ -9,7 +9,7 @@ use crate::{ use alloy_eips::eip4844::BlobTransactionSidecar; use alloy_primitives::{Address, TxHash, B256, U256}; use futures_util::future::Either; -use reth_primitives::{RecoveredTx, SealedBlock}; +use reth_primitives::{Recovered, SealedBlock}; use std::{fmt, future::Future, time::Instant}; mod constants; @@ -391,7 +391,7 @@ impl ValidPoolTransaction { /// Converts to this type into the consensus transaction of the pooled transaction. /// /// Note: this takes `&self` since indented usage is via `Arc`. - pub fn to_consensus(&self) -> RecoveredTx { + pub fn to_consensus(&self) -> Recovered { self.transaction.clone_into_consensus() } diff --git a/crates/trie/parallel/src/proof.rs b/crates/trie/parallel/src/proof.rs index f7716ee13161..2dadd22f2e73 100644 --- a/crates/trie/parallel/src/proof.rs +++ b/crates/trie/parallel/src/proof.rs @@ -27,10 +27,10 @@ use reth_trie_db::{DatabaseHashedCursorFactory, DatabaseTrieCursorFactory}; use std::{sync::Arc, time::Instant}; use tracing::{debug, trace}; -#[cfg(feature = "metrics")] -use crate::metrics::ParallelStateRootMetrics; - -/// TODO: +/// Parallel proof calculator. +/// +/// This can collect proof for many targets in parallel, spawning a task for each hashed address +/// that has proof targets. #[derive(Debug)] pub struct ParallelProof { /// Consistent view of the database. @@ -48,14 +48,11 @@ pub struct ParallelProof { collect_branch_node_masks: bool, /// Thread pool for local tasks thread_pool: Arc, - /// Parallel state root metrics. - #[cfg(feature = "metrics")] - metrics: ParallelStateRootMetrics, } impl ParallelProof { /// Create new state proof generator. - pub fn new( + pub const fn new( view: ConsistentDbView, nodes_sorted: Arc, state_sorted: Arc, @@ -69,8 +66,6 @@ impl ParallelProof { prefix_sets, collect_branch_node_masks: false, thread_pool, - #[cfg(feature = "metrics")] - metrics: ParallelStateRootMetrics::default(), } } @@ -119,7 +114,7 @@ where let storage_root_targets_len = storage_root_targets.len(); debug!( - target: "trie::parallel_state_root", + target: "trie::parallel_proof", total_targets = storage_root_targets_len, "Starting parallel proof generation" ); @@ -143,7 +138,7 @@ where self.thread_pool.spawn_fifo(move || { debug!( - target: "trie::parallel", + target: "trie::parallel_proof", ?hashed_address, "Starting proof calculation" ); @@ -153,7 +148,7 @@ where let provider_start = Instant::now(); let provider_ro = view.provider_ro()?; trace!( - target: "trie::parallel", + target: "trie::parallel_proof", ?hashed_address, provider_time = ?provider_start.elapsed(), "Got provider" @@ -169,12 +164,13 @@ where &hashed_state_sorted, ); trace!( - target: "trie::parallel", + target: "trie::parallel_proof", ?hashed_address, cursor_time = ?cursor_start.elapsed(), "Created cursors" ); + let target_slots_len = target_slots.len(); let proof_start = Instant::now(); let proof_result = StorageProof::new_hashed( trie_cursor_factory, @@ -187,8 +183,10 @@ where .map_err(|e| ParallelStateRootError::Other(e.to_string())); trace!( - target: "trie::parallel", + target: "trie::parallel_proof", ?hashed_address, + prefix_set = ?prefix_set.len(), + target_slots = ?target_slots_len, proof_time = ?proof_start.elapsed(), "Completed proof calculation" ); @@ -201,7 +199,7 @@ where // `account_node_iter` below. if let Err(e) = tx.send(result) { debug!( - target: "trie::parallel", + target: "trie::parallel_proof", ?hashed_address, error = ?e, task_time = ?task_start.elapsed(), @@ -297,9 +295,6 @@ where } let _ = hash_builder.root(); - #[cfg(feature = "metrics")] - self.metrics.record_state_trie(tracker.finish()); - let account_subtree = hash_builder.take_proof_nodes(); let (branch_node_hash_masks, branch_node_tree_masks) = if self.collect_branch_node_masks { let updated_branch_nodes = hash_builder.updated_branch_nodes.unwrap_or_default(); diff --git a/crates/trie/sparse/Cargo.toml b/crates/trie/sparse/Cargo.toml index 38a71432b4f2..908d03950e9f 100644 --- a/crates/trie/sparse/Cargo.toml +++ b/crates/trie/sparse/Cargo.toml @@ -28,9 +28,12 @@ thiserror.workspace = true [dev-dependencies] reth-primitives-traits = { workspace = true, features = ["arbitrary"] } +reth-provider = { workspace = true, features = ["test-utils"] } +reth-storage-api.workspace = true +reth-testing-utils.workspace = true reth-trie = { workspace = true, features = ["test-utils"] } reth-trie-common = { workspace = true, features = ["test-utils", "arbitrary"] } -reth-testing-utils.workspace = true +reth-trie-db = { workspace = true, features = ["test-utils"] } arbitrary.workspace = true assert_matches.workspace = true @@ -44,8 +47,10 @@ rand.workspace = true [features] test-utils = [ "reth-primitives-traits/test-utils", - "reth-trie/test-utils", + "reth-provider/test-utils", "reth-trie-common/test-utils", + "reth-trie-db/test-utils", + "reth-trie/test-utils", ] arbitrary = [ "reth-primitives-traits/arbitrary", diff --git a/crates/trie/sparse/src/trie.rs b/crates/trie/sparse/src/trie.rs index f59699b34d1a..d8d41181b55e 100644 --- a/crates/trie/sparse/src/trie.rs +++ b/crates/trie/sparse/src/trie.rs @@ -1401,10 +1401,7 @@ impl SparseTrieUpdates { #[cfg(test)] mod tests { use super::*; - use alloy_primitives::{ - map::{B256HashSet, HashSet}, - U256, - }; + use alloy_primitives::{map::B256HashSet, U256}; use alloy_rlp::Encodable; use assert_matches::assert_matches; use itertools::Itertools; @@ -1413,19 +1410,21 @@ mod tests { use proptest_arbitrary_interop::arb; use rand::seq::IteratorRandom; use reth_primitives_traits::Account; + use reth_provider::{test_utils::create_test_provider_factory, TrieWriter}; use reth_trie::{ hashed_cursor::{noop::NoopHashedAccountCursor, HashedPostStateAccountCursor}, node_iter::{TrieElement, TrieNodeIter}, - trie_cursor::noop::NoopAccountTrieCursor, - updates::TrieUpdates, + trie_cursor::{noop::NoopAccountTrieCursor, TrieCursor, TrieCursorFactory}, walker::TrieWalker, BranchNode, ExtensionNode, HashedPostState, LeafNode, }; use reth_trie_common::{ proof::{ProofNodes, ProofRetainer}, + updates::TrieUpdates, HashBuilder, }; - use std::collections::BTreeMap; + use reth_trie_db::DatabaseTrieCursorFactory; + use std::collections::{BTreeMap, BTreeSet}; /// Pad nibbles to the length of a B256 hash with zeros on the left. fn pad_nibbles_left(nibbles: Nibbles) -> Nibbles { @@ -1447,6 +1446,7 @@ mod tests { /// Returns the state root and the retained proof nodes. fn run_hash_builder( state: impl IntoIterator + Clone, + trie_cursor: impl TrieCursor, destroyed_accounts: B256HashSet, proof_targets: impl IntoIterator, ) -> (B256, TrieUpdates, ProofNodes, HashMap, HashMap) @@ -1459,8 +1459,9 @@ mod tests { let mut prefix_set = PrefixSetMut::default(); prefix_set.extend_keys(state.clone().into_iter().map(|(nibbles, _)| nibbles)); - let walker = TrieWalker::new(NoopAccountTrieCursor::default(), prefix_set.freeze()) - .with_deletions_retained(true); + prefix_set.extend_keys(destroyed_accounts.iter().map(Nibbles::unpack)); + let walker = + TrieWalker::new(trie_cursor, prefix_set.freeze()).with_deletions_retained(true); let hashed_post_state = HashedPostState::default() .with_accounts(state.into_iter().map(|(nibbles, account)| { (nibbles.pack().into_inner().unwrap().into(), Some(account)) @@ -1551,7 +1552,11 @@ mod tests { (_, SparseNode::Empty | SparseNode::Hash(_)) => continue, _ => false, }; - assert!(equals, "proof node: {:?}, sparse node: {:?}", proof_node, sparse_node); + assert!( + equals, + "path: {:?}\nproof node: {:?}\nsparse node: {:?}", + proof_node_path, proof_node, sparse_node + ); } } @@ -1572,7 +1577,12 @@ mod tests { }; let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes, _, _) = - run_hash_builder([(key.clone(), value())], Default::default(), [key.clone()]); + run_hash_builder( + [(key.clone(), value())], + NoopAccountTrieCursor::default(), + Default::default(), + [key.clone()], + ); let mut sparse = RevealedSparseTrie::default().with_updates(true); sparse.update_leaf(key, value_encoded()).unwrap(); @@ -1599,6 +1609,7 @@ mod tests { let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes, _, _) = run_hash_builder( paths.iter().cloned().zip(std::iter::repeat_with(value)), + NoopAccountTrieCursor::default(), Default::default(), paths.clone(), ); @@ -1628,6 +1639,7 @@ mod tests { let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes, _, _) = run_hash_builder( paths.iter().cloned().zip(std::iter::repeat_with(value)), + NoopAccountTrieCursor::default(), Default::default(), paths.clone(), ); @@ -1665,6 +1677,7 @@ mod tests { let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes, _, _) = run_hash_builder( paths.iter().sorted_unstable().cloned().zip(std::iter::repeat_with(value)), + NoopAccountTrieCursor::default(), Default::default(), paths.clone(), ); @@ -1703,6 +1716,7 @@ mod tests { let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes, _, _) = run_hash_builder( paths.iter().cloned().zip(std::iter::repeat_with(|| old_value)), + NoopAccountTrieCursor::default(), Default::default(), paths.clone(), ); @@ -1721,6 +1735,7 @@ mod tests { let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes, _, _) = run_hash_builder( paths.iter().cloned().zip(std::iter::repeat_with(|| new_value)), + NoopAccountTrieCursor::default(), Default::default(), paths.clone(), ); @@ -2050,9 +2065,10 @@ mod tests { // to test the sparse trie updates. const KEY_NIBBLES_LEN: usize = 3; - fn test(updates: Vec<(HashMap, HashSet)>) { + fn test(updates: Vec<(BTreeMap, BTreeSet)>) { { let mut state = BTreeMap::default(); + let provider_factory = create_test_provider_factory(); let mut sparse = RevealedSparseTrie::default().with_updates(true); for (update, keys_to_delete) in updates { @@ -2072,28 +2088,36 @@ mod tests { // Insert state updates into the hash builder and calculate the root state.extend(update); + let provider = provider_factory.provider().unwrap(); + let trie_cursor = DatabaseTrieCursorFactory::new(provider.tx_ref()); let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes, _, _) = run_hash_builder( state.clone(), + trie_cursor.account_trie_cursor().unwrap(), Default::default(), state.keys().cloned().collect::>(), ); + // 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.commit().unwrap(); + // Assert that the sparse trie root matches the hash builder root assert_eq!(sparse_root, hash_builder_root); // Assert that the sparse trie updates match the hash builder updates pretty_assertions::assert_eq!( - sparse_updates.updated_nodes, - hash_builder_updates.account_nodes + BTreeMap::from_iter(sparse_updates.updated_nodes), + BTreeMap::from_iter(hash_builder_updates.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); // Delete some keys from both the hash builder and the sparse trie and check // that the sparse trie root still matches the hash builder root - for key in keys_to_delete { - state.remove(&key).unwrap(); - sparse.remove_leaf(&key).unwrap(); + for key in &keys_to_delete { + state.remove(key).unwrap(); + sparse.remove_leaf(key).unwrap(); } // We need to clone the sparse trie, so that all updated branch nodes are @@ -2103,19 +2127,30 @@ mod tests { let sparse_root = updated_sparse.root(); let sparse_updates = updated_sparse.take_updates(); + let provider = provider_factory.provider().unwrap(); + let trie_cursor = DatabaseTrieCursorFactory::new(provider.tx_ref()); let (hash_builder_root, hash_builder_updates, hash_builder_proof_nodes, _, _) = run_hash_builder( state.clone(), - Default::default(), + trie_cursor.account_trie_cursor().unwrap(), + keys_to_delete + .iter() + .map(|nibbles| B256::from_slice(&nibbles.pack())) + .collect(), state.keys().cloned().collect::>(), ); + // 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.commit().unwrap(); + // Assert that the sparse trie root matches the hash builder root assert_eq!(sparse_root, hash_builder_root); // Assert that the sparse trie updates match the hash builder updates pretty_assertions::assert_eq!( - sparse_updates.updated_nodes, - hash_builder_updates.account_nodes + BTreeMap::from_iter(sparse_updates.updated_nodes), + BTreeMap::from_iter(hash_builder_updates.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); @@ -2124,10 +2159,10 @@ mod tests { } fn transform_updates( - updates: Vec>, + updates: Vec>, mut rng: impl Rng, - ) -> Vec<(HashMap, HashSet)> { - let mut keys = HashSet::new(); + ) -> Vec<(BTreeMap, BTreeSet)> { + let mut keys = BTreeSet::new(); updates .into_iter() .map(|update| { @@ -2148,12 +2183,12 @@ mod tests { proptest!(ProptestConfig::with_cases(10), |( updates in proptest::collection::vec( - proptest::collection::hash_map( + proptest::collection::btree_map( any_with::(SizeRange::new(KEY_NIBBLES_LEN..=KEY_NIBBLES_LEN)).prop_map(pad_nibbles_right), arb::(), - 1..100, - ).prop_map(HashMap::from_iter), - 1..100, + 1..50, + ), + 1..50, ).prop_perturb(transform_updates) )| { test(updates) @@ -2187,6 +2222,7 @@ mod tests { let (_, _, hash_builder_proof_nodes, branch_node_hash_masks, branch_node_tree_masks) = run_hash_builder( [(key1(), value()), (key3(), value())], + NoopAccountTrieCursor::default(), Default::default(), [Nibbles::default()], ); @@ -2200,7 +2236,12 @@ mod tests { // Generate the proof for the first key and reveal it in the sparse trie let (_, _, hash_builder_proof_nodes, branch_node_hash_masks, branch_node_tree_masks) = - run_hash_builder([(key1(), value()), (key3(), value())], Default::default(), [key1()]); + run_hash_builder( + [(key1(), value()), (key3(), value())], + NoopAccountTrieCursor::default(), + Default::default(), + [key1()], + ); for (path, node) in hash_builder_proof_nodes.nodes_sorted() { let hash_mask = branch_node_hash_masks.get(&path).copied(); let tree_mask = branch_node_tree_masks.get(&path).copied(); @@ -2226,7 +2267,12 @@ mod tests { // Generate the proof for the third key and reveal it in the sparse trie let (_, _, hash_builder_proof_nodes, branch_node_hash_masks, branch_node_tree_masks) = - run_hash_builder([(key1(), value()), (key3(), value())], Default::default(), [key3()]); + run_hash_builder( + [(key1(), value()), (key3(), value())], + NoopAccountTrieCursor::default(), + Default::default(), + [key3()], + ); for (path, node) in hash_builder_proof_nodes.nodes_sorted() { let hash_mask = branch_node_hash_masks.get(&path).copied(); let tree_mask = branch_node_tree_masks.get(&path).copied(); @@ -2245,6 +2291,7 @@ mod tests { // compare them to the sparse trie let (_, _, hash_builder_proof_nodes, _, _) = run_hash_builder( [(key1(), value()), (key2(), value()), (key3(), value())], + NoopAccountTrieCursor::default(), Default::default(), [key1(), key2(), key3()], ); @@ -2273,6 +2320,7 @@ mod tests { let (_, _, hash_builder_proof_nodes, branch_node_hash_masks, branch_node_tree_masks) = run_hash_builder( [(key1(), value()), (key2(), value()), (key3(), value())], + NoopAccountTrieCursor::default(), Default::default(), [Nibbles::default()], ); @@ -2289,6 +2337,7 @@ mod tests { let (_, _, hash_builder_proof_nodes, branch_node_hash_masks, branch_node_tree_masks) = run_hash_builder( [(key1(), value()), (key2(), value()), (key3(), value())], + NoopAccountTrieCursor::default(), Default::default(), [key1(), Nibbles::from_nibbles_unchecked([0x01])], ); @@ -2319,6 +2368,7 @@ mod tests { let (_, _, hash_builder_proof_nodes, branch_node_hash_masks, branch_node_tree_masks) = run_hash_builder( [(key1(), value()), (key2(), value()), (key3(), value())], + NoopAccountTrieCursor::default(), Default::default(), [key2()], ); @@ -2361,6 +2411,7 @@ mod tests { let (_, _, hash_builder_proof_nodes, branch_node_hash_masks, branch_node_tree_masks) = run_hash_builder( [(key1(), value()), (key2(), value())], + NoopAccountTrieCursor::default(), Default::default(), [Nibbles::default()], ); @@ -2389,7 +2440,12 @@ mod tests { // Generate the proof for the first key and reveal it in the sparse trie let (_, _, hash_builder_proof_nodes, branch_node_hash_masks, branch_node_tree_masks) = - run_hash_builder([(key1(), value()), (key2(), value())], Default::default(), [key1()]); + run_hash_builder( + [(key1(), value()), (key2(), value())], + NoopAccountTrieCursor::default(), + Default::default(), + [key1()], + ); for (path, node) in hash_builder_proof_nodes.nodes_sorted() { let hash_mask = branch_node_hash_masks.get(&path).copied(); let tree_mask = branch_node_tree_masks.get(&path).copied(); @@ -2491,6 +2547,7 @@ mod tests { let (hash_builder_root, hash_builder_updates, _, _, _) = run_hash_builder( [(key1(), value()), (key2(), value())], + NoopAccountTrieCursor::default(), Default::default(), [Nibbles::default()], ); diff --git a/crates/trie/trie/src/proof/blinded.rs b/crates/trie/trie/src/proof/blinded.rs index 9b838c2e9dc6..9e802cf5256f 100644 --- a/crates/trie/trie/src/proof/blinded.rs +++ b/crates/trie/trie/src/proof/blinded.rs @@ -9,8 +9,8 @@ use reth_trie_common::{prefix_set::TriePrefixSetsMut, Nibbles}; use reth_trie_sparse::blinded::{ pad_path_to_key, BlindedProvider, BlindedProviderFactory, RevealedNode, }; -use std::sync::Arc; -use tracing::trace; +use std::{sync::Arc, time::Instant}; +use tracing::{enabled, trace, Level}; /// Factory for instantiating providers capable of retrieving blinded trie nodes via proofs. #[derive(Debug)] @@ -88,6 +88,8 @@ where H: HashedCursorFactory + Clone + Send + Sync, { fn blinded_node(&mut self, path: &Nibbles) -> Result, SparseTrieError> { + let start = enabled!(target: "trie::proof::blinded", Level::TRACE).then(Instant::now); + let targets = HashMap::from_iter([(pad_path_to_key(path), HashSet::default())]); let mut proof = Proof::new(self.trie_cursor_factory.clone(), self.hashed_cursor_factory.clone()) @@ -98,8 +100,16 @@ where let node = proof.account_subtree.into_inner().remove(path); let tree_mask = proof.branch_node_tree_masks.remove(path); let hash_mask = proof.branch_node_hash_masks.remove(path); - trace!(target: "trie::proof::blinded", ?path, ?node, "Blinded node for account trie"); + trace!( + target: "trie::proof::blinded", + elapsed = ?start.unwrap().elapsed(), + ?path, + ?node, + ?tree_mask, + ?hash_mask, + "Blinded node for account trie" + ); Ok(node.map(|node| RevealedNode { node, tree_mask, hash_mask })) } } @@ -135,6 +145,8 @@ where H: HashedCursorFactory + Clone + Send + Sync, { fn blinded_node(&mut self, path: &Nibbles) -> Result, SparseTrieError> { + let start = enabled!(target: "trie::proof::blinded", Level::TRACE).then(Instant::now); + let targets = HashSet::from_iter([pad_path_to_key(path)]); let storage_prefix_set = self.prefix_sets.storage_prefix_sets.get(&self.account).cloned().unwrap_or_default(); @@ -150,8 +162,17 @@ where let node = proof.subtree.into_inner().remove(path); let tree_mask = proof.branch_node_tree_masks.remove(path); let hash_mask = proof.branch_node_hash_masks.remove(path); - trace!(target: "trie::proof::blinded", account = ?self.account, ?path, ?node, "Blinded node for storage trie"); + trace!( + target: "trie::proof::blinded", + account = ?self.account, + elapsed = ?start.unwrap().elapsed(), + ?path, + ?node, + ?tree_mask, + ?hash_mask, + "Blinded node for storage trie" + ); Ok(node.map(|node| RevealedNode { node, tree_mask, hash_mask })) } } diff --git a/examples/bsc-p2p/src/main.rs b/examples/bsc-p2p/src/main.rs index a2dec469591b..d0ea0f6d9eb5 100644 --- a/examples/bsc-p2p/src/main.rs +++ b/examples/bsc-p2p/src/main.rs @@ -14,9 +14,7 @@ use chainspec::{boot_nodes, bsc_chain_spec}; use reth_discv4::Discv4ConfigBuilder; -use reth_network::{ - EthNetworkPrimitives, NetworkConfig, NetworkEvent, NetworkEventListenerProvider, NetworkManager, -}; +use reth_network::{NetworkConfig, NetworkEvent, NetworkEventListenerProvider, NetworkManager}; use reth_network_api::{ events::{PeerEvent, SessionInfo}, PeersInfo, @@ -69,7 +67,7 @@ async fn main() { // latest BSC forkId, we need to override this to allow connections from BSC nodes let fork_id = ForkId { hash: ForkHash([0x07, 0xb5, 0x43, 0x28]), next: 0 }; net_cfg.fork_filter.set_current_fork_id(fork_id); - let net_manager = NetworkManager::::new(net_cfg).await.unwrap(); + let net_manager = NetworkManager::eth(net_cfg).await.unwrap(); // The network handle is our entrypoint into the network. let net_handle = net_manager.handle().clone(); diff --git a/examples/custom-beacon-withdrawals/src/main.rs b/examples/custom-beacon-withdrawals/src/main.rs index 7bb8a77d2598..8e52447ac482 100644 --- a/examples/custom-beacon-withdrawals/src/main.rs +++ b/examples/custom-beacon-withdrawals/src/main.rs @@ -7,23 +7,23 @@ use alloy_consensus::BlockHeader; use alloy_eips::{eip4895::Withdrawal, eip7685::Requests}; use alloy_sol_macro::sol; use alloy_sol_types::SolCall; -#[cfg(feature = "optimism")] -use reth::revm::primitives::OptimismFields; use reth::{ api::{ConfigureEvm, NodeTypesWithEngine}, builder::{components::ExecutorBuilder, BuilderContext, FullNodeTypes}, cli::Cli, providers::ProviderError, revm::{ - interpreter::Host, - primitives::{address, Address, Bytes, Env, TransactTo, TxEnv, U256}, - Database, DatabaseCommit, Evm, State, + primitives::{address, Address}, + Database, DatabaseCommit, State, }, }; use reth_chainspec::{ChainSpec, EthereumHardforks}; -use reth_evm::execute::{ - BlockExecutionError, BlockExecutionStrategy, BlockExecutionStrategyFactory, ExecuteOutput, - InternalBlockExecutionError, +use reth_evm::{ + execute::{ + BlockExecutionError, BlockExecutionStrategy, BlockExecutionStrategyFactory, ExecuteOutput, + InternalBlockExecutionError, + }, + Evm, }; use reth_evm_ethereum::EthEvmConfig; use reth_node_ethereum::{node::EthereumAddOns, BasicBlockExecutorProvider, EthereumNode}; @@ -177,19 +177,11 @@ sol!( /// Applies the post-block call to the withdrawal / deposit contract, using the given block, /// [`ChainSpec`], EVM. -pub fn apply_withdrawals_contract_call( +pub fn apply_withdrawals_contract_call( withdrawals: &[Withdrawal], - evm: &mut Evm<'_, EXT, DB>, -) -> Result<(), BlockExecutionError> -where - DB::Error: std::fmt::Display, -{ - // get previous env - let previous_env = Box::new(evm.context.env().clone()); - - // modify env for pre block call - fill_tx_env_with_system_contract_call( - &mut evm.context.evm.env, + evm: &mut impl Evm, +) -> Result<(), BlockExecutionError> { + let mut state = match evm.transact_system_call( SYSTEM_ADDRESS, WITHDRAWALS_ADDRESS, withdrawalsCall { @@ -198,12 +190,9 @@ where } .abi_encode() .into(), - ); - - let mut state = match evm.transact() { + ) { Ok(res) => res.state, Err(e) => { - evm.context.evm.env = previous_env; return Err(BlockExecutionError::Internal(InternalBlockExecutionError::Other( format!("withdrawal contract system call revert: {}", e).into(), ))) @@ -213,47 +202,8 @@ where // Clean-up post system tx context state.remove(&SYSTEM_ADDRESS); state.remove(&evm.block().coinbase); - evm.context.evm.db.commit(state); - // re-set the previous env - evm.context.evm.env = previous_env; - Ok(()) -} - -fn fill_tx_env_with_system_contract_call( - env: &mut Env, - caller: Address, - contract: Address, - data: Bytes, -) { - env.tx = TxEnv { - caller, - transact_to: TransactTo::Call(contract), - // Explicitly set nonce to None so revm does not do any nonce checks - nonce: None, - gas_limit: 30_000_000, - value: U256::ZERO, - data, - // Setting the gas price to zero enforces that no value is transferred as part of the call, - // and that the call will not count against the block's gas limit - gas_price: U256::ZERO, - // The chain ID check is not relevant here and is disabled if set to None - chain_id: None, - // Setting the gas priority fee to None ensures the effective gas price is derived from the - // `gas_price` field, which we need to be zero - gas_priority_fee: None, - access_list: Vec::new(), - // blob fields can be None for this tx - blob_hashes: Vec::new(), - max_fee_per_blob_gas: None, - authorization_list: None, - #[cfg(feature = "optimism")] - optimism: OptimismFields::default(), - }; - - // ensure the block gas limit is >= the tx - env.block.gas_limit = U256::from(env.tx.gas_limit); + evm.db_mut().commit(state); - // disable the base fee check for this call by setting the base fee to zero - env.block.basefee = U256::ZERO; + Ok(()) } diff --git a/examples/custom-evm/src/main.rs b/examples/custom-evm/src/main.rs index ac534d43c677..4efa8d86b76d 100644 --- a/examples/custom-evm/src/main.rs +++ b/examples/custom-evm/src/main.rs @@ -15,8 +15,8 @@ use reth::{ handler::register::EvmHandler, inspector_handle_register, precompile::{Precompile, PrecompileOutput, PrecompileSpecId}, - primitives::{CfgEnvWithHandlerCfg, Env, PrecompileResult, TxEnv}, - ContextPrecompiles, Database, Evm, EvmBuilder, GetInspector, + primitives::{CfgEnvWithHandlerCfg, Env, HandlerCfg, PrecompileResult, SpecId, TxEnv}, + ContextPrecompiles, Database, EvmBuilder, GetInspector, }, rpc::types::engine::PayloadAttributes, tasks::TaskManager, @@ -24,7 +24,7 @@ use reth::{ }; use reth_chainspec::{Chain, ChainSpec}; use reth_evm::env::EvmEnv; -use reth_evm_ethereum::EthEvmConfig; +use reth_evm_ethereum::{EthEvm, EthEvmConfig}; use reth_node_api::{ ConfigureEvm, ConfigureEvmEnv, FullNodeTypes, NextBlockEnvAttributes, NodeTypes, NodeTypesWithEngine, PayloadTypes, @@ -86,69 +86,70 @@ impl MyEvmConfig { impl ConfigureEvmEnv for MyEvmConfig { type Header = Header; type Transaction = TransactionSigned; - type Error = Infallible; + type TxEnv = TxEnv; + type Spec = SpecId; - fn fill_tx_env(&self, tx_env: &mut TxEnv, transaction: &TransactionSigned, sender: Address) { - self.inner.fill_tx_env(tx_env, transaction, sender); - } - - fn fill_tx_env_system_contract_call( - &self, - env: &mut Env, - caller: Address, - contract: Address, - data: Bytes, - ) { - self.inner.fill_tx_env_system_contract_call(env, caller, contract, data); + fn tx_env(&self, transaction: &Self::Transaction, signer: Address) -> Self::TxEnv { + self.inner.tx_env(transaction, signer) } - fn fill_cfg_env(&self, cfg_env: &mut CfgEnvWithHandlerCfg, header: &Self::Header) { - self.inner.fill_cfg_env(cfg_env, header); + fn evm_env(&self, header: &Self::Header) -> EvmEnv { + self.inner.evm_env(header) } - fn next_cfg_and_block_env( + fn next_evm_env( &self, parent: &Self::Header, attributes: NextBlockEnvAttributes, ) -> Result { - self.inner.next_cfg_and_block_env(parent, attributes) + self.inner.next_evm_env(parent, attributes) } } impl ConfigureEvm for MyEvmConfig { - fn evm_with_env(&self, db: DB, evm_env: EvmEnv, tx: TxEnv) -> Evm<'_, (), DB> { + type Evm<'a, DB: Database + 'a, I: 'a> = EthEvm<'a, I, DB>; + + fn evm_with_env(&self, db: DB, evm_env: EvmEnv) -> Self::Evm<'_, DB, ()> { + let cfg_env_with_handler_cfg = CfgEnvWithHandlerCfg { + cfg_env: evm_env.cfg_env, + handler_cfg: HandlerCfg::new(evm_env.spec), + }; EvmBuilder::default() .with_db(db) - .with_cfg_env_with_handler_cfg(evm_env.cfg_env_with_handler_cfg) + .with_cfg_env_with_handler_cfg(cfg_env_with_handler_cfg) .with_block_env(evm_env.block_env) - .with_tx_env(tx) // add additional precompiles .append_handler_register(MyEvmConfig::set_precompiles) .build() + .into() } fn evm_with_env_and_inspector( &self, db: DB, evm_env: EvmEnv, - tx: TxEnv, inspector: I, - ) -> Evm<'_, I, DB> + ) -> Self::Evm<'_, DB, I> where DB: Database, I: GetInspector, { + let cfg_env_with_handler_cfg = CfgEnvWithHandlerCfg { + cfg_env: evm_env.cfg_env, + handler_cfg: HandlerCfg::new(evm_env.spec), + }; + EvmBuilder::default() .with_db(db) .with_external_context(inspector) - .with_cfg_env_with_handler_cfg(evm_env.cfg_env_with_handler_cfg) + .with_cfg_env_with_handler_cfg(cfg_env_with_handler_cfg) .with_block_env(evm_env.block_env) - .with_tx_env(tx) // add additional precompiles .append_handler_register(MyEvmConfig::set_precompiles) .append_handler_register(inspector_handle_register) .build() + .into() } } diff --git a/examples/custom-inspector/src/main.rs b/examples/custom-inspector/src/main.rs index 5df076008bdd..4e09133766cb 100644 --- a/examples/custom-inspector/src/main.rs +++ b/examples/custom-inspector/src/main.rs @@ -30,7 +30,7 @@ use reth::{ }; use reth_evm::EvmEnv; use reth_node_ethereum::node::EthereumNode; -use revm_primitives::CfgEnvWithHandlerCfg; +use revm_primitives::HandlerCfg; fn main() { Cli::::parse() @@ -65,13 +65,9 @@ fn main() { BlockNumberOrTag::Latest.into(), EvmOverrides::default(), move |db, evm_env, tx_env| { - let EvmEnv { - cfg_env_with_handler_cfg: - CfgEnvWithHandlerCfg { handler_cfg, cfg_env }, - block_env, - } = evm_env; + let EvmEnv { cfg_env, block_env, spec } = evm_env; let env = EnvWithHandlerCfg { - handler_cfg, + handler_cfg: HandlerCfg::new(spec), env: Env::boxed(cfg_env, block_env, tx_env), }; let mut dummy_inspector = DummyInspector::default(); diff --git a/examples/custom-payload-builder/src/generator.rs b/examples/custom-payload-builder/src/generator.rs index 76222e0ccde5..dfb973a96fc4 100644 --- a/examples/custom-payload-builder/src/generator.rs +++ b/examples/custom-payload-builder/src/generator.rs @@ -80,7 +80,7 @@ where .ok_or_else(|| PayloadBuilderError::MissingParentBlock(attributes.parent()))?; // we already know the hash, so we can seal it - block.seal(attributes.parent()) + block.seal_unchecked(attributes.parent()) }; let hash = parent_block.hash(); let header = SealedHeader::new(parent_block.header().clone(), hash); diff --git a/examples/custom-rlpx-subprotocol/src/main.rs b/examples/custom-rlpx-subprotocol/src/main.rs index 702d0e8cf5ef..7b52dd75056e 100644 --- a/examples/custom-rlpx-subprotocol/src/main.rs +++ b/examples/custom-rlpx-subprotocol/src/main.rs @@ -14,8 +14,8 @@ use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; use reth::builder::NodeHandle; use reth_network::{ - config::SecretKey, protocol::IntoRlpxSubProtocol, EthNetworkPrimitives, NetworkConfig, - NetworkManager, NetworkProtocols, + config::SecretKey, protocol::IntoRlpxSubProtocol, NetworkConfig, NetworkManager, + NetworkProtocols, }; use reth_network_api::{test_utils::PeersHandleProvider, NetworkInfo}; use reth_node_ethereum::EthereumNode; @@ -53,7 +53,7 @@ fn main() -> eyre::Result<()> { .build_with_noop_provider(node.chain_spec()); // spawn the second network instance - let subnetwork = NetworkManager::::new(net_cfg).await?; + let subnetwork = NetworkManager::eth(net_cfg).await?; let subnetwork_peer_id = *subnetwork.peer_id(); let subnetwork_peer_addr = subnetwork.local_addr(); let subnetwork_handle = subnetwork.peers_handle(); diff --git a/examples/network/src/main.rs b/examples/network/src/main.rs index bd4f232a754c..ba3b46e53767 100644 --- a/examples/network/src/main.rs +++ b/examples/network/src/main.rs @@ -8,8 +8,7 @@ use futures::StreamExt; use reth_network::{ - config::rng_secret_key, EthNetworkPrimitives, NetworkConfig, NetworkEventListenerProvider, - NetworkManager, + config::rng_secret_key, NetworkConfig, NetworkEventListenerProvider, NetworkManager, }; use reth_provider::test_utils::NoopProvider; @@ -25,7 +24,7 @@ async fn main() -> eyre::Result<()> { let config = NetworkConfig::builder(local_key).mainnet_boot_nodes().build(client); // create the network instance - let network = NetworkManager::::new(config).await?; + let network = NetworkManager::eth(config).await?; // get a handle to the network to interact with it let handle = network.handle().clone(); diff --git a/examples/polygon-p2p/src/main.rs b/examples/polygon-p2p/src/main.rs index bae5399d9cd6..b4ff9b59d370 100644 --- a/examples/polygon-p2p/src/main.rs +++ b/examples/polygon-p2p/src/main.rs @@ -12,8 +12,7 @@ use chain_cfg::{boot_nodes, head, polygon_chain_spec}; use reth_discv4::Discv4ConfigBuilder; use reth_network::{ - config::NetworkMode, EthNetworkPrimitives, NetworkConfig, NetworkEvent, - NetworkEventListenerProvider, NetworkManager, + config::NetworkMode, NetworkConfig, NetworkEvent, NetworkEventListenerProvider, NetworkManager, }; use reth_network_api::events::SessionInfo; use reth_tracing::{ @@ -59,7 +58,7 @@ async fn main() { discv4_cfg.add_boot_nodes(boot_nodes()).lookup_interval(interval); let net_cfg = net_cfg.set_discovery_v4(discv4_cfg.build()); - let net_manager = NetworkManager::::new(net_cfg).await.unwrap(); + let net_manager = NetworkManager::eth(net_cfg).await.unwrap(); // The network handle is our entrypoint into the network. let net_handle = net_manager.handle(); diff --git a/examples/stateful-precompile/src/main.rs b/examples/stateful-precompile/src/main.rs index 2cdebfe5602c..92599b79f1fa 100644 --- a/examples/stateful-precompile/src/main.rs +++ b/examples/stateful-precompile/src/main.rs @@ -14,9 +14,10 @@ use reth::{ inspector_handle_register, precompile::{Precompile, PrecompileSpecId}, primitives::{ - CfgEnvWithHandlerCfg, Env, PrecompileResult, SpecId, StatefulPrecompileMut, TxEnv, + CfgEnvWithHandlerCfg, Env, HandlerCfg, PrecompileResult, SpecId, StatefulPrecompileMut, + TxEnv, }, - ContextPrecompile, ContextPrecompiles, Database, Evm, EvmBuilder, GetInspector, + ContextPrecompile, ContextPrecompiles, Database, EvmBuilder, GetInspector, }, tasks::TaskManager, }; @@ -25,8 +26,8 @@ use reth_evm::env::EvmEnv; use reth_node_api::{ConfigureEvm, ConfigureEvmEnv, FullNodeTypes, NodeTypes}; use reth_node_core::{args::RpcServerArgs, node_config::NodeConfig}; use reth_node_ethereum::{ - node::EthereumAddOns, BasicBlockExecutorProvider, EthEvmConfig, EthExecutionStrategyFactory, - EthereumNode, + evm::EthEvm, node::EthereumAddOns, BasicBlockExecutorProvider, EthEvmConfig, + EthExecutionStrategyFactory, EthereumNode, }; use reth_primitives::{EthPrimitives, TransactionSigned}; use reth_tracing::{RethTracer, Tracer}; @@ -150,73 +151,75 @@ impl ConfigureEvmEnv for MyEvmConfig { type Header = Header; type Transaction = TransactionSigned; type Error = Infallible; + type TxEnv = TxEnv; + type Spec = SpecId; - fn fill_tx_env(&self, tx_env: &mut TxEnv, transaction: &TransactionSigned, sender: Address) { - self.inner.fill_tx_env(tx_env, transaction, sender) + fn tx_env(&self, transaction: &Self::Transaction, signer: Address) -> Self::TxEnv { + self.inner.tx_env(transaction, signer) } - fn fill_tx_env_system_contract_call( - &self, - env: &mut Env, - caller: Address, - contract: Address, - data: Bytes, - ) { - self.inner.fill_tx_env_system_contract_call(env, caller, contract, data) - } - - fn fill_cfg_env(&self, cfg_env: &mut CfgEnvWithHandlerCfg, header: &Self::Header) { - self.inner.fill_cfg_env(cfg_env, header) + fn evm_env(&self, header: &Self::Header) -> EvmEnv { + self.inner.evm_env(header) } - fn next_cfg_and_block_env( + fn next_evm_env( &self, parent: &Self::Header, attributes: NextBlockEnvAttributes, ) -> Result { - self.inner.next_cfg_and_block_env(parent, attributes) + self.inner.next_evm_env(parent, attributes) } } impl ConfigureEvm for MyEvmConfig { - fn evm_with_env(&self, db: DB, evm_env: EvmEnv, tx: TxEnv) -> Evm<'_, (), DB> { + type Evm<'a, DB: Database + 'a, I: 'a> = EthEvm<'a, I, DB>; + + fn evm_with_env(&self, db: DB, evm_env: EvmEnv) -> Self::Evm<'_, DB, ()> { + let cfg_env_with_handler_cfg = CfgEnvWithHandlerCfg { + cfg_env: evm_env.cfg_env, + handler_cfg: HandlerCfg::new(evm_env.spec), + }; + let new_cache = self.precompile_cache.clone(); EvmBuilder::default() .with_db(db) - .with_cfg_env_with_handler_cfg(evm_env.cfg_env_with_handler_cfg) + .with_cfg_env_with_handler_cfg(cfg_env_with_handler_cfg) .with_block_env(evm_env.block_env) - .with_tx_env(tx) // add additional precompiles .append_handler_register_box(Box::new(move |handler| { MyEvmConfig::set_precompiles(handler, new_cache.clone()) })) .build() + .into() } fn evm_with_env_and_inspector( &self, db: DB, evm_env: EvmEnv, - tx: TxEnv, inspector: I, - ) -> Evm<'_, I, DB> + ) -> Self::Evm<'_, DB, I> where DB: Database, I: GetInspector, { + let cfg_env_with_handler_cfg = CfgEnvWithHandlerCfg { + cfg_env: evm_env.cfg_env, + handler_cfg: HandlerCfg::new(evm_env.spec), + }; let new_cache = self.precompile_cache.clone(); EvmBuilder::default() .with_db(db) .with_external_context(inspector) - .with_cfg_env_with_handler_cfg(evm_env.cfg_env_with_handler_cfg) + .with_cfg_env_with_handler_cfg(cfg_env_with_handler_cfg) .with_block_env(evm_env.block_env) - .with_tx_env(tx) // add additional precompiles .append_handler_register_box(Box::new(move |handler| { MyEvmConfig::set_precompiles(handler, new_cache.clone()) })) .append_handler_register(inspector_handle_register) .build() + .into() } } diff --git a/testing/ef-tests/src/cases/blockchain_test.rs b/testing/ef-tests/src/cases/blockchain_test.rs index a3c168cb54de..85e7eb9dc911 100644 --- a/testing/ef-tests/src/cases/blockchain_test.rs +++ b/testing/ef-tests/src/cases/blockchain_test.rs @@ -206,6 +206,18 @@ pub fn should_skip(path: &Path) -> bool { | "loopMul.json" | "CALLBlake2f_MaxRounds.json" | "shiftCombinations.json" + + // Skipped by revm as well: + | "RevertInCreateInInit_Paris.json" + | "RevertInCreateInInit.json" + | "dynamicAccountOverwriteEmpty.json" + | "dynamicAccountOverwriteEmpty_Paris.json" + | "RevertInCreateInInitCreate2Paris.json" + | "create2collisionStorage.json" + | "RevertInCreateInInitCreate2.json" + | "create2collisionStorageParis.json" + | "InitCollision.json" + | "InitCollisionParis.json" ) // Ignore outdated EOF tests that haven't been updated for Cancun yet. || path_contains(path_str, &["EIPTests", "stEOF"]) diff --git a/testing/ef-tests/tests/tests.rs b/testing/ef-tests/tests/tests.rs index 3aeb917a24c2..6b626d82c186 100644 --- a/testing/ef-tests/tests/tests.rs +++ b/testing/ef-tests/tests/tests.rs @@ -78,4 +78,14 @@ mod general_state_tests { general_state_test!(vm_tests, VMTests); } -// TODO: Add ValidBlocks and InvalidBlocks tests +macro_rules! blockchain_test { + ($test_name:ident, $dir:ident) => { + #[test] + fn $test_name() { + BlockchainTests::new(format!("{}", stringify!($dir))).run(); + } + }; +} + +blockchain_test!(valid_blocks, ValidBlocks); +// blockchain_test!(invalid_blocks, InvalidBlocks); diff --git a/testing/testing-utils/src/generators.rs b/testing/testing-utils/src/generators.rs index 895155cc7f04..1ffe4178ae2a 100644 --- a/testing/testing-utils/src/generators.rs +++ b/testing/testing-utils/src/generators.rs @@ -12,11 +12,11 @@ use rand::{ distributions::uniform::SampleRange, rngs::StdRng, seq::SliceRandom, thread_rng, SeedableRng, }; use reth_primitives::{ - proofs, Account, BlockBody, Log, Receipt, SealedBlock, SealedHeader, StorageEntry, Transaction, + Account, BlockBody, Log, Receipt, SealedBlock, SealedHeader, StorageEntry, Transaction, TransactionSigned, }; -use reth_primitives_traits::{crypto::secp256k1::sign_message, Block as _}; +use reth_primitives_traits::{crypto::secp256k1::sign_message, proofs, Block as _}; use secp256k1::{Keypair, Secp256k1}; use std::{ cmp::{max, min},