diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml new file mode 100644 index 000000000000..03c491b368a6 --- /dev/null +++ b/.github/workflows/windows.yml @@ -0,0 +1,47 @@ +# Windows build + +name: windows + +on: + push: + branches: [main] + pull_request: + branches: [main] + merge_group: + +jobs: + 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 + + check-op-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 OP-Reth + run: cargo check -p op-reth --features optimism --target x86_64-pc-windows-gnu diff --git a/Cargo.lock b/Cargo.lock index d4fa4c6a6a26..93ae84221192 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] @@ -97,9 +97,9 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "alloy-chains" -version = "0.1.34" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8158b4878c67837e5413721cc44298e6a2d88d39203175ea025e51892a16ba4c" +checksum = "94c225801d42099570d0674701dddd4142f0ef715282aeb5985042e2ec962df7" dependencies = [ "alloy-rlp", "arbitrary", @@ -296,7 +296,7 @@ dependencies = [ "getrandom 0.2.15", "hashbrown 0.14.5", "hex-literal", - "indexmap 2.5.0", + "indexmap 2.6.0", "itoa", "k256", "keccak-asm", @@ -619,7 +619,7 @@ dependencies = [ "alloy-sol-macro-input", "const-hex", "heck", - "indexmap 2.5.0", + "indexmap 2.6.0", "proc-macro-error2", "proc-macro2", "quote", @@ -1016,9 +1016,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fec134f64e2bc57411226dfc4e52dec859ddfc7e711fc5e07b612584f000e4aa" +checksum = "7e614738943d3f68c628ae3dbce7c3daffb196665f82f8c8ea6b65de73c79429" dependencies = [ "brotli", "flate2", @@ -1203,26 +1203,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bindgen" -version = "0.69.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" -dependencies = [ - "bitflags 2.6.0", - "cexpr", - "clang-sys", - "itertools 0.12.1", - "lazy_static", - "lazycell", - "proc-macro2", - "quote", - "regex", - "rustc-hash 1.1.0", - "shlex", - "syn 2.0.79", -] - [[package]] name = "bindgen" version = "0.70.1" @@ -1333,7 +1313,7 @@ dependencies = [ "bitflags 2.6.0", "boa_interner", "boa_macros", - "indexmap 2.5.0", + "indexmap 2.6.0", "num-bigint", "rustc-hash 2.0.0", ] @@ -1359,7 +1339,7 @@ dependencies = [ "fast-float", "hashbrown 0.14.5", "icu_normalizer", - "indexmap 2.5.0", + "indexmap 2.6.0", "intrusive-collections", "itertools 0.13.0", "num-bigint", @@ -1405,7 +1385,7 @@ dependencies = [ "boa_gc", "boa_macros", "hashbrown 0.14.5", - "indexmap 2.5.0", + "indexmap 2.6.0", "once_cell", "phf", "rustc-hash 2.0.0", @@ -1473,9 +1453,9 @@ dependencies = [ [[package]] name = "brotli" -version = "6.0.0" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" +checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -1619,9 +1599,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.24" +version = "1.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812acba72f0a070b003d3697490d2b55b837230ae7c6c6497f05cc2ddbb8d938" +checksum = "e8d9e0b4957f635b8d3da819d0db5603620467ecf1f692d22a8c2717ce27e6d8" dependencies = [ "jobserver", "libc", @@ -2563,7 +2543,7 @@ dependencies = [ [[package]] name = "ef-tests" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -3133,9 +3113,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -3148,9 +3128,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -3158,15 +3138,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -3175,9 +3155,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" @@ -3196,9 +3176,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", @@ -3207,15 +3187,15 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-timer" @@ -3229,9 +3209,9 @@ dependencies = [ [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", @@ -3311,9 +3291,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.31.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "glob" @@ -3390,7 +3370,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.5.0", + "indexmap 2.6.0", "slab", "tokio", "tokio-util", @@ -3436,6 +3416,12 @@ dependencies = [ "serde", ] +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" + [[package]] name = "hashlink" version = "0.8.4" @@ -3963,13 +3949,13 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "arbitrary", "equivalent", - "hashbrown 0.14.5", + "hashbrown 0.15.0", "serde", ] @@ -3986,7 +3972,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "232929e1d75fe899576a3d5c7416ad0d88dbfbb3c3d6aa00873a7408a50ddb88" dependencies = [ "ahash", - "indexmap 2.5.0", + "indexmap 2.6.0", "is-terminal", "itoa", "log", @@ -4084,9 +4070,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.10.0" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" [[package]] name = "iri-string" @@ -4124,15 +4110,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.13.0" @@ -4449,12 +4426,6 @@ dependencies = [ "spin", ] -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - [[package]] name = "libc" version = "0.2.159" @@ -4498,11 +4469,11 @@ dependencies = [ [[package]] name = "libproc" -version = "0.14.8" +version = "0.14.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae9ea4b75e1a81675429dafe43441df1caea70081e82246a8cccf514884a88bb" +checksum = "78cca3586d5efa98fba425856ba227950fd4287525dd5317b352f476ca7d603b" dependencies = [ - "bindgen 0.69.4", + "bindgen", "errno", "libc", ] @@ -4723,7 +4694,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4f0c8427b39666bf970460908b213ec09b3b350f20c0c2eabcbba51704a08e6" dependencies = [ "base64 0.22.1", - "indexmap 2.5.0", + "indexmap 2.6.0", "metrics", "metrics-util", "quanta", @@ -5134,21 +5105,18 @@ dependencies = [ [[package]] name = "object" -version = "0.36.4" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.20.1" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" -dependencies = [ - "portable-atomic", -] +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "oorandom" @@ -5252,13 +5220,19 @@ dependencies = [ [[package]] name = "op-reth" -version = "1.0.7" +version = "1.0.8" dependencies = [ "clap", "reth-cli-util", "reth-node-builder", + "reth-optimism-chainspec", "reth-optimism-cli", + "reth-optimism-consensus", + "reth-optimism-evm", + "reth-optimism-forks", "reth-optimism-node", + "reth-optimism-payload-builder", + "reth-optimism-primitives", "reth-optimism-rpc", "reth-provider", "tracing", @@ -5488,18 +5462,18 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.5" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +checksum = "baf123a161dde1e524adf36f90bc5d8d3462824a9c43553ad07a8183161189ec" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.5" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +checksum = "a4502d8515ca9f32f1fb543d987f63d95a14934883db45bdb48060b6b69257f8" dependencies = [ "proc-macro2", "quote", @@ -6035,9 +6009,9 @@ dependencies = [ [[package]] name = "raw-cpuid" -version = "11.1.0" +version = "11.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb9ee317cfe3fbd54b36a511efc1edd42e216903c9cd575e686dd68a2ba90d8d" +checksum = "1ab240315c661615f2ee9f0f2cd32d5a7343a84d5ebcccb99d46e6637565e7b0" dependencies = [ "bitflags 2.6.0", ] @@ -6208,7 +6182,7 @@ dependencies = [ [[package]] name = "reth" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -6237,6 +6211,7 @@ dependencies = [ "reth-downloaders", "reth-engine-util", "reth-errors", + "reth-ethereum-cli", "reth-ethereum-payload-builder", "reth-evm", "reth-execution-types", @@ -6280,7 +6255,7 @@ dependencies = [ [[package]] name = "reth-auto-seal-consensus" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rpc-types-engine", @@ -6310,7 +6285,7 @@ dependencies = [ [[package]] name = "reth-basic-payload-builder" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -6333,7 +6308,7 @@ dependencies = [ [[package]] name = "reth-beacon-consensus" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-genesis", "alloy-primitives", @@ -6384,7 +6359,7 @@ dependencies = [ [[package]] name = "reth-bench" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-json-rpc", @@ -6419,7 +6394,7 @@ dependencies = [ [[package]] name = "reth-blockchain-tree" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -6457,7 +6432,7 @@ dependencies = [ [[package]] name = "reth-blockchain-tree-api" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "reth-consensus", @@ -6469,7 +6444,7 @@ dependencies = [ [[package]] name = "reth-chain-state" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -6497,7 +6472,7 @@ dependencies = [ [[package]] name = "reth-chainspec" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-chains", "alloy-eips", @@ -6508,28 +6483,29 @@ dependencies = [ "auto_impl", "derive_more 1.0.0", "once_cell", - "op-alloy-rpc-types", "reth-ethereum-forks", "reth-network-peers", "reth-optimism-forks", "reth-primitives-traits", "reth-trie-common", - "serde", "serde_json", ] [[package]] name = "reth-cli" -version = "1.0.7" +version = "1.0.8" dependencies = [ + "alloy-genesis", "clap", "eyre", "reth-cli-runner", + "serde_json", + "shellexpand", ] [[package]] name = "reth-cli-commands" -version = "1.0.7" +version = "1.0.8" dependencies = [ "ahash", "alloy-eips", @@ -6561,6 +6537,7 @@ dependencies = [ "reth-downloaders", "reth-ecies", "reth-eth-wire", + "reth-ethereum-cli", "reth-evm", "reth-exex", "reth-fs-util", @@ -6589,7 +6566,7 @@ dependencies = [ [[package]] name = "reth-cli-runner" -version = "1.0.7" +version = "1.0.8" dependencies = [ "reth-tasks", "tokio", @@ -6598,7 +6575,7 @@ dependencies = [ [[package]] name = "reth-cli-util" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-primitives", @@ -6615,7 +6592,7 @@ dependencies = [ [[package]] name = "reth-codecs" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -6638,7 +6615,7 @@ dependencies = [ [[package]] name = "reth-codecs-derive" -version = "1.0.7" +version = "1.0.8" dependencies = [ "convert_case", "proc-macro2", @@ -6649,7 +6626,7 @@ dependencies = [ [[package]] name = "reth-config" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "eyre", @@ -6665,7 +6642,7 @@ dependencies = [ [[package]] name = "reth-consensus" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "auto_impl", @@ -6675,7 +6652,7 @@ dependencies = [ [[package]] name = "reth-consensus-common" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -6690,7 +6667,7 @@ dependencies = [ [[package]] name = "reth-consensus-debug-client" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -6713,7 +6690,7 @@ dependencies = [ [[package]] name = "reth-db" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "arbitrary", @@ -6725,6 +6702,7 @@ dependencies = [ "iai-callgrind", "metrics", "page_size", + "parking_lot 0.12.3", "paste", "pprof", "proptest", @@ -6753,7 +6731,7 @@ dependencies = [ [[package]] name = "reth-db-api" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-genesis", "alloy-primitives", @@ -6780,7 +6758,7 @@ dependencies = [ [[package]] name = "reth-db-common" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-genesis", "alloy-primitives", @@ -6808,7 +6786,7 @@ dependencies = [ [[package]] name = "reth-db-models" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "arbitrary", @@ -6824,7 +6802,7 @@ dependencies = [ [[package]] name = "reth-discv4" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -6850,7 +6828,7 @@ dependencies = [ [[package]] name = "reth-discv5" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -6874,7 +6852,7 @@ dependencies = [ [[package]] name = "reth-dns-discovery" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-chains", "alloy-primitives", @@ -6902,7 +6880,7 @@ dependencies = [ [[package]] name = "reth-downloaders" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-primitives", @@ -6939,7 +6917,7 @@ dependencies = [ [[package]] name = "reth-e2e-test-utils" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -6977,7 +6955,7 @@ dependencies = [ [[package]] name = "reth-ecies" -version = "1.0.7" +version = "1.0.8" dependencies = [ "aes", "alloy-primitives", @@ -7007,7 +6985,7 @@ dependencies = [ [[package]] name = "reth-engine-local" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rpc-types-engine", @@ -7037,7 +7015,7 @@ dependencies = [ [[package]] name = "reth-engine-primitives" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "reth-execution-types", @@ -7049,7 +7027,7 @@ dependencies = [ [[package]] name = "reth-engine-service" -version = "1.0.7" +version = "1.0.8" dependencies = [ "futures", "pin-project", @@ -7077,7 +7055,7 @@ dependencies = [ [[package]] name = "reth-engine-tree" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-primitives", @@ -7124,7 +7102,7 @@ dependencies = [ [[package]] name = "reth-engine-util" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rpc-types-engine", @@ -7154,7 +7132,7 @@ dependencies = [ [[package]] name = "reth-errors" -version = "1.0.7" +version = "1.0.8" dependencies = [ "reth-blockchain-tree-api", "reth-consensus", @@ -7166,7 +7144,7 @@ dependencies = [ [[package]] name = "reth-eth-wire" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -7200,7 +7178,7 @@ dependencies = [ [[package]] name = "reth-eth-wire-types" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-chains", "alloy-consensus", @@ -7223,21 +7201,18 @@ dependencies = [ [[package]] name = "reth-ethereum-cli" -version = "1.0.7" +version = "1.0.8" dependencies = [ - "alloy-genesis", "clap", "eyre", "reth-chainspec", "reth-cli", "reth-cli-commands", - "serde_json", - "shellexpand", ] [[package]] name = "reth-ethereum-consensus" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "reth-chainspec", @@ -7249,7 +7224,7 @@ dependencies = [ [[package]] name = "reth-ethereum-engine-primitives" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-primitives", @@ -7268,7 +7243,7 @@ dependencies = [ [[package]] name = "reth-ethereum-forks" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-chains", "alloy-primitives", @@ -7287,7 +7262,7 @@ dependencies = [ [[package]] name = "reth-ethereum-payload-builder" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "reth-basic-payload-builder", @@ -7311,7 +7286,7 @@ dependencies = [ [[package]] name = "reth-etl" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "rayon", @@ -7321,7 +7296,7 @@ dependencies = [ [[package]] name = "reth-evm" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-primitives", @@ -7343,7 +7318,7 @@ dependencies = [ [[package]] name = "reth-evm-ethereum" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -7366,7 +7341,7 @@ dependencies = [ [[package]] name = "reth-execution-errors" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-primitives", @@ -7381,7 +7356,7 @@ dependencies = [ [[package]] name = "reth-execution-types" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-primitives", @@ -7398,7 +7373,7 @@ dependencies = [ [[package]] name = "reth-exex" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -7443,7 +7418,7 @@ dependencies = [ [[package]] name = "reth-exex-test-utils" -version = "1.0.7" +version = "1.0.8" dependencies = [ "eyre", "futures-util", @@ -7475,7 +7450,7 @@ dependencies = [ [[package]] name = "reth-exex-types" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-primitives", @@ -7491,7 +7466,7 @@ dependencies = [ [[package]] name = "reth-fs-util" -version = "1.0.7" +version = "1.0.8" dependencies = [ "serde", "serde_json", @@ -7500,7 +7475,7 @@ dependencies = [ [[package]] name = "reth-invalid-block-hooks" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -7524,7 +7499,7 @@ dependencies = [ [[package]] name = "reth-ipc" -version = "1.0.7" +version = "1.0.8" dependencies = [ "async-trait", "bytes", @@ -7546,14 +7521,14 @@ dependencies = [ [[package]] name = "reth-libmdbx" -version = "1.0.7" +version = "1.0.8" dependencies = [ "bitflags 2.6.0", "byteorder", "criterion", "dashmap 6.1.0", "derive_more 1.0.0", - "indexmap 2.5.0", + "indexmap 2.6.0", "parking_lot 0.12.3", "pprof", "rand 0.8.5", @@ -7567,15 +7542,15 @@ dependencies = [ [[package]] name = "reth-mdbx-sys" -version = "1.0.7" +version = "1.0.8" dependencies = [ - "bindgen 0.70.1", + "bindgen", "cc", ] [[package]] name = "reth-metrics" -version = "1.0.7" +version = "1.0.8" dependencies = [ "futures", "metrics", @@ -7586,14 +7561,14 @@ dependencies = [ [[package]] name = "reth-net-banlist" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", ] [[package]] name = "reth-net-nat" -version = "1.0.7" +version = "1.0.8" dependencies = [ "futures-util", "if-addrs", @@ -7607,7 +7582,7 @@ dependencies = [ [[package]] name = "reth-network" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -7667,7 +7642,7 @@ dependencies = [ [[package]] name = "reth-network-api" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rpc-types-admin", @@ -7689,7 +7664,7 @@ dependencies = [ [[package]] name = "reth-network-p2p" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-primitives", @@ -7709,7 +7684,7 @@ dependencies = [ [[package]] name = "reth-network-peers" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -7725,7 +7700,7 @@ dependencies = [ [[package]] name = "reth-network-types" -version = "1.0.7" +version = "1.0.8" dependencies = [ "humantime-serde", "reth-ethereum-forks", @@ -7738,7 +7713,7 @@ dependencies = [ [[package]] name = "reth-nippy-jar" -version = "1.0.7" +version = "1.0.8" dependencies = [ "anyhow", "bincode", @@ -7756,7 +7731,7 @@ dependencies = [ [[package]] name = "reth-node-api" -version = "1.0.7" +version = "1.0.8" dependencies = [ "reth-engine-primitives", "reth-evm", @@ -7773,7 +7748,7 @@ dependencies = [ [[package]] name = "reth-node-builder" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -7837,9 +7812,8 @@ dependencies = [ [[package]] name = "reth-node-core" -version = "1.0.7" +version = "1.0.8" dependencies = [ - "alloy-genesis", "alloy-primitives", "alloy-rpc-types-engine", "clap", @@ -7852,19 +7826,16 @@ dependencies = [ "proptest", "rand 0.8.5", "reth-chainspec", - "reth-cli", "reth-cli-util", "reth-config", "reth-consensus-common", "reth-db", "reth-discv4", "reth-discv5", - "reth-fs-util", "reth-net-nat", "reth-network", "reth-network-p2p", "reth-network-peers", - "reth-optimism-chainspec", "reth-primitives", "reth-prune-types", "reth-rpc-api", @@ -7879,7 +7850,6 @@ dependencies = [ "reth-transaction-pool", "secp256k1", "serde", - "serde_json", "shellexpand", "strum", "tempfile", @@ -7892,7 +7862,7 @@ dependencies = [ [[package]] name = "reth-node-ethereum" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-genesis", "alloy-primitives", @@ -7928,7 +7898,7 @@ dependencies = [ [[package]] name = "reth-node-events" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rpc-types-engine", @@ -7950,7 +7920,7 @@ dependencies = [ [[package]] name = "reth-node-metrics" -version = "1.0.7" +version = "1.0.8" dependencies = [ "eyre", "http", @@ -7976,7 +7946,7 @@ dependencies = [ [[package]] name = "reth-node-types" -version = "1.0.7" +version = "1.0.8" dependencies = [ "reth-chainspec", "reth-db-api", @@ -7985,7 +7955,7 @@ dependencies = [ [[package]] name = "reth-optimism-chainspec" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-chains", "alloy-genesis", @@ -8003,7 +7973,7 @@ dependencies = [ [[package]] name = "reth-optimism-cli" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -8026,6 +7996,7 @@ dependencies = [ "reth-node-builder", "reth-node-core", "reth-node-events", + "reth-node-metrics", "reth-optimism-chainspec", "reth-optimism-evm", "reth-optimism-node", @@ -8046,7 +8017,7 @@ dependencies = [ [[package]] name = "reth-optimism-consensus" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "reth-chainspec", @@ -8061,7 +8032,7 @@ dependencies = [ [[package]] name = "reth-optimism-evm" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -8087,7 +8058,7 @@ dependencies = [ [[package]] name = "reth-optimism-forks" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-chains", "alloy-primitives", @@ -8098,7 +8069,7 @@ dependencies = [ [[package]] name = "reth-optimism-node" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-genesis", @@ -8151,7 +8122,7 @@ dependencies = [ [[package]] name = "reth-optimism-payload-builder" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-primitives", @@ -8184,7 +8155,7 @@ dependencies = [ [[package]] name = "reth-optimism-primitives" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "reth-primitives", @@ -8193,7 +8164,7 @@ dependencies = [ [[package]] name = "reth-optimism-rpc" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-primitives", @@ -8232,7 +8203,7 @@ dependencies = [ [[package]] name = "reth-optimism-storage" -version = "1.0.7" +version = "1.0.8" dependencies = [ "reth-codecs", "reth-db-api", @@ -8243,7 +8214,7 @@ dependencies = [ [[package]] name = "reth-payload-builder" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -8264,7 +8235,7 @@ dependencies = [ [[package]] name = "reth-payload-primitives" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -8285,7 +8256,7 @@ dependencies = [ [[package]] name = "reth-payload-validator" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-rpc-types", "reth-chainspec", @@ -8295,7 +8266,7 @@ dependencies = [ [[package]] name = "reth-primitives" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -8340,7 +8311,7 @@ dependencies = [ [[package]] name = "reth-primitives-traits" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -8368,7 +8339,7 @@ dependencies = [ [[package]] name = "reth-provider" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -8418,7 +8389,7 @@ dependencies = [ [[package]] name = "reth-prune" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "assert_matches", @@ -8447,7 +8418,7 @@ dependencies = [ [[package]] name = "reth-prune-types" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "arbitrary", @@ -8467,7 +8438,7 @@ dependencies = [ [[package]] name = "reth-revm" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "reth-chainspec", @@ -8484,7 +8455,7 @@ dependencies = [ [[package]] name = "reth-rpc" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -8552,7 +8523,7 @@ dependencies = [ [[package]] name = "reth-rpc-api" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-json-rpc", @@ -8578,7 +8549,7 @@ dependencies = [ [[package]] name = "reth-rpc-api-testing-util" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -8597,7 +8568,7 @@ dependencies = [ [[package]] name = "reth-rpc-builder" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-network", "alloy-primitives", @@ -8649,7 +8620,7 @@ dependencies = [ [[package]] name = "reth-rpc-engine-api" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-primitives", @@ -8685,7 +8656,7 @@ dependencies = [ [[package]] name = "reth-rpc-eth-api" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-dyn-abi", "alloy-eips", @@ -8725,7 +8696,7 @@ dependencies = [ [[package]] name = "reth-rpc-eth-types" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -8768,7 +8739,7 @@ dependencies = [ [[package]] name = "reth-rpc-layer" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-rpc-types-engine", "http", @@ -8783,7 +8754,7 @@ dependencies = [ [[package]] name = "reth-rpc-server-types" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rpc-types-engine", @@ -8798,7 +8769,7 @@ dependencies = [ [[package]] name = "reth-rpc-types-compat" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-primitives", @@ -8814,7 +8785,7 @@ dependencies = [ [[package]] name = "reth-stages" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -8864,7 +8835,7 @@ dependencies = [ [[package]] name = "reth-stages-api" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "aquamarine", @@ -8892,7 +8863,7 @@ dependencies = [ [[package]] name = "reth-stages-types" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "arbitrary", @@ -8909,7 +8880,7 @@ dependencies = [ [[package]] name = "reth-static-file" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "assert_matches", @@ -8934,7 +8905,7 @@ dependencies = [ [[package]] name = "reth-static-file-types" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "clap", @@ -8945,7 +8916,7 @@ dependencies = [ [[package]] name = "reth-storage-api" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-primitives", @@ -8963,7 +8934,7 @@ dependencies = [ [[package]] name = "reth-storage-errors" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-primitives", @@ -8975,7 +8946,7 @@ dependencies = [ [[package]] name = "reth-tasks" -version = "1.0.7" +version = "1.0.8" dependencies = [ "auto_impl", "dyn-clone", @@ -8992,7 +8963,7 @@ dependencies = [ [[package]] name = "reth-testing-utils" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -9005,7 +8976,7 @@ dependencies = [ [[package]] name = "reth-tokio-util" -version = "1.0.7" +version = "1.0.8" dependencies = [ "tokio", "tokio-stream", @@ -9014,7 +8985,7 @@ dependencies = [ [[package]] name = "reth-tracing" -version = "1.0.7" +version = "1.0.8" dependencies = [ "clap", "eyre", @@ -9028,7 +8999,7 @@ dependencies = [ [[package]] name = "reth-transaction-pool" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -9073,7 +9044,7 @@ dependencies = [ [[package]] name = "reth-trie" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -9104,7 +9075,7 @@ dependencies = [ [[package]] name = "reth-trie-common" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-genesis", @@ -9128,7 +9099,7 @@ dependencies = [ [[package]] name = "reth-trie-db" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -9163,7 +9134,7 @@ dependencies = [ [[package]] name = "reth-trie-parallel" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -9478,9 +9449,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.13" +version = "0.23.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" +checksum = "415d9944693cb90382053259f89fbb077ea730ad7273047ec63b19bc9b160ba8" dependencies = [ "log", "once_cell", @@ -9779,7 +9750,7 @@ version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ - "indexmap 2.5.0", + "indexmap 2.6.0", "itoa", "memchr", "ryu", @@ -9831,15 +9802,15 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.10.0" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9720086b3357bcb44fce40117d769a4d068c70ecfa190850a980a71755f66fcc" +checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817" dependencies = [ "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.5.0", + "indexmap 2.6.0", "serde", "serde_derive", "serde_json", @@ -9849,9 +9820,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.10.0" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f1abbfe725f27678f4663bcacb75a83e829fd464c25d78dd038a3a29e307cec" +checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d" dependencies = [ "darling", "proc-macro2", @@ -10649,7 +10620,7 @@ version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap 2.5.0", + "indexmap 2.6.0", "serde", "serde_spanned", "toml_datetime", @@ -10998,9 +10969,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" [[package]] name = "unicode-ident" diff --git a/Cargo.toml b/Cargo.toml index 6a268b0684d4..8c1ee8c4514c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace.package] -version = "1.0.7" +version = "1.0.8" edition = "2021" rust-version = "1.81" license = "MIT OR Apache-2.0" diff --git a/bin/reth/Cargo.toml b/bin/reth/Cargo.toml index 3d3cbd06f4d7..476f9cd5cec7 100644 --- a/bin/reth/Cargo.toml +++ b/bin/reth/Cargo.toml @@ -15,6 +15,7 @@ workspace = true [dependencies] # reth reth-cli.workspace = true +reth-ethereum-cli.workspace = true reth-chainspec.workspace = true reth-config.workspace = true reth-primitives.workspace = true diff --git a/bin/reth/src/cli/mod.rs b/bin/reth/src/cli/mod.rs index 8bb0971ec081..cca801da36b2 100644 --- a/bin/reth/src/cli/mod.rs +++ b/bin/reth/src/cli/mod.rs @@ -15,8 +15,8 @@ use reth_cli_commands::{ }; use reth_cli_runner::CliRunner; use reth_db::DatabaseEnv; +use reth_ethereum_cli::chainspec::EthereumChainSpecParser; use reth_node_builder::{NodeBuilder, WithLaunchContext}; -use reth_node_core::args::utils::EthereumChainSpecParser; use reth_node_ethereum::{EthExecutorProvider, EthereumNode}; use reth_tracing::FileWorkerGuard; use std::{ffi::OsString, fmt, future::Future, sync::Arc}; @@ -117,7 +117,8 @@ impl, Ext: clap::Args + fmt::Debug> Cl /// /// ```no_run /// use clap::Parser; - /// use reth::{args::utils::EthereumChainSpecParser, cli::Cli}; + /// use reth::cli::Cli; + /// use reth_ethereum_cli::chainspec::EthereumChainSpecParser; /// /// #[derive(Debug, Parser)] /// pub struct MyArgs { @@ -238,7 +239,7 @@ mod tests { use super::*; use crate::args::ColorMode; use clap::CommandFactory; - use reth_node_core::args::utils::SUPPORTED_CHAINS; + use reth_ethereum_cli::chainspec::SUPPORTED_CHAINS; #[test] fn parse_color_mode() { diff --git a/bin/reth/src/lib.rs b/bin/reth/src/lib.rs index 4ff2faa1d65f..6b71f48de123 100644 --- a/bin/reth/src/lib.rs +++ b/bin/reth/src/lib.rs @@ -90,6 +90,7 @@ pub mod dirs { /// Re-exported from `reth_chainspec` pub mod chainspec { pub use reth_chainspec::*; + pub use reth_ethereum_cli::chainspec::*; } /// Re-exported from `reth_provider`. diff --git a/bin/reth/src/main.rs b/bin/reth/src/main.rs index 578f2987d73f..7153cdcc6c88 100644 --- a/bin/reth/src/main.rs +++ b/bin/reth/src/main.rs @@ -4,7 +4,8 @@ static ALLOC: reth_cli_util::allocator::Allocator = reth_cli_util::allocator::new_allocator(); use clap::{Args, Parser}; -use reth::{args::utils::EthereumChainSpecParser, cli::Cli}; +use reth::cli::Cli; +use reth_ethereum_cli::chainspec::EthereumChainSpecParser; use reth_node_builder::{ engine_tree_config::{ TreeConfig, DEFAULT_MEMORY_BLOCK_BUFFER_TARGET, DEFAULT_PERSISTENCE_THRESHOLD, @@ -60,7 +61,7 @@ fn main() { let handle = builder .with_types_and_provider::>() .with_components(EthereumNode::components()) - .with_add_ons::() + .with_add_ons(EthereumAddOns::default()) .launch_with_fn(|builder| { let launcher = EngineNodeLauncher::new( builder.task_executor().clone(), diff --git a/crates/blockchain-tree/src/bundle.rs b/crates/blockchain-tree/src/bundle.rs index 6f62d4136bb7..3745753d3f47 100644 --- a/crates/blockchain-tree/src/bundle.rs +++ b/crates/blockchain-tree/src/bundle.rs @@ -18,7 +18,7 @@ pub struct BundleStateDataRef<'a> { pub canonical_fork: ForkBlock, } -impl<'a> ExecutionDataProvider for BundleStateDataRef<'a> { +impl ExecutionDataProvider for BundleStateDataRef<'_> { fn execution_outcome(&self) -> &ExecutionOutcome { self.execution_outcome } @@ -33,7 +33,7 @@ impl<'a> ExecutionDataProvider for BundleStateDataRef<'a> { } } -impl<'a> BlockExecutionForkProvider for BundleStateDataRef<'a> { +impl BlockExecutionForkProvider for BundleStateDataRef<'_> { fn canonical_fork(&self) -> ForkBlock { self.canonical_fork } diff --git a/crates/blockchain-tree/src/canonical_chain.rs b/crates/blockchain-tree/src/canonical_chain.rs index 7dcd466f7d64..253f799fe0f8 100644 --- a/crates/blockchain-tree/src/canonical_chain.rs +++ b/crates/blockchain-tree/src/canonical_chain.rs @@ -32,15 +32,7 @@ impl CanonicalChain { /// Returns the block number of the (non-finalized) canonical block with the given hash. #[inline] pub(crate) fn canonical_number(&self, block_hash: &BlockHash) -> Option { - self.chain.iter().find_map( - |(number, hash)| { - if hash == block_hash { - Some(*number) - } else { - None - } - }, - ) + self.chain.iter().find_map(|(number, hash)| (hash == block_hash).then_some(*number)) } /// Extends all items from the given iterator to the chain. @@ -81,3 +73,169 @@ impl CanonicalChain { self.chain.into_iter() } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_replace_canonical_chain() { + // Initialize a chain with some blocks + let mut initial_chain = BTreeMap::new(); + initial_chain.insert(BlockNumber::from(1u64), BlockHash::from([0x01; 32])); + initial_chain.insert(BlockNumber::from(2u64), BlockHash::from([0x02; 32])); + + let mut canonical_chain = CanonicalChain::new(initial_chain.clone()); + + // Verify initial chain state + assert_eq!(canonical_chain.chain.len(), 2); + assert_eq!( + canonical_chain.chain.get(&BlockNumber::from(1u64)), + Some(&BlockHash::from([0x01; 32])) + ); + + // Replace with a new chain + let mut new_chain = BTreeMap::new(); + new_chain.insert(BlockNumber::from(3u64), BlockHash::from([0x03; 32])); + new_chain.insert(BlockNumber::from(4u64), BlockHash::from([0x04; 32])); + new_chain.insert(BlockNumber::from(5u64), BlockHash::from([0x05; 32])); + + canonical_chain.replace(new_chain.clone()); + + // Verify replaced chain state + assert_eq!(canonical_chain.chain.len(), 3); + assert!(!canonical_chain.chain.contains_key(&BlockNumber::from(1u64))); + assert_eq!( + canonical_chain.chain.get(&BlockNumber::from(3u64)), + Some(&BlockHash::from([0x03; 32])) + ); + } + + #[test] + fn test_canonical_hash_canonical_chain() { + // Initialize a chain with some blocks + let mut chain = BTreeMap::new(); + chain.insert(BlockNumber::from(1u64), BlockHash::from([0x01; 32])); + chain.insert(BlockNumber::from(2u64), BlockHash::from([0x02; 32])); + chain.insert(BlockNumber::from(3u64), BlockHash::from([0x03; 32])); + + // Create an instance of a canonical chain + let canonical_chain = CanonicalChain::new(chain.clone()); + + // Check that the function returns the correct hash for a given block number + let block_number = BlockNumber::from(2u64); + let expected_hash = BlockHash::from([0x02; 32]); + assert_eq!(canonical_chain.canonical_hash(&block_number), Some(expected_hash)); + + // Check that a non-existent block returns None + let non_existent_block = BlockNumber::from(5u64); + assert_eq!(canonical_chain.canonical_hash(&non_existent_block), None); + } + + #[test] + fn test_canonical_number_canonical_chain() { + // Initialize a chain with some blocks + let mut chain = BTreeMap::new(); + chain.insert(BlockNumber::from(1u64), BlockHash::from([0x01; 32])); + chain.insert(BlockNumber::from(2u64), BlockHash::from([0x02; 32])); + chain.insert(BlockNumber::from(3u64), BlockHash::from([0x03; 32])); + + // Create an instance of a canonical chain + let canonical_chain = CanonicalChain::new(chain.clone()); + + // Check that the function returns the correct block number for a given block hash + let block_hash = BlockHash::from([0x02; 32]); + let expected_number = BlockNumber::from(2u64); + assert_eq!(canonical_chain.canonical_number(&block_hash), Some(expected_number)); + + // Check that a non-existent block hash returns None + let non_existent_hash = BlockHash::from([0x05; 32]); + assert_eq!(canonical_chain.canonical_number(&non_existent_hash), None); + } + + #[test] + fn test_extend_canonical_chain() { + // Initialize an empty chain + let mut canonical_chain = CanonicalChain::new(BTreeMap::new()); + + // Create an iterator with some blocks + let blocks = vec![ + (BlockNumber::from(1u64), BlockHash::from([0x01; 32])), + (BlockNumber::from(2u64), BlockHash::from([0x02; 32])), + ] + .into_iter(); + + // Extend the chain with the created blocks + canonical_chain.extend(blocks); + + // Check if the blocks were added correctly + assert_eq!(canonical_chain.chain.len(), 2); + assert_eq!( + canonical_chain.chain.get(&BlockNumber::from(1u64)), + Some(&BlockHash::from([0x01; 32])) + ); + assert_eq!( + canonical_chain.chain.get(&BlockNumber::from(2u64)), + Some(&BlockHash::from([0x02; 32])) + ); + + // Test extending with additional blocks again + let more_blocks = vec![(BlockNumber::from(3u64), BlockHash::from([0x03; 32]))].into_iter(); + canonical_chain.extend(more_blocks); + + assert_eq!(canonical_chain.chain.len(), 3); + assert_eq!( + canonical_chain.chain.get(&BlockNumber::from(3u64)), + Some(&BlockHash::from([0x03; 32])) + ); + } + + #[test] + fn test_retain_canonical_chain() { + // Initialize a chain with some blocks + let mut chain = BTreeMap::new(); + chain.insert(BlockNumber::from(1u64), BlockHash::from([0x01; 32])); + chain.insert(BlockNumber::from(2u64), BlockHash::from([0x02; 32])); + chain.insert(BlockNumber::from(3u64), BlockHash::from([0x03; 32])); + + // Create an instance of CanonicalChain + let mut canonical_chain = CanonicalChain::new(chain); + + // Retain only blocks with even block numbers + canonical_chain.retain(|number, _| number % 2 == 0); + + // Check if the chain only contains the block with number 2 + assert_eq!(canonical_chain.chain.len(), 1); + assert_eq!( + canonical_chain.chain.get(&BlockNumber::from(2u64)), + Some(&BlockHash::from([0x02; 32])) + ); + + // Ensure that the blocks with odd numbers were removed + assert_eq!(canonical_chain.chain.get(&BlockNumber::from(1u64)), None); + assert_eq!(canonical_chain.chain.get(&BlockNumber::from(3u64)), None); + } + + #[test] + fn test_tip_canonical_chain() { + // Initialize a chain with some blocks + let mut chain = BTreeMap::new(); + chain.insert(BlockNumber::from(1u64), BlockHash::from([0x01; 32])); + chain.insert(BlockNumber::from(2u64), BlockHash::from([0x02; 32])); + chain.insert(BlockNumber::from(3u64), BlockHash::from([0x03; 32])); + + // Create an instance of a canonical chain + let canonical_chain = CanonicalChain::new(chain); + + // Call the tip method and verify the returned value + let tip = canonical_chain.tip(); + assert_eq!(tip.number, BlockNumber::from(3u64)); + assert_eq!(tip.hash, BlockHash::from([0x03; 32])); + + // Test with an empty chain + let empty_chain = CanonicalChain::new(BTreeMap::new()); + let empty_tip = empty_chain.tip(); + assert_eq!(empty_tip.number, BlockNumber::default()); + assert_eq!(empty_tip.hash, BlockHash::default()); + } +} diff --git a/crates/chain-state/src/in_memory.rs b/crates/chain-state/src/in_memory.rs index f2a73d27fa21..f47417aa385d 100644 --- a/crates/chain-state/src/in_memory.rs +++ b/crates/chain-state/src/in_memory.rs @@ -177,7 +177,7 @@ impl CanonicalInMemoryState { let in_memory_state = InMemoryState::new(blocks, numbers, pending); let header = in_memory_state .head_state() - .map_or_else(SealedHeader::default, |state| state.block().block().header.clone()); + .map_or_else(SealedHeader::default, |state| state.block_ref().block().header.clone()); let chain_info_tracker = ChainInfoTracker::new(header, finalized); let (canon_state_notification_sender, _) = broadcast::channel(CANON_STATE_NOTIFICATION_CHANNEL_SIZE); @@ -219,7 +219,7 @@ impl CanonicalInMemoryState { /// Returns the header corresponding to the given hash. pub fn header_by_hash(&self, hash: B256) -> Option { - self.state_by_hash(hash).map(|block| block.block().block.header.clone()) + self.state_by_hash(hash).map(|block| block.block_ref().block.header.clone()) } /// Clears all entries in the in memory state. @@ -323,7 +323,7 @@ impl CanonicalInMemoryState { // height) let mut old_blocks = blocks .drain() - .filter(|(_, b)| b.block().block().number > persisted_height) + .filter(|(_, b)| b.block_ref().block().number > persisted_height) .map(|(_, b)| b.block.clone()) .collect::>(); @@ -345,7 +345,7 @@ impl CanonicalInMemoryState { // also shift the pending state if it exists self.inner.in_memory_state.pending.send_modify(|p| { if let Some(p) = p.as_mut() { - p.parent = blocks.get(&p.block().block.parent_hash).cloned(); + p.parent = blocks.get(&p.block_ref().block.parent_hash).cloned(); } }); } @@ -452,7 +452,7 @@ impl CanonicalInMemoryState { /// Returns the `SealedHeader` corresponding to the pending state. pub fn pending_sealed_header(&self) -> Option { - self.pending_state().map(|h| h.block().block().header.clone()) + self.pending_state().map(|h| h.block_ref().block().header.clone()) } /// Returns the `Header` corresponding to the pending state. @@ -462,20 +462,20 @@ impl CanonicalInMemoryState { /// Returns the `SealedBlock` corresponding to the pending state. pub fn pending_block(&self) -> Option { - self.pending_state().map(|block_state| block_state.block().block().clone()) + self.pending_state().map(|block_state| block_state.block_ref().block().clone()) } /// Returns the `SealedBlockWithSenders` corresponding to the pending state. pub fn pending_block_with_senders(&self) -> Option { self.pending_state() - .and_then(|block_state| block_state.block().block().clone().seal_with_senders()) + .and_then(|block_state| block_state.block_ref().block().clone().seal_with_senders()) } /// Returns a tuple with the `SealedBlock` corresponding to the pending /// state and a vector of its `Receipt`s. pub fn pending_block_and_receipts(&self) -> Option<(SealedBlock, Vec)> { self.pending_state().map(|block_state| { - (block_state.block().block().clone(), block_state.executed_block_receipts()) + (block_state.block_ref().block().clone(), block_state.executed_block_receipts()) }) } @@ -543,7 +543,7 @@ impl CanonicalInMemoryState { pub fn transaction_by_hash(&self, hash: TxHash) -> Option { for block_state in self.canonical_chain() { if let Some(tx) = - block_state.block().block().body.transactions().find(|tx| tx.hash() == hash) + block_state.block_ref().block().body.transactions().find(|tx| tx.hash() == hash) { return Some(tx.clone()) } @@ -559,7 +559,7 @@ impl CanonicalInMemoryState { ) -> Option<(TransactionSigned, TransactionMeta)> { for block_state in self.canonical_chain() { if let Some((index, tx)) = block_state - .block() + .block_ref() .block() .body .transactions() @@ -570,10 +570,10 @@ impl CanonicalInMemoryState { tx_hash, index: index as u64, block_hash: block_state.hash(), - block_number: block_state.block().block.number, - base_fee: block_state.block().block().header.base_fee_per_gas, - timestamp: block_state.block().block.timestamp, - excess_blob_gas: block_state.block().block.excess_blob_gas, + block_number: block_state.block_ref().block.number, + base_fee: block_state.block_ref().block.header.base_fee_per_gas, + timestamp: block_state.block_ref().block.timestamp, + excess_blob_gas: block_state.block_ref().block.excess_blob_gas, }; return Some((tx.clone(), meta)) } @@ -1156,13 +1156,19 @@ mod tests { let block2 = test_block_builder.get_executed_block_with_number(0, B256::random()); let chain = NewCanonicalChain::Commit { new: vec![block1.clone()] }; state.update_chain(chain); - assert_eq!(state.head_state().unwrap().block().block().hash(), block1.block().hash()); - assert_eq!(state.state_by_number(0).unwrap().block().block().hash(), block1.block().hash()); + assert_eq!(state.head_state().unwrap().block_ref().block().hash(), block1.block().hash()); + assert_eq!( + state.state_by_number(0).unwrap().block_ref().block().hash(), + block1.block().hash() + ); let chain = NewCanonicalChain::Reorg { new: vec![block2.clone()], old: vec![block1] }; state.update_chain(chain); - assert_eq!(state.head_state().unwrap().block().block().hash(), block2.block().hash()); - assert_eq!(state.state_by_number(0).unwrap().block().block().hash(), block2.block().hash()); + assert_eq!(state.head_state().unwrap().block_ref().block().hash(), block2.block().hash()); + assert_eq!( + state.state_by_number(0).unwrap().block_ref().block().hash(), + block2.block().hash() + ); assert_eq!(state.inner.in_memory_state.block_count(), 1); } diff --git a/crates/chainspec/Cargo.toml b/crates/chainspec/Cargo.toml index de04ffd1e6aa..80636d139a1b 100644 --- a/crates/chainspec/Cargo.toml +++ b/crates/chainspec/Cargo.toml @@ -27,13 +27,9 @@ alloy-genesis.workspace = true alloy-primitives = { workspace = true, features = ["rand", "rlp"] } alloy-trie.workspace = true -# op -op-alloy-rpc-types = { workspace = true, optional = true } - # misc auto_impl.workspace = true once_cell.workspace = true -serde = { workspace = true, optional = true } serde_json.workspace = true derive_more.workspace = true @@ -44,12 +40,9 @@ alloy-eips = { workspace = true, features = ["arbitrary"] } alloy-rlp = { workspace = true, features = ["arrayvec"] } alloy-genesis.workspace = true -# op -op-alloy-rpc-types.workspace = true - [features] default = ["std"] -optimism = ["serde", "dep:op-alloy-rpc-types", "reth-optimism-forks"] +optimism = ["reth-optimism-forks"] std = [ "alloy-chains/std", "alloy-eips/std", diff --git a/crates/chainspec/src/spec.rs b/crates/chainspec/src/spec.rs index 45070db197d4..d6ca92aa24eb 100644 --- a/crates/chainspec/src/spec.rs +++ b/crates/chainspec/src/spec.rs @@ -590,14 +590,89 @@ impl ChainSpec { impl From for ChainSpec { fn from(genesis: Genesis) -> Self { - #[cfg(not(feature = "optimism"))] - { - into_ethereum_chain_spec(genesis) + // Block-based hardforks + let hardfork_opts = [ + (EthereumHardfork::Homestead.boxed(), genesis.config.homestead_block), + (EthereumHardfork::Dao.boxed(), genesis.config.dao_fork_block), + (EthereumHardfork::Tangerine.boxed(), genesis.config.eip150_block), + (EthereumHardfork::SpuriousDragon.boxed(), genesis.config.eip155_block), + (EthereumHardfork::Byzantium.boxed(), genesis.config.byzantium_block), + (EthereumHardfork::Constantinople.boxed(), genesis.config.constantinople_block), + (EthereumHardfork::Petersburg.boxed(), genesis.config.petersburg_block), + (EthereumHardfork::Istanbul.boxed(), genesis.config.istanbul_block), + (EthereumHardfork::MuirGlacier.boxed(), genesis.config.muir_glacier_block), + (EthereumHardfork::Berlin.boxed(), genesis.config.berlin_block), + (EthereumHardfork::London.boxed(), genesis.config.london_block), + (EthereumHardfork::ArrowGlacier.boxed(), genesis.config.arrow_glacier_block), + (EthereumHardfork::GrayGlacier.boxed(), genesis.config.gray_glacier_block), + ]; + let mut hardforks = hardfork_opts + .into_iter() + .filter_map(|(hardfork, opt)| opt.map(|block| (hardfork, ForkCondition::Block(block)))) + .collect::>(); + + // Paris + let paris_block_and_final_difficulty = + if let Some(ttd) = genesis.config.terminal_total_difficulty { + hardforks.push(( + EthereumHardfork::Paris.boxed(), + ForkCondition::TTD { + total_difficulty: ttd, + fork_block: genesis.config.merge_netsplit_block, + }, + )); + + genesis.config.merge_netsplit_block.map(|block| (block, ttd)) + } else { + None + }; + + // Time-based hardforks + let time_hardfork_opts = [ + (EthereumHardfork::Shanghai.boxed(), genesis.config.shanghai_time), + (EthereumHardfork::Cancun.boxed(), genesis.config.cancun_time), + (EthereumHardfork::Prague.boxed(), genesis.config.prague_time), + ]; + + let mut time_hardforks = time_hardfork_opts + .into_iter() + .filter_map(|(hardfork, opt)| { + opt.map(|time| (hardfork, ForkCondition::Timestamp(time))) + }) + .collect::>(); + + hardforks.append(&mut time_hardforks); + + // Ordered Hardforks + let mainnet_hardforks: ChainHardforks = EthereumHardfork::mainnet().into(); + let mainnet_order = mainnet_hardforks.forks_iter(); + + let mut ordered_hardforks = Vec::with_capacity(hardforks.len()); + for (hardfork, _) in mainnet_order { + if let Some(pos) = hardforks.iter().position(|(e, _)| **e == *hardfork) { + ordered_hardforks.push(hardforks.remove(pos)); + } } - #[cfg(feature = "optimism")] - { - into_optimism_chain_spec(genesis) + // append the remaining unknown hardforks to ensure we don't filter any out + ordered_hardforks.append(&mut hardforks); + + // NOTE: in full node, we prune all receipts except the deposit contract's. We do not + // have the deployment block in the genesis file, so we use block zero. We use the same + // deposit topic as the mainnet contract if we have the deposit contract address in the + // genesis json. + let deposit_contract = genesis.config.deposit_contract_address.map(|address| { + DepositContract { address, block: 0, topic: MAINNET_DEPOSIT_CONTRACT.topic } + }); + + Self { + chain: genesis.config.chain_id.into(), + genesis, + genesis_hash: OnceCell::new(), + hardforks: ChainHardforks::new(ordered_hardforks), + paris_block_and_final_difficulty, + deposit_contract, + ..Default::default() } } } @@ -637,194 +712,6 @@ impl EthereumHardforks for ChainSpec { #[cfg(feature = "optimism")] impl reth_optimism_forks::OptimismHardforks for ChainSpec {} -/// Convert the given [`Genesis`] into an Ethereum [`ChainSpec`]. -#[cfg(not(feature = "optimism"))] -fn into_ethereum_chain_spec(genesis: Genesis) -> ChainSpec { - // Block-based hardforks - let hardfork_opts = [ - (EthereumHardfork::Homestead.boxed(), genesis.config.homestead_block), - (EthereumHardfork::Dao.boxed(), genesis.config.dao_fork_block), - (EthereumHardfork::Tangerine.boxed(), genesis.config.eip150_block), - (EthereumHardfork::SpuriousDragon.boxed(), genesis.config.eip155_block), - (EthereumHardfork::Byzantium.boxed(), genesis.config.byzantium_block), - (EthereumHardfork::Constantinople.boxed(), genesis.config.constantinople_block), - (EthereumHardfork::Petersburg.boxed(), genesis.config.petersburg_block), - (EthereumHardfork::Istanbul.boxed(), genesis.config.istanbul_block), - (EthereumHardfork::MuirGlacier.boxed(), genesis.config.muir_glacier_block), - (EthereumHardfork::Berlin.boxed(), genesis.config.berlin_block), - (EthereumHardfork::London.boxed(), genesis.config.london_block), - (EthereumHardfork::ArrowGlacier.boxed(), genesis.config.arrow_glacier_block), - (EthereumHardfork::GrayGlacier.boxed(), genesis.config.gray_glacier_block), - ]; - let mut hardforks = hardfork_opts - .into_iter() - .filter_map(|(hardfork, opt)| opt.map(|block| (hardfork, ForkCondition::Block(block)))) - .collect::>(); - - // Paris - let paris_block_and_final_difficulty = - if let Some(ttd) = genesis.config.terminal_total_difficulty { - hardforks.push(( - EthereumHardfork::Paris.boxed(), - ForkCondition::TTD { - total_difficulty: ttd, - fork_block: genesis.config.merge_netsplit_block, - }, - )); - - genesis.config.merge_netsplit_block.map(|block| (block, ttd)) - } else { - None - }; - - // Time-based hardforks - let time_hardfork_opts = [ - (EthereumHardfork::Shanghai.boxed(), genesis.config.shanghai_time), - (EthereumHardfork::Cancun.boxed(), genesis.config.cancun_time), - (EthereumHardfork::Prague.boxed(), genesis.config.prague_time), - ]; - - let mut time_hardforks = time_hardfork_opts - .into_iter() - .filter_map(|(hardfork, opt)| opt.map(|time| (hardfork, ForkCondition::Timestamp(time)))) - .collect::>(); - - hardforks.append(&mut time_hardforks); - - // Ordered Hardforks - let mainnet_hardforks: ChainHardforks = EthereumHardfork::mainnet().into(); - let mainnet_order = mainnet_hardforks.forks_iter(); - - let mut ordered_hardforks = Vec::with_capacity(hardforks.len()); - for (hardfork, _) in mainnet_order { - if let Some(pos) = hardforks.iter().position(|(e, _)| **e == *hardfork) { - ordered_hardforks.push(hardforks.remove(pos)); - } - } - - // append the remaining unknown hardforks to ensure we don't filter any out - ordered_hardforks.append(&mut hardforks); - - // NOTE: in full node, we prune all receipts except the deposit contract's. We do not - // have the deployment block in the genesis file, so we use block zero. We use the same - // deposit topic as the mainnet contract if we have the deposit contract address in the - // genesis json. - let deposit_contract = genesis.config.deposit_contract_address.map(|address| DepositContract { - address, - block: 0, - topic: MAINNET_DEPOSIT_CONTRACT.topic, - }); - - ChainSpec { - chain: genesis.config.chain_id.into(), - genesis, - genesis_hash: OnceCell::new(), - hardforks: ChainHardforks::new(ordered_hardforks), - paris_block_and_final_difficulty, - deposit_contract, - ..Default::default() - } -} - -#[cfg(feature = "optimism")] -/// Convert the given [`Genesis`] into an Optimism [`ChainSpec`]. -fn into_optimism_chain_spec(genesis: Genesis) -> ChainSpec { - use reth_optimism_forks::OptimismHardfork; - let optimism_genesis_info = OptimismGenesisInfo::extract_from(&genesis); - let genesis_info = optimism_genesis_info.optimism_chain_info.genesis_info.unwrap_or_default(); - - // Block-based hardforks - let hardfork_opts = [ - (EthereumHardfork::Homestead.boxed(), genesis.config.homestead_block), - (EthereumHardfork::Tangerine.boxed(), genesis.config.eip150_block), - (EthereumHardfork::SpuriousDragon.boxed(), genesis.config.eip155_block), - (EthereumHardfork::Byzantium.boxed(), genesis.config.byzantium_block), - (EthereumHardfork::Constantinople.boxed(), genesis.config.constantinople_block), - (EthereumHardfork::Petersburg.boxed(), genesis.config.petersburg_block), - (EthereumHardfork::Istanbul.boxed(), genesis.config.istanbul_block), - (EthereumHardfork::MuirGlacier.boxed(), genesis.config.muir_glacier_block), - (EthereumHardfork::Berlin.boxed(), genesis.config.berlin_block), - (EthereumHardfork::London.boxed(), genesis.config.london_block), - (EthereumHardfork::ArrowGlacier.boxed(), genesis.config.arrow_glacier_block), - (EthereumHardfork::GrayGlacier.boxed(), genesis.config.gray_glacier_block), - (OptimismHardfork::Bedrock.boxed(), genesis_info.bedrock_block), - ]; - let mut block_hardforks = hardfork_opts - .into_iter() - .filter_map(|(hardfork, opt)| opt.map(|block| (hardfork, ForkCondition::Block(block)))) - .collect::>(); - - // Paris - let paris_block_and_final_difficulty = - if let Some(ttd) = genesis.config.terminal_total_difficulty { - block_hardforks.push(( - EthereumHardfork::Paris.boxed(), - ForkCondition::TTD { - total_difficulty: ttd, - fork_block: genesis.config.merge_netsplit_block, - }, - )); - - genesis.config.merge_netsplit_block.map(|block| (block, ttd)) - } else { - None - }; - - // Time-based hardforks - let time_hardfork_opts = [ - (EthereumHardfork::Shanghai.boxed(), genesis.config.shanghai_time), - (EthereumHardfork::Cancun.boxed(), genesis.config.cancun_time), - (EthereumHardfork::Prague.boxed(), genesis.config.prague_time), - (OptimismHardfork::Regolith.boxed(), genesis_info.regolith_time), - (OptimismHardfork::Canyon.boxed(), genesis_info.canyon_time), - (OptimismHardfork::Ecotone.boxed(), genesis_info.ecotone_time), - (OptimismHardfork::Fjord.boxed(), genesis_info.fjord_time), - (OptimismHardfork::Granite.boxed(), genesis_info.granite_time), - ]; - - let mut time_hardforks = time_hardfork_opts - .into_iter() - .filter_map(|(hardfork, opt)| opt.map(|time| (hardfork, ForkCondition::Timestamp(time)))) - .collect::>(); - - block_hardforks.append(&mut time_hardforks); - - // Ordered Hardforks - let mainnet_hardforks = OptimismHardfork::op_mainnet(); - let mainnet_order = mainnet_hardforks.forks_iter(); - - let mut ordered_hardforks = Vec::with_capacity(block_hardforks.len()); - for (hardfork, _) in mainnet_order { - if let Some(pos) = block_hardforks.iter().position(|(e, _)| **e == *hardfork) { - ordered_hardforks.push(block_hardforks.remove(pos)); - } - } - - // append the remaining unknown hardforks to ensure we don't filter any out - ordered_hardforks.append(&mut block_hardforks); - - // NOTE: in full node, we prune all receipts except the deposit contract's. We do not - // have the deployment block in the genesis file, so we use block zero. We use the same - // deposit topic as the mainnet contract if we have the deposit contract address in the - // genesis json. - let deposit_contract = genesis.config.deposit_contract_address.map(|address| DepositContract { - address, - block: 0, - topic: MAINNET_DEPOSIT_CONTRACT.topic, - }); - - ChainSpec { - chain: genesis.config.chain_id.into(), - genesis, - genesis_hash: OnceCell::new(), - hardforks: ChainHardforks::new(ordered_hardforks), - paris_block_and_final_difficulty, - deposit_contract, - base_fee_params: optimism_genesis_info.base_fee_params, - ..Default::default() - } -} - /// A trait for reading the current chainspec. #[auto_impl::auto_impl(&, Arc)] pub trait ChainSpecProvider: Send + Sync { @@ -1102,59 +989,6 @@ impl DepositContract { } } -/// Genesis info for Optimism. -#[cfg(feature = "optimism")] -#[derive(Default, Debug, serde::Deserialize)] -#[serde(rename_all = "camelCase")] -struct OptimismGenesisInfo { - optimism_chain_info: op_alloy_rpc_types::genesis::OptimismChainInfo, - #[serde(skip)] - base_fee_params: BaseFeeParamsKind, -} - -#[cfg(feature = "optimism")] -impl OptimismGenesisInfo { - fn extract_from(genesis: &Genesis) -> Self { - let mut info = Self { - optimism_chain_info: op_alloy_rpc_types::genesis::OptimismChainInfo::extract_from( - &genesis.config.extra_fields, - ) - .unwrap_or_default(), - ..Default::default() - }; - if let Some(optimism_base_fee_info) = &info.optimism_chain_info.base_fee_info { - if let (Some(elasticity), Some(denominator)) = ( - optimism_base_fee_info.eip1559_elasticity, - optimism_base_fee_info.eip1559_denominator, - ) { - let base_fee_params = if let Some(canyon_denominator) = - optimism_base_fee_info.eip1559_denominator_canyon - { - BaseFeeParamsKind::Variable( - vec![ - ( - EthereumHardfork::London.boxed(), - BaseFeeParams::new(denominator as u128, elasticity as u128), - ), - ( - reth_optimism_forks::OptimismHardfork::Canyon.boxed(), - BaseFeeParams::new(canyon_denominator as u128, elasticity as u128), - ), - ] - .into(), - ) - } else { - BaseFeeParams::new(denominator as u128, elasticity as u128).into() - }; - - info.base_fee_params = base_fee_params; - } - } - - info - } -} - /// Verifies [`ChainSpec`] configuration against expected data in given cases. #[cfg(any(test, feature = "test-utils"))] pub fn test_fork_ids(spec: &ChainSpec, cases: &[(Head, ForkId)]) { @@ -2477,7 +2311,6 @@ Post-merge hard forks (timestamp based): } #[test] - #[cfg(not(feature = "optimism"))] fn test_fork_order_ethereum_mainnet() { let genesis = Genesis { config: ChainConfig { @@ -2506,7 +2339,7 @@ Post-merge hard forks (timestamp based): ..Default::default() }; - let chain_spec = into_ethereum_chain_spec(genesis); + let chain_spec: ChainSpec = genesis.into(); let hardforks: Vec<_> = chain_spec.hardforks.forks_iter().map(|(h, _)| h).collect(); let expected_hardforks = vec![ @@ -2534,80 +2367,4 @@ Post-merge hard forks (timestamp based): .all(|(expected, actual)| &**expected == *actual)); assert_eq!(expected_hardforks.len(), hardforks.len()); } - - #[test] - #[cfg(feature = "optimism")] - fn test_fork_order_optimism_mainnet() { - use reth_optimism_forks::OptimismHardfork; - - let genesis = Genesis { - config: ChainConfig { - chain_id: 0, - homestead_block: Some(0), - dao_fork_block: Some(0), - dao_fork_support: false, - eip150_block: Some(0), - eip155_block: Some(0), - eip158_block: Some(0), - byzantium_block: Some(0), - constantinople_block: Some(0), - petersburg_block: Some(0), - istanbul_block: Some(0), - muir_glacier_block: Some(0), - berlin_block: Some(0), - london_block: Some(0), - arrow_glacier_block: Some(0), - gray_glacier_block: Some(0), - merge_netsplit_block: Some(0), - shanghai_time: Some(0), - cancun_time: Some(0), - terminal_total_difficulty: Some(U256::ZERO), - extra_fields: [ - (String::from("bedrockBlock"), 0.into()), - (String::from("regolithTime"), 0.into()), - (String::from("canyonTime"), 0.into()), - (String::from("ecotoneTime"), 0.into()), - (String::from("fjordTime"), 0.into()), - (String::from("graniteTime"), 0.into()), - ] - .into_iter() - .collect(), - ..Default::default() - }, - ..Default::default() - }; - - let chain_spec: ChainSpec = into_optimism_chain_spec(genesis); - - let hardforks: Vec<_> = chain_spec.hardforks.forks_iter().map(|(h, _)| h).collect(); - let expected_hardforks = vec![ - EthereumHardfork::Homestead.boxed(), - EthereumHardfork::Tangerine.boxed(), - EthereumHardfork::SpuriousDragon.boxed(), - EthereumHardfork::Byzantium.boxed(), - EthereumHardfork::Constantinople.boxed(), - EthereumHardfork::Petersburg.boxed(), - EthereumHardfork::Istanbul.boxed(), - EthereumHardfork::MuirGlacier.boxed(), - EthereumHardfork::Berlin.boxed(), - EthereumHardfork::London.boxed(), - EthereumHardfork::ArrowGlacier.boxed(), - EthereumHardfork::GrayGlacier.boxed(), - EthereumHardfork::Paris.boxed(), - OptimismHardfork::Bedrock.boxed(), - OptimismHardfork::Regolith.boxed(), - EthereumHardfork::Shanghai.boxed(), - OptimismHardfork::Canyon.boxed(), - EthereumHardfork::Cancun.boxed(), - OptimismHardfork::Ecotone.boxed(), - OptimismHardfork::Fjord.boxed(), - OptimismHardfork::Granite.boxed(), - ]; - - assert!(expected_hardforks - .iter() - .zip(hardforks.iter()) - .all(|(expected, actual)| &**expected == *actual)); - assert_eq!(expected_hardforks.len(), hardforks.len()); - } } diff --git a/crates/cli/cli/Cargo.toml b/crates/cli/cli/Cargo.toml index e8f7a1dcbe15..7eb1f43b1e58 100644 --- a/crates/cli/cli/Cargo.toml +++ b/crates/cli/cli/Cargo.toml @@ -15,9 +15,13 @@ workspace = true # reth reth-cli-runner.workspace = true +alloy-genesis.workspace = true + # misc clap.workspace = true +shellexpand.workspace = true eyre.workspace = true +serde_json.workspace = true diff --git a/crates/cli/cli/src/chainspec.rs b/crates/cli/cli/src/chainspec.rs index 63705bd28f4a..8432009409f7 100644 --- a/crates/cli/cli/src/chainspec.rs +++ b/crates/cli/cli/src/chainspec.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{fs, path::PathBuf, sync::Arc}; use clap::builder::TypedValueParser; @@ -61,3 +61,21 @@ pub trait ChainSpecParser: Clone + Send + Sync + 'static { format!("The chain this node is running.\nPossible values are either a built-in chain or the path to a chain specification file.\n\nBuilt-in chains:\n {}", Self::SUPPORTED_CHAINS.join(", ")) } } + +/// A helper to parse a [`Genesis`](alloy_genesis::Genesis) as argument or from disk. +pub fn parse_genesis(s: &str) -> eyre::Result { + // try to read json from path first + let raw = match fs::read_to_string(PathBuf::from(shellexpand::full(s)?.into_owned())) { + Ok(raw) => raw, + Err(io_err) => { + // valid json may start with "\n", but must contain "{" + if s.contains('{') { + s.to_string() + } else { + return Err(io_err.into()) // assume invalid path + } + } + }; + + Ok(serde_json::from_str(&raw)?) +} diff --git a/crates/cli/commands/Cargo.toml b/crates/cli/commands/Cargo.toml index 4835c3d0fa29..e307859dfd86 100644 --- a/crates/cli/commands/Cargo.toml +++ b/crates/cli/commands/Cargo.toml @@ -14,6 +14,7 @@ repository.workspace = true reth-beacon-consensus.workspace = true reth-chainspec.workspace = true reth-cli.workspace = true +reth-ethereum-cli.workspace = true reth-cli-runner.workspace = true reth-cli-util.workspace = true reth-config.workspace = true diff --git a/crates/cli/commands/src/db/mod.rs b/crates/cli/commands/src/db/mod.rs index 1c000f56bc25..e1a9a90bacc3 100644 --- a/crates/cli/commands/src/db/mod.rs +++ b/crates/cli/commands/src/db/mod.rs @@ -160,7 +160,7 @@ impl> Command #[cfg(test)] mod tests { use super::*; - use reth_node_core::args::utils::{EthereumChainSpecParser, SUPPORTED_CHAINS}; + use reth_ethereum_cli::chainspec::{EthereumChainSpecParser, SUPPORTED_CHAINS}; use std::path::Path; #[test] diff --git a/crates/cli/commands/src/dump_genesis.rs b/crates/cli/commands/src/dump_genesis.rs index a5c0675cc7e8..c3e7bb217b57 100644 --- a/crates/cli/commands/src/dump_genesis.rs +++ b/crates/cli/commands/src/dump_genesis.rs @@ -32,7 +32,7 @@ impl> DumpGenesisCommand { #[cfg(test)] mod tests { use super::*; - use reth_node_core::args::utils::{EthereumChainSpecParser, SUPPORTED_CHAINS}; + use reth_ethereum_cli::chainspec::{EthereumChainSpecParser, SUPPORTED_CHAINS}; #[test] fn parse_dump_genesis_command_chain_args() { diff --git a/crates/cli/commands/src/import.rs b/crates/cli/commands/src/import.rs index 31c6cdc69157..6b750d32a3df 100644 --- a/crates/cli/commands/src/import.rs +++ b/crates/cli/commands/src/import.rs @@ -231,7 +231,7 @@ where #[cfg(test)] mod tests { use super::*; - use reth_node_core::args::utils::{EthereumChainSpecParser, SUPPORTED_CHAINS}; + use reth_ethereum_cli::chainspec::{EthereumChainSpecParser, SUPPORTED_CHAINS}; #[test] fn parse_common_import_command_chain_args() { diff --git a/crates/cli/commands/src/node.rs b/crates/cli/commands/src/node.rs index fe49b769a3d3..5b1a87e068b3 100644 --- a/crates/cli/commands/src/node.rs +++ b/crates/cli/commands/src/node.rs @@ -6,11 +6,12 @@ use reth_cli::chainspec::ChainSpecParser; use reth_cli_runner::CliContext; use reth_cli_util::parse_socket_address; use reth_db::{init_db, DatabaseEnv}; +use reth_ethereum_cli::chainspec::EthereumChainSpecParser; use reth_node_builder::{NodeBuilder, WithLaunchContext}; use reth_node_core::{ args::{ - utils::EthereumChainSpecParser, DatabaseArgs, DatadirArgs, DebugArgs, DevArgs, NetworkArgs, - PayloadBuilderArgs, PruningArgs, RpcServerArgs, TxPoolArgs, + DatabaseArgs, DatadirArgs, DebugArgs, DevArgs, NetworkArgs, PayloadBuilderArgs, + PruningArgs, RpcServerArgs, TxPoolArgs, }, node_config::NodeConfig, version, @@ -210,7 +211,7 @@ pub struct NoArgs; mod tests { use super::*; use reth_discv4::DEFAULT_DISCOVERY_PORT; - use reth_node_core::args::utils::SUPPORTED_CHAINS; + use reth_ethereum_cli::chainspec::SUPPORTED_CHAINS; use std::{ net::{IpAddr, Ipv4Addr}, path::Path, diff --git a/crates/cli/commands/src/stage/unwind.rs b/crates/cli/commands/src/stage/unwind.rs index ae3ae2500874..19305554eaa3 100644 --- a/crates/cli/commands/src/stage/unwind.rs +++ b/crates/cli/commands/src/stage/unwind.rs @@ -213,7 +213,7 @@ impl Subcommands { #[cfg(test)] mod tests { - use reth_node_core::args::utils::EthereumChainSpecParser; + use reth_ethereum_cli::chainspec::EthereumChainSpecParser; use super::*; diff --git a/crates/engine/invalid-block-hooks/src/witness.rs b/crates/engine/invalid-block-hooks/src/witness.rs index 37d5bb08293d..51978311faad 100644 --- a/crates/engine/invalid-block-hooks/src/witness.rs +++ b/crates/engine/invalid-block-hooks/src/witness.rs @@ -170,12 +170,8 @@ where if let Some(healthy_node_client) = &self.healthy_node_client { // Compare the witness against the healthy node. let healthy_node_witness = futures::executor::block_on(async move { - DebugApiClient::debug_execution_witness( - healthy_node_client, - block.number.into(), - true, - ) - .await + DebugApiClient::debug_execution_witness(healthy_node_client, block.number.into()) + .await })?; let healthy_path = self.save_file( diff --git a/crates/engine/tree/src/tree/metrics.rs b/crates/engine/tree/src/tree/metrics.rs index 52dbf34173df..d46c2f05a028 100644 --- a/crates/engine/tree/src/tree/metrics.rs +++ b/crates/engine/tree/src/tree/metrics.rs @@ -34,6 +34,14 @@ pub(crate) struct EngineMetrics { pub(crate) new_payload_messages: Counter, /// Histogram of persistence operation durations (in seconds) pub(crate) persistence_duration: Histogram, + /// Tracks the how often we failed to deliver a newPayload response. + /// + /// This effectively tracks how often the message sender dropped the channel and indicates a CL + /// request timeout (e.g. it took more than 8s to send the response and the CL terminated the + /// request which resulted in a closed channel). + pub(crate) failed_new_payload_response_deliveries: Counter, + /// Tracks the how often we failed to deliver a forkchoice update response. + pub(crate) failed_forkchoice_updated_response_deliveries: Counter, // TODO add latency metrics } diff --git a/crates/engine/tree/src/tree/mod.rs b/crates/engine/tree/src/tree/mod.rs index e01b75288220..47be69a3dee4 100644 --- a/crates/engine/tree/src/tree/mod.rs +++ b/crates/engine/tree/src/tree/mod.rs @@ -1219,6 +1219,10 @@ where if let Err(err) = tx.send(output.map(|o| o.outcome).map_err(Into::into)) { + self.metrics + .engine + .failed_forkchoice_updated_response_deliveries + .increment(1); error!(target: "engine::tree", "Failed to send event: {err:?}"); } } @@ -1230,6 +1234,10 @@ where ) })) { error!(target: "engine::tree", "Failed to send event: {err:?}"); + self.metrics + .engine + .failed_new_payload_response_deliveries + .increment(1); } } BeaconEngineMessage::TransitionConfigurationExchanged => { diff --git a/crates/ethereum/cli/Cargo.toml b/crates/ethereum/cli/Cargo.toml index b5fadc684b84..108f42f539b9 100644 --- a/crates/ethereum/cli/Cargo.toml +++ b/crates/ethereum/cli/Cargo.toml @@ -15,13 +15,6 @@ workspace = true reth-cli.workspace = true reth-chainspec.workspace = true -# ethereum -alloy-genesis.workspace = true - -# io -shellexpand.workspace = true -serde_json.workspace = true - # misc eyre.workspace = true diff --git a/crates/ethereum/cli/src/chainspec.rs b/crates/ethereum/cli/src/chainspec.rs index 05db177df832..cbcce9f69f61 100644 --- a/crates/ethereum/cli/src/chainspec.rs +++ b/crates/ethereum/cli/src/chainspec.rs @@ -1,48 +1,33 @@ -use alloy_genesis::Genesis; use reth_chainspec::{ChainSpec, DEV, HOLESKY, MAINNET, SEPOLIA}; -use reth_cli::chainspec::ChainSpecParser; -use std::{fs, path::PathBuf, sync::Arc}; +use reth_cli::chainspec::{parse_genesis, ChainSpecParser}; +use std::sync::Arc; + +/// Chains supported by reth. First value should be used as the default. +pub const SUPPORTED_CHAINS: &[&str] = &["mainnet", "sepolia", "holesky", "dev"]; /// Clap value parser for [`ChainSpec`]s. /// /// The value parser matches either a known chain, the path /// to a json file, or a json formatted string in-memory. The json needs to be a Genesis struct. -fn chain_value_parser(s: &str) -> eyre::Result, eyre::Error> { +pub fn chain_value_parser(s: &str) -> eyre::Result, eyre::Error> { Ok(match s { "mainnet" => MAINNET.clone(), "sepolia" => SEPOLIA.clone(), "holesky" => HOLESKY.clone(), "dev" => DEV.clone(), - _ => { - // try to read json from path first - let raw = match fs::read_to_string(PathBuf::from(shellexpand::full(s)?.into_owned())) { - Ok(raw) => raw, - Err(io_err) => { - // valid json may start with "\n", but must contain "{" - if s.contains('{') { - s.to_string() - } else { - return Err(io_err.into()) // assume invalid path - } - } - }; - - // both serialized Genesis and ChainSpec structs supported - let genesis: Genesis = serde_json::from_str(&raw)?; - - Arc::new(genesis.into()) - } + _ => Arc::new(parse_genesis(s)?.into()), }) } /// Ethereum chain specification parser. #[derive(Debug, Clone, Default)] -pub struct EthChainSpecParser; +#[non_exhaustive] +pub struct EthereumChainSpecParser; -impl ChainSpecParser for EthChainSpecParser { +impl ChainSpecParser for EthereumChainSpecParser { type ChainSpec = ChainSpec; - const SUPPORTED_CHAINS: &'static [&'static str] = &["mainnet", "sepolia", "holesky", "dev"]; + const SUPPORTED_CHAINS: &'static [&'static str] = SUPPORTED_CHAINS; fn parse(s: &str) -> eyre::Result> { chain_value_parser(s) @@ -56,8 +41,8 @@ mod tests { #[test] fn parse_known_chain_spec() { - for &chain in EthChainSpecParser::SUPPORTED_CHAINS { - assert!(::parse(chain).is_ok()); + for &chain in EthereumChainSpecParser::SUPPORTED_CHAINS { + assert!(::parse(chain).is_ok()); } } @@ -108,7 +93,7 @@ mod tests { } }"#; - let spec = ::parse(s).unwrap(); + let spec = ::parse(s).unwrap(); assert!(spec.is_shanghai_active_at_timestamp(0)); assert!(spec.is_cancun_active_at_timestamp(0)); assert!(spec.is_prague_active_at_timestamp(0)); diff --git a/crates/ethereum/engine-primitives/src/lib.rs b/crates/ethereum/engine-primitives/src/lib.rs index 69d73a021747..034a8c6bffbb 100644 --- a/crates/ethereum/engine-primitives/src/lib.rs +++ b/crates/ethereum/engine-primitives/src/lib.rs @@ -26,21 +26,40 @@ use reth_payload_primitives::{ /// The types used in the default mainnet ethereum beacon consensus engine. #[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)] #[non_exhaustive] -pub struct EthEngineTypes; +pub struct EthEngineTypes { + _marker: std::marker::PhantomData, +} -impl PayloadTypes for EthEngineTypes { - type BuiltPayload = EthBuiltPayload; - type PayloadAttributes = EthPayloadAttributes; - type PayloadBuilderAttributes = EthPayloadBuilderAttributes; +impl PayloadTypes for EthEngineTypes { + type BuiltPayload = T::BuiltPayload; + type PayloadAttributes = T::PayloadAttributes; + type PayloadBuilderAttributes = T::PayloadBuilderAttributes; } -impl EngineTypes for EthEngineTypes { +impl EngineTypes for EthEngineTypes +where + T::BuiltPayload: TryInto + + TryInto + + TryInto + + TryInto, +{ type ExecutionPayloadV1 = ExecutionPayloadV1; type ExecutionPayloadV2 = ExecutionPayloadEnvelopeV2; type ExecutionPayloadV3 = ExecutionPayloadEnvelopeV3; type ExecutionPayloadV4 = ExecutionPayloadEnvelopeV4; } +/// A default payload type for [`EthEngineTypes`] +#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)] +#[non_exhaustive] +pub struct EthPayloadTypes; + +impl PayloadTypes for EthPayloadTypes { + type BuiltPayload = EthBuiltPayload; + type PayloadAttributes = EthPayloadAttributes; + type PayloadBuilderAttributes = EthPayloadBuilderAttributes; +} + /// Validator for the ethereum engine API. #[derive(Debug, Clone)] pub struct EthereumEngineValidator { diff --git a/crates/ethereum/engine-primitives/src/payload.rs b/crates/ethereum/engine-primitives/src/payload.rs index dd0b7b405e9f..ae370fdb9d7b 100644 --- a/crates/ethereum/engine-primitives/src/payload.rs +++ b/crates/ethereum/engine-primitives/src/payload.rs @@ -89,7 +89,7 @@ impl BuiltPayload for EthBuiltPayload { } } -impl<'a> BuiltPayload for &'a EthBuiltPayload { +impl BuiltPayload for &EthBuiltPayload { fn block(&self) -> &SealedBlock { (**self).block() } diff --git a/crates/ethereum/evm/src/execute.rs b/crates/ethereum/evm/src/execute.rs index f1d7de115d57..8c84fafc25fb 100644 --- a/crates/ethereum/evm/src/execute.rs +++ b/crates/ethereum/evm/src/execute.rs @@ -14,7 +14,7 @@ use reth_evm::{ BatchExecutor, BlockExecutionError, BlockExecutionInput, BlockExecutionOutput, BlockExecutorProvider, BlockValidationError, Executor, ProviderError, }, - system_calls::SystemCaller, + system_calls::{NoopHook, OnStateHook, SystemCaller}, ConfigureEvm, }; use reth_execution_types::ExecutionOutcome; @@ -126,20 +126,25 @@ where /// This applies the pre-execution and post-execution changes that require an [EVM](Evm), and /// executes the transactions. /// + /// The optional `state_hook` will be executed with the state changes if present. + /// /// # Note /// /// It does __not__ apply post-execution changes that do not require an [EVM](Evm), for that see /// [`EthBlockExecutor::post_execution`]. - fn execute_state_transitions( + fn execute_state_transitions( &self, block: &BlockWithSenders, mut evm: Evm<'_, Ext, &mut State>, + state_hook: Option, ) -> Result where DB: Database, DB::Error: Into + Display, + F: OnStateHook, { - let mut system_caller = SystemCaller::new(&self.evm_config, &self.chain_spec); + let mut system_caller = + SystemCaller::new(&self.evm_config, &self.chain_spec).with_state_hook(state_hook); system_caller.apply_pre_execution_changes(block, &mut evm)?; @@ -161,7 +166,7 @@ where self.evm_config.fill_tx_env(evm.tx_mut(), transaction, *sender); // Execute transaction. - let ResultAndState { result, state } = evm.transact().map_err(move |err| { + let result_and_state = evm.transact().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 { @@ -169,6 +174,8 @@ where error: Box::new(new_err), } })?; + system_caller.on_state(&result_and_state); + let ResultAndState { result, state } = result_and_state; evm.db_mut().commit(state); // append gas used @@ -260,17 +267,31 @@ where EnvWithHandlerCfg::new_with_cfg_env(cfg, block_env, Default::default()) } + /// Convenience method to invoke `execute_without_verification_with_state_hook` setting the + /// state hook as `None`. + fn execute_without_verification( + &mut self, + block: &BlockWithSenders, + total_difficulty: U256, + ) -> Result { + self.execute_without_verification_with_state_hook(block, total_difficulty, None::) + } + /// Execute a single block and apply the state changes to the internal state. /// /// Returns the receipts of the transactions in the block, the total gas used and the list of /// EIP-7685 [requests](Request). /// /// Returns an error if execution fails. - fn execute_without_verification( + fn execute_without_verification_with_state_hook( &mut self, block: &BlockWithSenders, total_difficulty: U256, - ) -> Result { + state_hook: Option, + ) -> Result + where + F: OnStateHook, + { // 1. prepare state on new block self.on_new_block(&block.header); @@ -278,7 +299,7 @@ where let env = self.evm_env_for_block(&block.header, total_difficulty); let output = { let evm = self.executor.evm_config.evm_with_env(&mut self.state, env); - self.executor.execute_state_transitions(block, evm) + self.executor.execute_state_transitions(block, evm, state_hook) }?; // 3. apply post execution changes @@ -368,6 +389,27 @@ where witness(&self.state); Ok(BlockExecutionOutput { state: self.state.take_bundle(), receipts, requests, gas_used }) } + + fn execute_with_state_hook( + mut self, + input: Self::Input<'_>, + state_hook: F, + ) -> Result + where + F: OnStateHook, + { + let BlockExecutionInput { block, total_difficulty } = input; + let EthExecuteOutput { receipts, requests, gas_used } = self + .execute_without_verification_with_state_hook( + block, + total_difficulty, + Some(state_hook), + )?; + + // NOTE: we need to merge keep the reverts for the bundle retention + self.state.merge_transitions(BundleRetention::Reverts); + Ok(BlockExecutionOutput { state: self.state.take_bundle(), receipts, requests, gas_used }) + } } /// An executor for a batch of blocks. /// diff --git a/crates/ethereum/node/src/node.rs b/crates/ethereum/node/src/node.rs index c1c4653ae6aa..f17658bb32da 100644 --- a/crates/ethereum/node/src/node.rs +++ b/crates/ethereum/node/src/node.rs @@ -77,7 +77,8 @@ impl NodeTypesWithEngine for EthereumNode { } /// Add-ons w.r.t. l1 ethereum. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] +#[non_exhaustive] pub struct EthereumAddOns; impl NodeAddOns for EthereumAddOns { @@ -104,6 +105,10 @@ where fn components_builder(&self) -> Self::ComponentsBuilder { Self::components() } + + fn add_ons(&self) -> Self::AddOns { + EthereumAddOns::default() + } } /// A regular ethereum evm and executor builder. diff --git a/crates/ethereum/node/tests/it/builder.rs b/crates/ethereum/node/tests/it/builder.rs index 379f66e814b6..218839fbe019 100644 --- a/crates/ethereum/node/tests/it/builder.rs +++ b/crates/ethereum/node/tests/it/builder.rs @@ -22,7 +22,7 @@ fn test_basic_setup() { .with_database(db) .with_types::() .with_components(EthereumNode::components()) - .with_add_ons::() + .with_add_ons(EthereumAddOns::default()) .on_component_initialized(move |ctx| { let _provider = ctx.provider(); println!("{msg}"); @@ -54,7 +54,7 @@ async fn test_eth_launcher() { NodeTypesWithDBAdapter>>, >>() .with_components(EthereumNode::components()) - .with_add_ons::() + .with_add_ons(EthereumAddOns::default()) .launch_with_fn(|builder| { let launcher = EngineNodeLauncher::new( tasks.executor(), diff --git a/crates/ethereum/node/tests/it/exex.rs b/crates/ethereum/node/tests/it/exex.rs index db19aaaf3612..856220300c2b 100644 --- a/crates/ethereum/node/tests/it/exex.rs +++ b/crates/ethereum/node/tests/it/exex.rs @@ -33,7 +33,7 @@ fn basic_exex() { .with_database(db) .with_types::() .with_components(EthereumNode::components()) - .with_add_ons::() + .with_add_ons(EthereumAddOns::default()) .install_exex("dummy", move |ctx| future::ok(DummyExEx { _ctx: ctx })) .check_launch(); } diff --git a/crates/etl/src/lib.rs b/crates/etl/src/lib.rs index 3f978fabeee2..d30f432f9c19 100644 --- a/crates/etl/src/lib.rs +++ b/crates/etl/src/lib.rs @@ -190,14 +190,14 @@ pub struct EtlIter<'a> { files: &'a mut Vec, } -impl<'a> EtlIter<'a> { +impl EtlIter<'_> { /// Peeks into the next element pub fn peek(&self) -> Option<&(Vec, Vec)> { self.heap.peek().map(|(Reverse(entry), _)| entry) } } -impl<'a> Iterator for EtlIter<'a> { +impl Iterator for EtlIter<'_> { type Item = std::io::Result<(Vec, Vec)>; fn next(&mut self) -> Option { diff --git a/crates/evm/execution-types/src/chain.rs b/crates/evm/execution-types/src/chain.rs index 5db5495de59f..467bd4c0ec7c 100644 --- a/crates/evm/execution-types/src/chain.rs +++ b/crates/evm/execution-types/src/chain.rs @@ -355,7 +355,7 @@ impl Chain { #[derive(Debug)] pub struct DisplayBlocksChain<'a>(pub &'a BTreeMap); -impl<'a> fmt::Display for DisplayBlocksChain<'a> { +impl fmt::Display for DisplayBlocksChain<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut list = f.debug_list(); let mut values = self.0.values().map(|block| block.num_hash()); @@ -376,7 +376,7 @@ pub struct ChainBlocks<'a> { blocks: Cow<'a, BTreeMap>, } -impl<'a> ChainBlocks<'a> { +impl ChainBlocks<'_> { /// Creates a consuming iterator over all blocks in the chain with increasing block number. /// /// Note: this always yields at least one block. @@ -442,7 +442,7 @@ impl<'a> ChainBlocks<'a> { } } -impl<'a> IntoIterator for ChainBlocks<'a> { +impl IntoIterator for ChainBlocks<'_> { type Item = (BlockNumber, SealedBlockWithSenders); type IntoIter = std::collections::btree_map::IntoIter; @@ -571,7 +571,7 @@ pub(super) mod serde_bincode_compat { } } - impl<'a> SerializeAs for Chain<'a> { + impl SerializeAs for Chain<'_> { fn serialize_as(source: &super::Chain, serializer: S) -> Result where S: Serializer, diff --git a/crates/evm/src/either.rs b/crates/evm/src/either.rs index a3fca50ec7ee..e28ec3887f80 100644 --- a/crates/evm/src/either.rs +++ b/crates/evm/src/either.rs @@ -2,7 +2,10 @@ use core::fmt::Display; -use crate::execute::{BatchExecutor, BlockExecutorProvider, Executor}; +use crate::{ + execute::{BatchExecutor, BlockExecutorProvider, Executor}, + system_calls::OnStateHook, +}; use alloy_primitives::BlockNumber; use reth_execution_errors::BlockExecutionError; use reth_execution_types::{BlockExecutionInput, BlockExecutionOutput, ExecutionOutcome}; @@ -87,6 +90,20 @@ where Self::Right(b) => b.execute_with_state_witness(input, witness), } } + + fn execute_with_state_hook( + self, + input: Self::Input<'_>, + state_hook: F, + ) -> Result + where + F: OnStateHook, + { + match self { + Self::Left(a) => a.execute_with_state_hook(input, state_hook), + Self::Right(b) => b.execute_with_state_hook(input, state_hook), + } + } } impl BatchExecutor for Either diff --git a/crates/evm/src/execute.rs b/crates/evm/src/execute.rs index ffc08469dc8d..3fc2975f0ff7 100644 --- a/crates/evm/src/execute.rs +++ b/crates/evm/src/execute.rs @@ -12,6 +12,8 @@ use reth_prune_types::PruneModes; use revm::State; use revm_primitives::db::Database; +use crate::system_calls::OnStateHook; + /// A general purpose executor trait that executes an input (e.g. block) and produces an output /// (e.g. state changes and receipts). /// @@ -43,6 +45,16 @@ pub trait Executor { ) -> Result where F: FnMut(&State); + + /// Executes the EVM with the given input and accepts a state hook closure that is invoked with + /// the EVM state after execution. + fn execute_with_state_hook( + self, + input: Self::Input<'_>, + state_hook: F, + ) -> Result + where + F: OnStateHook; } /// A general purpose executor that can execute multiple inputs in sequence, validate the outputs, @@ -199,6 +211,17 @@ mod tests { { Err(BlockExecutionError::msg("execution unavailable for tests")) } + + fn execute_with_state_hook( + self, + _: Self::Input<'_>, + _: F, + ) -> Result + where + F: OnStateHook, + { + Err(BlockExecutionError::msg("execution unavailable for tests")) + } } impl BatchExecutor for TestExecutor { diff --git a/crates/evm/src/noop.rs b/crates/evm/src/noop.rs index 392bfd0bd722..3e01bfc4cc46 100644 --- a/crates/evm/src/noop.rs +++ b/crates/evm/src/noop.rs @@ -10,7 +10,10 @@ use reth_storage_errors::provider::ProviderError; use revm::State; use revm_primitives::db::Database; -use crate::execute::{BatchExecutor, BlockExecutorProvider, Executor}; +use crate::{ + execute::{BatchExecutor, BlockExecutorProvider, Executor}, + system_calls::OnStateHook, +}; const UNAVAILABLE_FOR_NOOP: &str = "execution unavailable for noop"; @@ -58,6 +61,17 @@ impl Executor for NoopBlockExecutorProvider { { Err(BlockExecutionError::msg(UNAVAILABLE_FOR_NOOP)) } + + fn execute_with_state_hook( + self, + _: Self::Input<'_>, + _: F, + ) -> Result + where + F: OnStateHook, + { + Err(BlockExecutionError::msg(UNAVAILABLE_FOR_NOOP)) + } } impl BatchExecutor for NoopBlockExecutorProvider { diff --git a/crates/evm/src/system_calls/mod.rs b/crates/evm/src/system_calls/mod.rs index ce5fec42184c..43baa1c766c2 100644 --- a/crates/evm/src/system_calls/mod.rs +++ b/crates/evm/src/system_calls/mod.rs @@ -49,22 +49,19 @@ pub struct SystemCaller<'a, EvmConfig, Chainspec, Hook = NoopHook> { hook: Option, } -impl<'a, EvmConfig, Chainspec> SystemCaller<'a, EvmConfig, Chainspec> { +impl<'a, EvmConfig, Chainspec> SystemCaller<'a, EvmConfig, Chainspec, NoopHook> { /// Create a new system caller with the given EVM config, database, and chain spec, and creates /// the EVM with the given initialized config and block environment. pub const fn new(evm_config: &'a EvmConfig, chain_spec: Chainspec) -> Self { Self { evm_config, chain_spec, hook: None } } -} - -impl<'a, EvmConfig, Chainspec, Hook> SystemCaller<'a, EvmConfig, Chainspec, Hook> { /// Installs a custom hook to be called after each state change. pub fn with_state_hook( self, - hook: H, + hook: Option, ) -> SystemCaller<'a, EvmConfig, Chainspec, H> { let Self { evm_config, chain_spec, .. } = self; - SystemCaller { evm_config, chain_spec, hook: Some(hook) } + SystemCaller { evm_config, chain_spec, hook } } /// Convenience method to consume the type and drop borrowed fields pub fn finish(self) {} @@ -88,7 +85,7 @@ where .build() } -impl<'a, EvmConfig, Chainspec, Hook> SystemCaller<'a, EvmConfig, Chainspec, Hook> +impl SystemCaller<'_, EvmConfig, Chainspec, Hook> where EvmConfig: ConfigureEvm
, Chainspec: EthereumHardforks, @@ -321,4 +318,11 @@ where eip7251::post_commit(result_and_state.result) } + + /// Delegate to stored `OnStateHook`, noop if hook is `None`. + pub fn on_state(&mut self, state: &ResultAndState) { + if let Some(ref mut hook) = &mut self.hook { + hook.on_state(state); + } + } } diff --git a/crates/evm/src/test_utils.rs b/crates/evm/src/test_utils.rs index cf45930aece9..45ab2e97734e 100644 --- a/crates/evm/src/test_utils.rs +++ b/crates/evm/src/test_utils.rs @@ -1,7 +1,10 @@ //! Helpers for testing. -use crate::execute::{ - BatchExecutor, BlockExecutionInput, BlockExecutionOutput, BlockExecutorProvider, Executor, +use crate::{ + execute::{ + BatchExecutor, BlockExecutionInput, BlockExecutionOutput, BlockExecutorProvider, Executor, + }, + system_calls::OnStateHook, }; use alloy_primitives::BlockNumber; use parking_lot::Mutex; @@ -73,6 +76,17 @@ impl Executor for MockExecutorProvider { { unimplemented!() } + + fn execute_with_state_hook( + self, + _: Self::Input<'_>, + _: F, + ) -> Result + where + F: OnStateHook, + { + unimplemented!() + } } impl BatchExecutor for MockExecutorProvider { diff --git a/crates/exex/exex/src/backfill/job.rs b/crates/exex/exex/src/backfill/job.rs index 7642edbac30e..7d66ce23d609 100644 --- a/crates/exex/exex/src/backfill/job.rs +++ b/crates/exex/exex/src/backfill/job.rs @@ -8,7 +8,7 @@ use alloy_primitives::BlockNumber; use reth_evm::execute::{ BatchExecutor, BlockExecutionError, BlockExecutionOutput, BlockExecutorProvider, Executor, }; -use reth_primitives::{Block, BlockBody, BlockWithSenders, Receipt}; +use reth_primitives::{Block, BlockWithSenders, Receipt}; use reth_primitives_traits::format_gas_throughput; use reth_provider::{ BlockReader, Chain, HeaderProvider, ProviderError, StateProviderFactory, TransactionVariant, @@ -64,6 +64,12 @@ where } fn execute_range(&mut self) -> Result { + debug!( + target: "exex::backfill", + range = ?self.range, + "Executing block range" + ); + let mut executor = self.executor.batch_executor(StateProviderDatabase::new( self.provider.history_by_block_number(self.range.start().saturating_sub(1))?, )); @@ -103,16 +109,8 @@ where // Unseal the block for execution let (block, senders) = block.into_components(); let (unsealed_header, hash) = block.header.split(); - let block = Block { - header: unsealed_header, - body: BlockBody { - transactions: block.body.transactions, - ommers: block.body.ommers, - withdrawals: block.body.withdrawals, - requests: block.body.requests, - }, - } - .with_senders_unchecked(senders); + let block = + Block { header: unsealed_header, body: block.body }.with_senders_unchecked(senders); executor.execute_and_verify_one((&block, td).into())?; execution_duration += execute_start.elapsed(); diff --git a/crates/exex/exex/src/backfill/stream.rs b/crates/exex/exex/src/backfill/stream.rs index 07b710b7e389..ca4bd326daa0 100644 --- a/crates/exex/exex/src/backfill/stream.rs +++ b/crates/exex/exex/src/backfill/stream.rs @@ -15,6 +15,7 @@ use reth_primitives::{BlockWithSenders, Receipt}; use reth_provider::{BlockReader, Chain, HeaderProvider, StateProviderFactory}; use reth_prune_types::PruneModes; use reth_stages_api::ExecutionStageThresholds; +use reth_tracing::tracing::debug; use tokio::task::JoinHandle; /// The default parallelism for active tasks in [`StreamBackfillJob`]. @@ -40,6 +41,7 @@ pub struct StreamBackfillJob { tasks: BackfillTasks, parallelism: usize, batch_size: usize, + thresholds: ExecutionStageThresholds, } impl StreamBackfillJob { @@ -118,11 +120,12 @@ where // If we have range bounds, then we can spawn a new task for that range if let Some((first, last)) = range_bounds { let range = first..=last; + debug!(target: "exex::backfill", tasks = %this.tasks.len(), ?range, "Spawning new backfill task"); let mut job = BackfillJob { executor: this.executor.clone(), provider: this.provider.clone(), prune_modes: this.prune_modes.clone(), - thresholds: ExecutionStageThresholds::default(), + thresholds: this.thresholds.clone(), range, stream_parallelism: this.parallelism, }; @@ -148,12 +151,14 @@ impl From> for StreamBackfillJob From> for StreamBackfillJob { fn from(job: BackfillJob) -> Self { + let batch_size = job.thresholds.max_blocks.map_or(DEFAULT_BATCH_SIZE, |max| max as usize); Self { executor: job.executor, provider: job.provider, @@ -161,7 +166,11 @@ impl From> for StreamBackfillJob { /// considered delivered by the node. pub notifications: ExExNotifications, - /// node components + /// Node components pub components: Node, } @@ -92,4 +93,16 @@ impl ExExContext { pub fn task_executor(&self) -> &TaskExecutor { self.components.task_executor() } + + /// Sets notifications stream to [`crate::ExExNotificationsWithoutHead`], a stream of + /// notifications without a head. + pub fn set_notifications_without_head(&mut self) { + self.notifications.set_without_head(); + } + + /// Sets notifications stream to [`crate::ExExNotificationsWithHead`], a stream of notifications + /// with the provided head. + pub fn set_notifications_with_head(&mut self, head: ExExHead) { + self.notifications.set_with_head(head); + } } diff --git a/crates/exex/exex/src/manager.rs b/crates/exex/exex/src/manager.rs index e8e24c09db02..31dc822222b6 100644 --- a/crates/exex/exex/src/manager.rs +++ b/crates/exex/exex/src/manager.rs @@ -610,12 +610,16 @@ impl Clone for ExExManagerHandle { mod tests { use super::*; use alloy_primitives::B256; - use eyre::OptionExt; - use futures::{FutureExt, StreamExt}; + use futures::StreamExt; use rand::Rng; + use reth_db_common::init::init_genesis; + use reth_evm_ethereum::execute::EthExecutorProvider; use reth_primitives::SealedBlockWithSenders; - use reth_provider::{test_utils::create_test_provider_factory, BlockWriter, Chain}; - use reth_testing_utils::generators::{self, random_block}; + use reth_provider::{ + providers::BlockchainProvider2, test_utils::create_test_provider_factory, BlockReader, + Chain, TransactionVariant, + }; + use reth_testing_utils::generators; fn empty_finalized_header_stream() -> ForkChoiceStream { let (tx, rx) = watch::channel(None); @@ -975,11 +979,20 @@ mod tests { #[tokio::test] async fn exex_handle_new() { + let provider_factory = create_test_provider_factory(); + init_genesis(&provider_factory).unwrap(); + let provider = BlockchainProvider2::new(provider_factory).unwrap(); + let temp_dir = tempfile::tempdir().unwrap(); let wal = Wal::new(temp_dir.path()).unwrap(); - let (mut exex_handle, _, mut notifications) = - ExExHandle::new("test_exex".to_string(), Head::default(), (), (), wal.handle()); + let (mut exex_handle, _, mut notifications) = ExExHandle::new( + "test_exex".to_string(), + Head::default(), + provider, + EthExecutorProvider::mainnet(), + wal.handle(), + ); // Check initial state assert_eq!(exex_handle.id, "test_exex"); @@ -1008,7 +1021,7 @@ mod tests { // Send a notification and ensure it's received correctly match exex_handle.send(&mut cx, &(22, notification.clone())) { Poll::Ready(Ok(())) => { - let received_notification = notifications.next().await.unwrap(); + let received_notification = notifications.next().await.unwrap().unwrap(); assert_eq!(received_notification, notification); } Poll::Pending => panic!("Notification send is pending"), @@ -1021,11 +1034,20 @@ mod tests { #[tokio::test] async fn test_notification_if_finished_height_gt_chain_tip() { + let provider_factory = create_test_provider_factory(); + init_genesis(&provider_factory).unwrap(); + let provider = BlockchainProvider2::new(provider_factory).unwrap(); + let temp_dir = tempfile::tempdir().unwrap(); let wal = Wal::new(temp_dir.path()).unwrap(); - let (mut exex_handle, _, mut notifications) = - ExExHandle::new("test_exex".to_string(), Head::default(), (), (), wal.handle()); + let (mut exex_handle, _, mut notifications) = ExExHandle::new( + "test_exex".to_string(), + Head::default(), + provider, + EthExecutorProvider::mainnet(), + wal.handle(), + ); // Set finished_height to a value higher than the block tip exex_handle.finished_height = Some(BlockNumHash::new(15, B256::random())); @@ -1046,11 +1068,7 @@ mod tests { poll_fn(|cx| { // The notification should be skipped, so nothing should be sent. // Check that the receiver channel is indeed empty - assert_eq!( - notifications.poll_next_unpin(cx), - Poll::Pending, - "Receiver channel should be empty" - ); + assert!(notifications.poll_next_unpin(cx).is_pending()); Poll::Ready(()) }) .await; @@ -1066,11 +1084,20 @@ mod tests { #[tokio::test] async fn test_sends_chain_reorged_notification() { + let provider_factory = create_test_provider_factory(); + init_genesis(&provider_factory).unwrap(); + let provider = BlockchainProvider2::new(provider_factory).unwrap(); + let temp_dir = tempfile::tempdir().unwrap(); let wal = Wal::new(temp_dir.path()).unwrap(); - let (mut exex_handle, _, mut notifications) = - ExExHandle::new("test_exex".to_string(), Head::default(), (), (), wal.handle()); + let (mut exex_handle, _, mut notifications) = ExExHandle::new( + "test_exex".to_string(), + Head::default(), + provider, + EthExecutorProvider::mainnet(), + wal.handle(), + ); let notification = ExExNotification::ChainReorged { old: Arc::new(Chain::default()), @@ -1086,7 +1113,7 @@ mod tests { // Send the notification match exex_handle.send(&mut cx, &(22, notification.clone())) { Poll::Ready(Ok(())) => { - let received_notification = notifications.next().await.unwrap(); + let received_notification = notifications.next().await.unwrap().unwrap(); assert_eq!(received_notification, notification); } Poll::Pending | Poll::Ready(Err(_)) => { @@ -1100,11 +1127,20 @@ mod tests { #[tokio::test] async fn test_sends_chain_reverted_notification() { + let provider_factory = create_test_provider_factory(); + init_genesis(&provider_factory).unwrap(); + let provider = BlockchainProvider2::new(provider_factory).unwrap(); + let temp_dir = tempfile::tempdir().unwrap(); let wal = Wal::new(temp_dir.path()).unwrap(); - let (mut exex_handle, _, mut notifications) = - ExExHandle::new("test_exex".to_string(), Head::default(), (), (), wal.handle()); + let (mut exex_handle, _, mut notifications) = ExExHandle::new( + "test_exex".to_string(), + Head::default(), + provider, + EthExecutorProvider::mainnet(), + wal.handle(), + ); let notification = ExExNotification::ChainReverted { old: Arc::new(Chain::default()) }; @@ -1117,7 +1153,7 @@ mod tests { // Send the notification match exex_handle.send(&mut cx, &(22, notification.clone())) { Poll::Ready(Ok(())) => { - let received_notification = notifications.next().await.unwrap(); + let received_notification = notifications.next().await.unwrap().unwrap(); assert_eq!(received_notification, notification); } Poll::Pending | Poll::Ready(Err(_)) => { @@ -1135,30 +1171,34 @@ mod tests { let mut rng = generators::rng(); + let provider_factory = create_test_provider_factory(); + let genesis_hash = init_genesis(&provider_factory).unwrap(); + let genesis_block = provider_factory + .sealed_block_with_senders(genesis_hash.into(), TransactionVariant::NoHash) + .unwrap() + .ok_or_else(|| eyre::eyre!("genesis block not found"))?; + let provider = BlockchainProvider2::new(provider_factory).unwrap(); + let temp_dir = tempfile::tempdir().unwrap(); let wal = Wal::new(temp_dir.path()).unwrap(); - let provider_factory = create_test_provider_factory(); - - let block = random_block(&mut rng, 0, Default::default()) - .seal_with_senders() - .ok_or_eyre("failed to recover senders")?; - let provider_rw = provider_factory.provider_rw()?; - provider_rw.insert_block(block.clone())?; - provider_rw.commit()?; + let (exex_handle, events_tx, mut notifications) = ExExHandle::new( + "test_exex".to_string(), + Head::default(), + provider.clone(), + EthExecutorProvider::mainnet(), + wal.handle(), + ); let notification = ExExNotification::ChainCommitted { - new: Arc::new(Chain::new(vec![block.clone()], Default::default(), None)), + new: Arc::new(Chain::new(vec![genesis_block.clone()], Default::default(), None)), }; let (finalized_headers_tx, rx) = watch::channel(None); let finalized_header_stream = ForkChoiceStream::new(rx); - let (exex_handle, events_tx, mut notifications) = - ExExHandle::new("test_exex".to_string(), Head::default(), (), (), wal.handle()); - let mut exex_manager = std::pin::pin!(ExExManager::new( - provider_factory, + provider, vec![exex_handle], 1, wal, @@ -1170,16 +1210,13 @@ mod tests { exex_manager.handle().send(notification.clone())?; assert!(exex_manager.as_mut().poll(&mut cx)?.is_pending()); - assert_eq!( - notifications.next().poll_unpin(&mut cx), - Poll::Ready(Some(notification.clone())) - ); + assert_eq!(notifications.next().await.unwrap().unwrap(), notification.clone()); assert_eq!( exex_manager.wal.iter_notifications()?.collect::>>()?, [notification.clone()] ); - finalized_headers_tx.send(Some(block.header.clone()))?; + finalized_headers_tx.send(Some(genesis_block.header.clone()))?; assert!(exex_manager.as_mut().poll(&mut cx).is_pending()); // WAL isn't finalized because the ExEx didn't emit the `FinishedHeight` event assert_eq!( @@ -1192,7 +1229,7 @@ mod tests { .send(ExExEvent::FinishedHeight((rng.gen::(), rng.gen::()).into())) .unwrap(); - finalized_headers_tx.send(Some(block.header.clone()))?; + finalized_headers_tx.send(Some(genesis_block.header.clone()))?; assert!(exex_manager.as_mut().poll(&mut cx).is_pending()); // WAL isn't finalized because the ExEx emitted a `FinishedHeight` event with a // non-canonical block @@ -1202,9 +1239,9 @@ mod tests { ); // Send a `FinishedHeight` event with a canonical block - events_tx.send(ExExEvent::FinishedHeight(block.num_hash())).unwrap(); + events_tx.send(ExExEvent::FinishedHeight(genesis_block.num_hash())).unwrap(); - finalized_headers_tx.send(Some(block.header.clone()))?; + finalized_headers_tx.send(Some(genesis_block.header.clone()))?; assert!(exex_manager.as_mut().poll(&mut cx).is_pending()); // WAL is finalized assert!(exex_manager.wal.iter_notifications()?.next().is_none()); diff --git a/crates/exex/exex/src/notifications.rs b/crates/exex/exex/src/notifications.rs index 116dac95422b..6efdb1775cf1 100644 --- a/crates/exex/exex/src/notifications.rs +++ b/crates/exex/exex/src/notifications.rs @@ -13,27 +13,28 @@ use std::{ }; use tokio::sync::mpsc::Receiver; -/// A stream of [`ExExNotification`]s. The stream will emit notifications for all blocks. +/// A stream of [`ExExNotification`]s. The stream will emit notifications for all blocks. If the +/// stream is configured with a head via [`ExExNotifications::set_with_head`] or +/// [`ExExNotifications::with_head`], it will run backfill jobs to catch up to the node head. +#[derive(Debug)] pub struct ExExNotifications { - node_head: Head, - provider: P, - executor: E, - notifications: Receiver, - wal_handle: WalHandle, + inner: ExExNotificationsInner, } -impl Debug for ExExNotifications { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("ExExNotifications") - .field("provider", &self.provider) - .field("executor", &self.executor) - .field("notifications", &self.notifications) - .finish() - } +#[derive(Debug)] +enum ExExNotificationsInner { + /// A stream of [`ExExNotification`]s. The stream will emit notifications for all blocks. + WithoutHead(ExExNotificationsWithoutHead), + /// A stream of [`ExExNotification`]s. The stream will only emit notifications for blocks that + /// are committed or reverted after the given head. + WithHead(ExExNotificationsWithHead), + /// Internal state used when transitioning between [`ExExNotificationsInner::WithoutHead`] and + /// [`ExExNotificationsInner::WithHead`]. + Invalid, } impl ExExNotifications { - /// Creates a new instance of [`ExExNotifications`]. + /// Creates a new stream of [`ExExNotifications`] without a head. pub const fn new( node_head: Head, provider: P, @@ -41,73 +42,131 @@ impl ExExNotifications { notifications: Receiver, wal_handle: WalHandle, ) -> Self { - Self { node_head, provider, executor, notifications, wal_handle } + Self { + inner: ExExNotificationsInner::WithoutHead(ExExNotificationsWithoutHead::new( + node_head, + provider, + executor, + notifications, + wal_handle, + )), + } } - /// Receives the next value for this receiver. + /// Sets [`ExExNotifications`] to a stream of [`ExExNotification`]s without a head. /// - /// This method returns `None` if the channel has been closed and there are - /// no remaining messages in the channel's buffer. This indicates that no - /// further values can ever be received from this `Receiver`. The channel is - /// closed when all senders have been dropped, or when [`Receiver::close`] is called. + /// It's a no-op if the stream has already been configured without a head. /// - /// # Cancel safety - /// - /// This method is cancel safe. If `recv` is used as the event in a - /// [`tokio::select!`] statement and some other branch - /// completes first, it is guaranteed that no messages were received on this - /// channel. - /// - /// For full documentation, see [`Receiver::recv`]. - #[deprecated(note = "use `ExExNotifications::next` and its `Stream` implementation instead")] - pub async fn recv(&mut self) -> Option { - self.notifications.recv().await + /// See the documentation of [`ExExNotificationsWithoutHead`] for more details. + pub fn set_without_head(&mut self) { + let current = std::mem::replace(&mut self.inner, ExExNotificationsInner::Invalid); + self.inner = ExExNotificationsInner::WithoutHead(match current { + ExExNotificationsInner::WithoutHead(notifications) => notifications, + ExExNotificationsInner::WithHead(notifications) => ExExNotificationsWithoutHead::new( + notifications.node_head, + notifications.provider, + notifications.executor, + notifications.notifications, + notifications.wal_handle, + ), + ExExNotificationsInner::Invalid => unreachable!(), + }); } - /// Polls to receive the next message on this channel. + /// Returns a new [`ExExNotifications`] without a head. /// - /// This method returns: - /// - /// * `Poll::Pending` if no messages are available but the channel is not closed, or if a - /// spurious failure happens. - /// * `Poll::Ready(Some(message))` if a message is available. - /// * `Poll::Ready(None)` if the channel has been closed and all messages sent before it was - /// closed have been received. + /// See the documentation of [`ExExNotificationsWithoutHead`] for more details. + pub fn without_head(mut self) -> Self { + self.set_without_head(); + self + } + + /// Sets [`ExExNotifications`] to a stream of [`ExExNotification`]s with the provided head. /// - /// When the method returns `Poll::Pending`, the `Waker` in the provided - /// `Context` is scheduled to receive a wakeup when a message is sent on any - /// receiver, or when the channel is closed. Note that on multiple calls to - /// `poll_recv` or `poll_recv_many`, only the `Waker` from the `Context` - /// passed to the most recent call is scheduled to receive a wakeup. + /// It's a no-op if the stream has already been configured with a head. /// - /// If this method returns `Poll::Pending` due to a spurious failure, then - /// the `Waker` will be notified when the situation causing the spurious - /// failure has been resolved. Note that receiving such a wakeup does not - /// guarantee that the next call will succeed — it could fail with another - /// spurious failure. + /// See the documentation of [`ExExNotificationsWithHead`] for more details. + pub fn set_with_head(&mut self, exex_head: ExExHead) { + let current = std::mem::replace(&mut self.inner, ExExNotificationsInner::Invalid); + self.inner = ExExNotificationsInner::WithHead(match current { + ExExNotificationsInner::WithoutHead(notifications) => { + notifications.with_head(exex_head) + } + ExExNotificationsInner::WithHead(notifications) => ExExNotificationsWithHead::new( + notifications.node_head, + notifications.provider, + notifications.executor, + notifications.notifications, + notifications.wal_handle, + exex_head, + ), + ExExNotificationsInner::Invalid => unreachable!(), + }); + } + + /// Returns a new [`ExExNotifications`] with the provided head. /// - /// For full documentation, see [`Receiver::poll_recv`]. - #[deprecated( - note = "use `ExExNotifications::poll_next` and its `Stream` implementation instead" - )] - pub fn poll_recv(&mut self, cx: &mut Context<'_>) -> Poll> { - self.notifications.poll_recv(cx) + /// See the documentation of [`ExExNotificationsWithHead`] for more details. + pub fn with_head(mut self, exex_head: ExExHead) -> Self { + self.set_with_head(exex_head); + self } } -impl ExExNotifications +impl Stream for ExExNotifications where P: BlockReader + HeaderProvider + StateProviderFactory + Clone + Unpin + 'static, E: BlockExecutorProvider + Clone + Unpin + 'static, { - /// Subscribe to notifications with the given head. This head is the ExEx's - /// latest view of the host chain. - /// - /// Notifications will be sent starting from the head, not inclusive. For - /// example, if `head.number == 10`, then the first notification will be - /// with `block.number == 11`. A `head.number` of 10 indicates that the ExEx - /// has processed up to block 10, and is ready to process block 11. - pub fn with_head(self, head: ExExHead) -> ExExNotificationsWithHead { + type Item = eyre::Result; + + fn poll_next( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + match &mut self.get_mut().inner { + ExExNotificationsInner::WithoutHead(notifications) => { + notifications.poll_next_unpin(cx).map(|result| result.map(Ok)) + } + ExExNotificationsInner::WithHead(notifications) => notifications.poll_next_unpin(cx), + ExExNotificationsInner::Invalid => unreachable!(), + } + } +} + +/// A stream of [`ExExNotification`]s. The stream will emit notifications for all blocks. +pub struct ExExNotificationsWithoutHead { + node_head: Head, + provider: P, + executor: E, + notifications: Receiver, + wal_handle: WalHandle, +} + +impl Debug for ExExNotificationsWithoutHead { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ExExNotifications") + .field("provider", &self.provider) + .field("executor", &self.executor) + .field("notifications", &self.notifications) + .finish() + } +} + +impl ExExNotificationsWithoutHead { + /// Creates a new instance of [`ExExNotificationsWithoutHead`]. + const fn new( + node_head: Head, + provider: P, + executor: E, + notifications: Receiver, + wal_handle: WalHandle, + ) -> Self { + Self { node_head, provider, executor, notifications, wal_handle } + } + + /// Subscribe to notifications with the given head. + fn with_head(self, head: ExExHead) -> ExExNotificationsWithHead { ExExNotificationsWithHead::new( self.node_head, self.provider, @@ -119,7 +178,7 @@ where } } -impl Stream for ExExNotifications { +impl Stream for ExExNotificationsWithoutHead { type Item = ExExNotification; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { @@ -128,7 +187,13 @@ impl Stream for ExExNotifications { } /// A stream of [`ExExNotification`]s. The stream will only emit notifications for blocks that are -/// committed or reverted after the given head. +/// committed or reverted after the given head. The head is the ExEx's latest view of the host +/// chain. +/// +/// Notifications will be sent starting from the head, not inclusive. For example, if +/// `exex_head.number == 10`, then the first notification will be with `block.number == 11`. An +/// `exex_head.number` of 10 indicates that the ExEx has processed up to block 10, and is ready to +/// process block 11. #[derive(Debug)] pub struct ExExNotificationsWithHead { node_head: Head, @@ -147,13 +212,9 @@ pub struct ExExNotificationsWithHead { backfill_job: Option>, } -impl ExExNotificationsWithHead -where - P: BlockReader + HeaderProvider + StateProviderFactory + Clone + Unpin + 'static, - E: BlockExecutorProvider + Clone + Unpin + 'static, -{ +impl ExExNotificationsWithHead { /// Creates a new [`ExExNotificationsWithHead`]. - pub const fn new( + const fn new( node_head: Head, provider: P, executor: E, @@ -173,7 +234,13 @@ where backfill_job: None, } } +} +impl ExExNotificationsWithHead +where + P: BlockReader + HeaderProvider + StateProviderFactory + Clone + Unpin + 'static, + E: BlockExecutorProvider + Clone + Unpin + 'static, +{ /// Checks if the ExEx head is on the canonical chain. /// /// If the head block is not found in the database or it's ahead of the node head, it means @@ -367,7 +434,7 @@ mod tests { notifications_tx.send(notification.clone()).await?; - let mut notifications = ExExNotifications::new( + let mut notifications = ExExNotificationsWithoutHead::new( node_head, provider, EthExecutorProvider::mainnet(), @@ -438,7 +505,7 @@ mod tests { notifications_tx.send(notification.clone()).await?; - let mut notifications = ExExNotifications::new( + let mut notifications = ExExNotificationsWithoutHead::new( node_head, provider, EthExecutorProvider::mainnet(), @@ -528,7 +595,7 @@ mod tests { notifications_tx.send(new_notification.clone()).await?; - let mut notifications = ExExNotifications::new( + let mut notifications = ExExNotificationsWithoutHead::new( node_head, provider, EthExecutorProvider::mainnet(), @@ -609,7 +676,7 @@ mod tests { notifications_tx.send(new_notification.clone()).await?; - let mut notifications = ExExNotifications::new( + let mut notifications = ExExNotificationsWithoutHead::new( node_head, provider, EthExecutorProvider::mainnet(), diff --git a/crates/exex/exex/src/wal/metrics.rs b/crates/exex/exex/src/wal/metrics.rs index 7726fc978d47..01837629407e 100644 --- a/crates/exex/exex/src/wal/metrics.rs +++ b/crates/exex/exex/src/wal/metrics.rs @@ -7,10 +7,10 @@ use reth_metrics::Metrics; pub(super) struct Metrics { /// Size of all notifications in WAL in bytes pub size_bytes: Gauge, - /// Total number of notifications in WAL - pub notifications_total: Gauge, - /// Total number of committed blocks in WAL - pub committed_blocks_total: Gauge, + /// Number of notifications in WAL + pub notifications_count: Gauge, + /// Number of committed blocks in WAL + pub committed_blocks_count: Gauge, /// Lowest committed block height in WAL pub lowest_committed_block_height: Gauge, /// Highest committed block height in WAL diff --git a/crates/exex/exex/src/wal/mod.rs b/crates/exex/exex/src/wal/mod.rs index 2341b56d1044..00b0ea919ef6 100644 --- a/crates/exex/exex/src/wal/mod.rs +++ b/crates/exex/exex/src/wal/mod.rs @@ -167,8 +167,8 @@ impl WalInner { fn update_metrics(&self, block_cache: &BlockCache, size_delta: i64) { self.metrics.size_bytes.increment(size_delta as f64); - self.metrics.notifications_total.set(block_cache.notification_max_blocks.len() as f64); - self.metrics.committed_blocks_total.set(block_cache.committed_blocks.len() as f64); + self.metrics.notifications_count.set(block_cache.notification_max_blocks.len() as f64); + self.metrics.committed_blocks_count.set(block_cache.committed_blocks.len() as f64); if let Some(lowest_committed_block_height) = block_cache.lowest_committed_block_height { self.metrics.lowest_committed_block_height.set(lowest_committed_block_height as f64); diff --git a/crates/exex/test-utils/src/lib.rs b/crates/exex/test-utils/src/lib.rs index b8be08616b4a..f4561a6b55f2 100644 --- a/crates/exex/test-utils/src/lib.rs +++ b/crates/exex/test-utils/src/lib.rs @@ -154,6 +154,10 @@ where .consensus(TestConsensusBuilder::default()) .engine_validator(EthereumEngineValidatorBuilder::default()) } + + fn add_ons(&self) -> Self::AddOns { + EthereumAddOns::default() + } } /// A shared [`TempDatabase`] used for testing diff --git a/crates/exex/types/src/notification.rs b/crates/exex/types/src/notification.rs index 53411250270d..61d42a3319be 100644 --- a/crates/exex/types/src/notification.rs +++ b/crates/exex/types/src/notification.rs @@ -136,7 +136,7 @@ pub(super) mod serde_bincode_compat { } } - impl<'a> SerializeAs for ExExNotification<'a> { + impl SerializeAs for ExExNotification<'_> { fn serialize_as( source: &super::ExExNotification, serializer: S, diff --git a/crates/fs-util/src/lib.rs b/crates/fs-util/src/lib.rs index 0cfcf04539bd..d242ecc98e2d 100644 --- a/crates/fs-util/src/lib.rs +++ b/crates/fs-util/src/lib.rs @@ -307,6 +307,9 @@ where F: FnOnce(&mut File) -> std::result::Result<(), E>, E: Into>, { + #[cfg(windows)] + use std::os::windows::fs::OpenOptionsExt; + let mut tmp_path = file_path.to_path_buf(); tmp_path.set_extension("tmp"); diff --git a/crates/net/eth-wire-types/Cargo.toml b/crates/net/eth-wire-types/Cargo.toml index 6ce51786f282..82c9fe37a44d 100644 --- a/crates/net/eth-wire-types/Cargo.toml +++ b/crates/net/eth-wire-types/Cargo.toml @@ -20,7 +20,6 @@ reth-primitives.workspace = true # ethereum alloy-chains = { workspace = true, features = ["rlp"] } alloy-eips.workspace = true -alloy-genesis.workspace = true alloy-primitives.workspace = true alloy-rlp = { workspace = true, features = ["derive"] } @@ -36,6 +35,7 @@ proptest-arbitrary-interop = { workspace = true, optional = true } [dev-dependencies] reth-primitives = { workspace = true, features = ["arbitrary"] } +alloy-genesis.workspace = true alloy-chains = { workspace = true, features = ["arbitrary"] } arbitrary = { workspace = true, features = ["derive"] } proptest.workspace = true diff --git a/crates/net/eth-wire-types/src/status.rs b/crates/net/eth-wire-types/src/status.rs index baf1e2991522..a5e7530ec09a 100644 --- a/crates/net/eth-wire-types/src/status.rs +++ b/crates/net/eth-wire-types/src/status.rs @@ -1,9 +1,8 @@ use crate::EthVersion; use alloy_chains::{Chain, NamedChain}; -use alloy_genesis::Genesis; use alloy_primitives::{hex, B256, U256}; use alloy_rlp::{RlpDecodable, RlpEncodable}; -use reth_chainspec::{ChainSpec, EthChainSpec, Hardforks, MAINNET}; +use reth_chainspec::{EthChainSpec, Hardforks, MAINNET}; use reth_codecs_derive::add_arbitrary_tests; use reth_primitives::{EthereumHardfork, ForkId, Head}; use std::fmt::{Debug, Display}; @@ -43,23 +42,6 @@ pub struct Status { pub forkid: ForkId, } -impl From for Status { - fn from(genesis: Genesis) -> Self { - let chain = genesis.config.chain_id; - let total_difficulty = genesis.difficulty; - let chainspec = ChainSpec::from(genesis); - - Self { - version: EthVersion::Eth68 as u8, - chain: Chain::from_id(chain), - total_difficulty, - blockhash: chainspec.genesis_hash(), - genesis: chainspec.genesis_hash(), - forkid: chainspec.fork_id(&Head::default()), - } - } -} - impl Status { /// Helper for returning a builder for the status message. pub fn builder() -> StatusBuilder { @@ -71,10 +53,10 @@ impl Status { self.version = version as u8; } - /// Create a [`StatusBuilder`] from the given [`ChainSpec`] and head block. + /// Create a [`StatusBuilder`] from the given [`EthChainSpec`] and head block. /// - /// Sets the `chain` and `genesis`, `blockhash`, and `forkid` fields based on the [`ChainSpec`] - /// and head. + /// Sets the `chain` and `genesis`, `blockhash`, and `forkid` fields based on the + /// [`EthChainSpec`] and head. pub fn spec_builder(spec: Spec, head: &Head) -> StatusBuilder where Spec: EthChainSpec + Hardforks, diff --git a/crates/net/network/src/config.rs b/crates/net/network/src/config.rs index 8217a02a1bab..60b559b9dbc3 100644 --- a/crates/net/network/src/config.rs +++ b/crates/net/network/src/config.rs @@ -106,6 +106,14 @@ impl NetworkConfig { NetworkConfig::builder(secret_key).build(client) } + /// Apply a function to the config. + pub fn apply(self, f: F) -> Self + where + F: FnOnce(Self) -> Self, + { + f(self) + } + /// Sets the config to use for the discovery v4 protocol. pub fn set_discovery_v4(mut self, discovery_config: Discv4Config) -> Self { self.discovery_v4_config = Some(discovery_config); diff --git a/crates/net/network/src/peers.rs b/crates/net/network/src/peers.rs index b9196d29e8e4..3d5ff7a0d43a 100644 --- a/crates/net/network/src/peers.rs +++ b/crates/net/network/src/peers.rs @@ -1131,7 +1131,7 @@ mod tests { peers: &'a mut PeersManager, } - impl<'a> Future for PeerActionFuture<'a> { + impl Future for PeerActionFuture<'_> { type Output = PeerAction; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { diff --git a/crates/net/network/src/transactions/mod.rs b/crates/net/network/src/transactions/mod.rs index 7e3b71aa4365..2fa4ccfbb606 100644 --- a/crates/net/network/src/transactions/mod.rs +++ b/crates/net/network/src/transactions/mod.rs @@ -1033,7 +1033,7 @@ where has_bad_transactions = true; } else { // this is a new transaction that should be imported into the pool - let pool_transaction = Pool::Transaction::from_pooled(tx); + let pool_transaction = Pool::Transaction::from_pooled(tx.into()); new_txs.push(pool_transaction); entry.insert(HashSet::from([peer_id])); @@ -1396,11 +1396,14 @@ impl PropagateTransaction { } /// Create a new instance from a pooled transaction - fn new>( - tx: Arc>, - ) -> Self { + fn new(tx: Arc>) -> Self + where + T: PoolTransaction>, + { let size = tx.encoded_length(); - let transaction = Arc::new(tx.transaction.clone().into_consensus().into_signed()); + let recovered: TransactionSignedEcRecovered = + tx.transaction.clone().into_consensus().into(); + let transaction = Arc::new(recovered.into_signed()); Self { size, transaction } } } diff --git a/crates/node/builder/src/builder/add_ons.rs b/crates/node/builder/src/builder/add_ons.rs index 910cd5896efe..26d7553bb86d 100644 --- a/crates/node/builder/src/builder/add_ons.rs +++ b/crates/node/builder/src/builder/add_ons.rs @@ -14,6 +14,8 @@ pub struct AddOns> { pub exexs: Vec<(String, Box>)>, /// Additional RPC add-ons. pub rpc: RpcAddOns, + /// Additional captured addons. + pub addons: AddOns, } /// Captures node specific addons that can be installed on top of the type configured node and are diff --git a/crates/node/builder/src/builder/mod.rs b/crates/node/builder/src/builder/mod.rs index 4989589c9f98..d2ce2a1d8e2f 100644 --- a/crates/node/builder/src/builder/mod.rs +++ b/crates/node/builder/src/builder/mod.rs @@ -243,7 +243,7 @@ where where N: Node, ChainSpec = ChainSpec>, { - self.with_types().with_components(node.components_builder()).with_add_ons::() + self.with_types().with_components(node.components_builder()).with_add_ons(node.add_ons()) } } @@ -311,7 +311,7 @@ where where N: Node, ChainSpec = ChainSpec>, { - self.with_types().with_components(node.components_builder()).with_add_ons::() + self.with_types().with_components(node.components_builder()).with_add_ons(node.add_ons()) } /// Launches a preconfigured [Node] @@ -375,12 +375,15 @@ where { /// Advances the state of the node builder to the next state where all customizable /// [`NodeAddOns`] types are configured. - pub fn with_add_ons(self) -> WithLaunchContext> + pub fn with_add_ons( + self, + add_ons: AO, + ) -> WithLaunchContext> where AO: NodeAddOns>, { WithLaunchContext { - builder: self.builder.with_add_ons::(), + builder: self.builder.with_add_ons(add_ons), task_executor: self.task_executor, } } diff --git a/crates/node/builder/src/builder/states.rs b/crates/node/builder/src/builder/states.rs index 30ef54c5683e..80930ef743cd 100644 --- a/crates/node/builder/src/builder/states.rs +++ b/crates/node/builder/src/builder/states.rs @@ -58,6 +58,7 @@ impl NodeBuilderWithTypes { hooks: NodeHooks::default(), rpc: RpcAddOns { hooks: RpcHooks::default() }, exexs: Vec::new(), + addons: (), }, } } @@ -168,7 +169,7 @@ where { /// Advances the state of the node builder to the next state where all customizable /// [`NodeAddOns`] types are configured. - pub fn with_add_ons(self) -> NodeBuilderWithComponents + pub fn with_add_ons(self, addons: AO) -> NodeBuilderWithComponents where AO: NodeAddOns>, { @@ -182,6 +183,7 @@ where hooks: NodeHooks::default(), rpc: RpcAddOns { hooks: RpcHooks::default() }, exexs: Vec::new(), + addons, }, } } diff --git a/crates/node/builder/src/components/pool.rs b/crates/node/builder/src/components/pool.rs index af31f29307eb..234455913c6f 100644 --- a/crates/node/builder/src/components/pool.rs +++ b/crates/node/builder/src/components/pool.rs @@ -1,8 +1,8 @@ //! Pool component for the node builder. -use std::future::Future; - -use reth_transaction_pool::TransactionPool; +use alloy_primitives::Address; +use reth_transaction_pool::{PoolConfig, SubPoolLimit, TransactionPool}; +use std::{collections::HashSet, future::Future}; use crate::{BuilderContext, FullNodeTypes}; @@ -34,3 +34,59 @@ where self(ctx) } } + +/// Convenience type to override cli or default pool configuration during build. +#[derive(Debug, Clone, Default)] +pub struct PoolBuilderConfigOverrides { + /// Max number of transaction in the pending sub-pool + pub pending_limit: Option, + /// Max number of transaction in the basefee sub-pool + pub basefee_limit: Option, + /// Max number of transaction in the queued sub-pool + pub queued_limit: Option, + /// Max number of transactions in the blob sub-pool + pub blob_limit: Option, + /// Max number of executable transaction slots guaranteed per account + pub max_account_slots: Option, + /// Minimum base fee required by the protocol. + pub minimal_protocol_basefee: Option, + /// Addresses that will be considered as local. Above exemptions apply. + pub local_addresses: HashSet
, +} + +impl PoolBuilderConfigOverrides { + /// Applies the configured overrides to the given [`PoolConfig`]. + pub fn apply(self, mut config: PoolConfig) -> PoolConfig { + let Self { + pending_limit, + basefee_limit, + queued_limit, + blob_limit, + max_account_slots, + minimal_protocol_basefee, + local_addresses, + } = self; + + if let Some(pending_limit) = pending_limit { + config.pending_limit = pending_limit; + } + if let Some(basefee_limit) = basefee_limit { + config.basefee_limit = basefee_limit; + } + if let Some(queued_limit) = queued_limit { + config.queued_limit = queued_limit; + } + if let Some(blob_limit) = blob_limit { + config.blob_limit = blob_limit; + } + if let Some(max_account_slots) = max_account_slots { + config.max_account_slots = max_account_slots; + } + if let Some(minimal_protocol_basefee) = minimal_protocol_basefee { + config.minimal_protocol_basefee = minimal_protocol_basefee; + } + config.local_transactions_config.local_addresses.extend(local_addresses); + + config + } +} diff --git a/crates/node/builder/src/launch/engine.rs b/crates/node/builder/src/launch/engine.rs index e71a0263c52c..46ffacbf717a 100644 --- a/crates/node/builder/src/launch/engine.rs +++ b/crates/node/builder/src/launch/engine.rs @@ -91,7 +91,7 @@ where let NodeBuilderWithComponents { adapter: NodeTypesAdapter { database }, components_builder, - add_ons: AddOns { hooks, rpc, exexs: installed_exex }, + add_ons: AddOns { hooks, rpc, exexs: installed_exex, .. }, config, } = target; let NodeHooks { on_component_initialized, on_node_started, .. } = hooks; diff --git a/crates/node/builder/src/launch/mod.rs b/crates/node/builder/src/launch/mod.rs index db98ffacedeb..3188cde4b157 100644 --- a/crates/node/builder/src/launch/mod.rs +++ b/crates/node/builder/src/launch/mod.rs @@ -126,7 +126,7 @@ where let NodeBuilderWithComponents { adapter: NodeTypesAdapter { database }, components_builder, - add_ons: AddOns { hooks, rpc, exexs: installed_exex }, + add_ons: AddOns { hooks, rpc, exexs: installed_exex, .. }, config, } = target; let NodeHooks { on_component_initialized, on_node_started, .. } = hooks; diff --git a/crates/node/builder/src/node.rs b/crates/node/builder/src/node.rs index 5d047f94c918..3a70c08c1031 100644 --- a/crates/node/builder/src/node.rs +++ b/crates/node/builder/src/node.rs @@ -34,21 +34,29 @@ pub trait Node: NodeTypesWithEngine + Clone { /// Returns a [`NodeComponentsBuilder`] for the node. fn components_builder(&self) -> Self::ComponentsBuilder; + + /// Returns the node add-ons. + fn add_ons(&self) -> Self::AddOns; } /// A [`Node`] type builder #[derive(Clone, Default, Debug)] -pub struct AnyNode(PhantomData<(N, AO)>, C); +pub struct AnyNode(PhantomData, C, AO); -impl AnyNode { +impl AnyNode { /// Configures the types of the node. - pub fn types(self) -> AnyNode { - AnyNode::(PhantomData::<(T, ())>, self.1) + pub fn types(self) -> AnyNode { + AnyNode(PhantomData, self.1, self.2) } /// Sets the node components builder. - pub const fn components_builder(&self, value: T) -> AnyNode { - AnyNode::(PhantomData::<(N, ())>, value) + pub fn components_builder(self, value: T) -> AnyNode { + AnyNode(PhantomData, value, self.2) + } + + /// Sets the node add-ons. + pub fn add_ons(self, value: T) -> AnyNode { + AnyNode(PhantomData, self.1, value) } } @@ -84,6 +92,10 @@ where fn components_builder(&self) -> Self::ComponentsBuilder { self.1.clone() } + + fn add_ons(&self) -> Self::AddOns { + self.2.clone() + } } /// The launched node with all components including RPC handlers. diff --git a/crates/node/builder/src/rpc.rs b/crates/node/builder/src/rpc.rs index 607a33147e94..cb8d8f355dac 100644 --- a/crates/node/builder/src/rpc.rs +++ b/crates/node/builder/src/rpc.rs @@ -254,7 +254,7 @@ pub struct RpcContext<'a, Node: FullNodeComponents, EthApi: EthApiTypes> { pub auth_module: &'a mut AuthRpcModule, } -impl<'a, Node, EthApi> RpcContext<'a, Node, EthApi> +impl RpcContext<'_, Node, EthApi> where Node: FullNodeComponents, EthApi: EthApiTypes, diff --git a/crates/node/core/Cargo.toml b/crates/node/core/Cargo.toml index 35802cf5165a..46476273e42d 100644 --- a/crates/node/core/Cargo.toml +++ b/crates/node/core/Cargo.toml @@ -14,9 +14,7 @@ workspace = true # reth reth-chainspec.workspace = true reth-primitives.workspace = true -reth-cli.workspace = true reth-cli-util.workspace = true -reth-fs-util.workspace = true reth-db = { workspace = true, features = ["mdbx"] } reth-storage-errors.workspace = true reth-storage-api.workspace = true @@ -37,10 +35,8 @@ reth-network-peers.workspace = true reth-consensus-common.workspace = true reth-prune-types.workspace = true reth-stages-types.workspace = true -reth-optimism-chainspec = { workspace = true, optional = true } # ethereum -alloy-genesis.workspace = true alloy-primitives.workspace = true alloy-rpc-types-engine = { workspace = true, features = ["jwt"] } @@ -59,7 +55,6 @@ thiserror.workspace = true # io dirs-next = "2.0.0" shellexpand.workspace = true -serde_json.workspace = true # tracing tracing.workspace = true @@ -85,7 +80,6 @@ optimism = [ "reth-primitives/optimism", "reth-rpc-types-compat/optimism", "reth-rpc-eth-api/optimism", - "dep:reth-optimism-chainspec", ] # Features for vergen to generate correct env vars jemalloc = [] diff --git a/crates/node/core/src/args/mod.rs b/crates/node/core/src/args/mod.rs index 1a647ac65b03..7f1b64361515 100644 --- a/crates/node/core/src/args/mod.rs +++ b/crates/node/core/src/args/mod.rs @@ -56,7 +56,5 @@ pub use datadir_args::DatadirArgs; mod benchmark_args; pub use benchmark_args::BenchmarkArgs; -pub mod utils; - mod error; pub mod types; diff --git a/crates/node/core/src/args/utils.rs b/crates/node/core/src/args/utils.rs deleted file mode 100644 index e6ebda45b7f7..000000000000 --- a/crates/node/core/src/args/utils.rs +++ /dev/null @@ -1,99 +0,0 @@ -//! Clap parser utilities - -use std::{path::PathBuf, sync::Arc}; - -use alloy_genesis::Genesis; -use reth_chainspec::ChainSpec; -#[cfg(not(feature = "optimism"))] -use reth_chainspec::{DEV, HOLESKY, MAINNET, SEPOLIA}; -use reth_cli::chainspec::ChainSpecParser; -use reth_fs_util as fs; -#[cfg(feature = "optimism")] -use reth_optimism_chainspec::{BASE_MAINNET, BASE_SEPOLIA, OP_DEV, OP_MAINNET, OP_SEPOLIA}; - -#[cfg(feature = "optimism")] -/// Chains supported by op-reth. First value should be used as the default. -pub const SUPPORTED_CHAINS: &[&str] = - &["optimism", "optimism-sepolia", "base", "base-sepolia", "dev"]; -#[cfg(not(feature = "optimism"))] -/// Chains supported by reth. First value should be used as the default. -pub const SUPPORTED_CHAINS: &[&str] = &["mainnet", "sepolia", "holesky", "dev"]; - -/// Clap value parser for [`ChainSpec`]s. -/// -/// The value parser matches either a known chain, the path -/// to a json file, or a json formatted string in-memory. The json needs to be a Genesis struct. -#[cfg(not(feature = "optimism"))] -pub fn chain_value_parser(s: &str) -> eyre::Result, eyre::Error> { - Ok(match s { - "mainnet" => MAINNET.clone(), - "sepolia" => SEPOLIA.clone(), - "holesky" => HOLESKY.clone(), - "dev" => DEV.clone(), - _ => Arc::new(parse_custom_chain_spec(s)?), - }) -} - -/// Clap value parser for [`OpChainSpec`](reth_optimism_chainspec::OpChainSpec)s. -/// -/// The value parser matches either a known chain, the path -/// to a json file, or a json formatted string in-memory. The json needs to be a Genesis struct. -#[cfg(feature = "optimism")] -pub fn chain_value_parser(s: &str) -> eyre::Result, eyre::Error> { - Ok(Arc::new(match s { - "optimism" => OP_MAINNET.inner.clone(), - "optimism_sepolia" | "optimism-sepolia" => OP_SEPOLIA.inner.clone(), - "base" => BASE_MAINNET.inner.clone(), - "base_sepolia" | "base-sepolia" => BASE_SEPOLIA.inner.clone(), - "dev" => OP_DEV.inner.clone(), - _ => parse_custom_chain_spec(s)?, - })) -} - -/// Parses a custom [`ChainSpec`]. -pub fn parse_custom_chain_spec(s: &str) -> eyre::Result { - // try to read json from path first - let raw = match fs::read_to_string(PathBuf::from(shellexpand::full(s)?.into_owned())) { - Ok(raw) => raw, - Err(io_err) => { - // valid json may start with "\n", but must contain "{" - if s.contains('{') { - s.to_string() - } else { - return Err(io_err.into()) // assume invalid path - } - } - }; - - // both serialized Genesis and ChainSpec structs supported - let genesis: Genesis = serde_json::from_str(&raw)?; - - Ok(genesis.into()) -} - -/// A chain specification parser for ethereum chains. -#[derive(Debug, Copy, Clone, Default)] -#[non_exhaustive] -pub struct EthereumChainSpecParser; - -impl ChainSpecParser for EthereumChainSpecParser { - type ChainSpec = ChainSpec; - - const SUPPORTED_CHAINS: &'static [&'static str] = SUPPORTED_CHAINS; - - fn parse(s: &str) -> eyre::Result> { - chain_value_parser(s) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn parse_known_chain_spec() { - for chain in SUPPORTED_CHAINS { - chain_value_parser(chain).unwrap(); - } - } -} diff --git a/crates/node/types/src/lib.rs b/crates/node/types/src/lib.rs index 2c72e02d3edc..0f6d5a1a370f 100644 --- a/crates/node/types/src/lib.rs +++ b/crates/node/types/src/lib.rs @@ -40,6 +40,7 @@ pub trait NodeTypes: Send + Sync + Unpin + 'static { pub trait NodeTypesWithEngine: NodeTypes { /// The node's engine types, defining the interaction with the consensus engine. type Engine: EngineTypes; + // type Engine: EngineTypes; } /// A helper trait that is downstream of the [`NodeTypesWithEngine`] trait and adds database to the diff --git a/crates/optimism/bin/Cargo.toml b/crates/optimism/bin/Cargo.toml index 13e7b1aa6d4d..2de0bb6ee181 100644 --- a/crates/optimism/bin/Cargo.toml +++ b/crates/optimism/bin/Cargo.toml @@ -15,6 +15,12 @@ reth-optimism-cli.workspace = true reth-provider.workspace = true reth-optimism-rpc.workspace = true reth-optimism-node.workspace = true +reth-optimism-chainspec.workspace = true +reth-optimism-consensus.workspace = true +reth-optimism-evm.workspace = true +reth-optimism-payload-builder.workspace = true +reth-optimism-primitives.workspace = true +reth-optimism-forks.workspace = true clap = { workspace = true, features = ["derive", "env"] } tracing.workspace = true @@ -25,7 +31,7 @@ workspace = true [features] default = ["jemalloc"] -jemalloc = ["reth-cli-util/jemalloc"] +jemalloc = ["reth-cli-util/jemalloc", "reth-optimism-cli/jemalloc"] jemalloc-prof = ["reth-cli-util/jemalloc-prof"] tracy-allocator = ["reth-cli-util/tracy-allocator"] diff --git a/crates/optimism/bin/src/lib.rs b/crates/optimism/bin/src/lib.rs new file mode 100644 index 000000000000..21c28f7c5470 --- /dev/null +++ b/crates/optimism/bin/src/lib.rs @@ -0,0 +1,73 @@ +//! Rust Optimism (op-reth) binary executable. +//! +//! ## Feature Flags +//! +//! - `jemalloc`: Uses [jemallocator](https://github.com/tikv/jemallocator) as the global allocator. +//! This is **not recommended on Windows**. See [here](https://rust-lang.github.io/rfcs/1974-global-allocators.html#jemalloc) +//! for more info. +//! - `jemalloc-prof`: Enables [jemallocator's](https://github.com/tikv/jemallocator) heap profiling +//! and leak detection functionality. See [jemalloc's opt.prof](https://jemalloc.net/jemalloc.3.html#opt.prof) +//! documentation for usage details. This is **not recommended on Windows**. See [here](https://rust-lang.github.io/rfcs/1974-global-allocators.html#jemalloc) +//! for more info. +//! - `asm-keccak`: replaces the default, pure-Rust implementation of Keccak256 with one implemented +//! in assembly; see [the `keccak-asm` crate](https://github.com/DaniPopes/keccak-asm) for more +//! details and supported targets +//! - `min-error-logs`: Disables all logs below `error` level. +//! - `min-warn-logs`: Disables all logs below `warn` level. +//! - `min-info-logs`: Disables all logs below `info` level. This can speed up the node, since fewer +//! calls to the logging component is made. +//! - `min-debug-logs`: Disables all logs below `debug` level. +//! - `min-trace-logs`: Disables all logs below `trace` level. +#![doc( + html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png", + html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", + issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" +)] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +// The `optimism` feature must be enabled to use this crate. +#![cfg(feature = "optimism")] + +/// Re-exported from `reth_optimism_cli`. +pub mod cli { + pub use reth_optimism_cli::*; +} + +/// Re-exported from `reth_optimism_chainspec`. +pub mod chainspec { + pub use reth_optimism_chainspec::*; +} + +/// Re-exported from `reth_optimism_consensus`. +pub mod consensus { + pub use reth_optimism_consensus::*; +} + +/// Re-exported from `reth_optimism_evm`. +pub mod evm { + pub use reth_optimism_evm::*; +} + +/// Re-exported from `reth_optimism_forks`. +pub mod forks { + pub use reth_optimism_forks::*; +} + +/// Re-exported from `reth_optimism_node`. +pub mod node { + pub use reth_optimism_node::*; +} + +/// Re-exported from `reth_optimism_payload_builder`. +pub mod payload { + pub use reth_optimism_payload_builder::*; +} + +/// Re-exported from `reth_optimism_primitives`. +pub mod primitives { + pub use reth_optimism_primitives::*; +} + +/// Re-exported from `reth_optimism_rpc`. +pub mod rpc { + pub use reth_optimism_rpc::*; +} diff --git a/crates/optimism/bin/src/main.rs b/crates/optimism/bin/src/main.rs index cecd01ea8897..6822a6a50ec4 100644 --- a/crates/optimism/bin/src/main.rs +++ b/crates/optimism/bin/src/main.rs @@ -1,7 +1,6 @@ #![allow(missing_docs, rustdoc::missing_crate_level_docs)] // The `optimism` feature must be enabled to use this crate. #![cfg(feature = "optimism")] -#![cfg_attr(not(test), warn(unused_crate_dependencies))] use clap::Parser; use reth_node_builder::{engine_tree_config::TreeConfig, EngineNodeLauncher}; @@ -35,7 +34,7 @@ fn main() { let handle = builder .with_types_and_provider::>() .with_components(OptimismNode::components(rollup_args)) - .with_add_ons::() + .with_add_ons(OptimismAddOns::new(sequencer_http_arg.clone())) .extend_rpc_modules(move |ctx| { // register sequencer tx forwarder if let Some(sequencer_http) = sequencer_http_arg { diff --git a/crates/optimism/chainspec/Cargo.toml b/crates/optimism/chainspec/Cargo.toml index 41b54902c201..e13b95056c7f 100644 --- a/crates/optimism/chainspec/Cargo.toml +++ b/crates/optimism/chainspec/Cargo.toml @@ -26,12 +26,15 @@ alloy-chains.workspace = true alloy-genesis.workspace = true alloy-primitives.workspace = true +# op +op-alloy-rpc-types.workspace = true + # io serde_json.workspace = true # misc -once_cell.workspace = true derive_more.workspace = true +once_cell.workspace = true [dev-dependencies] reth-chainspec = { workspace = true, features = ["test-utils"] } diff --git a/crates/optimism/chainspec/src/lib.rs b/crates/optimism/chainspec/src/lib.rs index 2668c374500d..22316a234d9b 100644 --- a/crates/optimism/chainspec/src/lib.rs +++ b/crates/optimism/chainspec/src/lib.rs @@ -27,10 +27,12 @@ pub use op::OP_MAINNET; pub use op_sepolia::OP_SEPOLIA; use derive_more::{Constructor, Deref, Into}; +use once_cell::sync::OnceCell; use reth_chainspec::{ - BaseFeeParams, ChainSpec, DepositContract, EthChainSpec, EthereumHardforks, ForkFilter, ForkId, - Hardforks, Head, + BaseFeeParams, BaseFeeParamsKind, ChainSpec, DepositContract, EthChainSpec, EthereumHardforks, + ForkFilter, ForkId, Hardforks, Head, }; +use reth_ethereum_forks::{ChainHardforks, EthereumHardfork, ForkCondition}; use reth_network_peers::NodeRecord; use reth_primitives_traits::Header; @@ -52,14 +54,14 @@ impl EthChainSpec for OpChainSpec { self.inner.chain() } - fn base_fee_params_at_timestamp(&self, timestamp: u64) -> BaseFeeParams { - self.inner.base_fee_params_at_timestamp(timestamp) - } - fn base_fee_params_at_block(&self, block_number: u64) -> BaseFeeParams { self.inner.base_fee_params_at_block(block_number) } + fn base_fee_params_at_timestamp(&self, timestamp: u64) -> BaseFeeParams { + self.inner.base_fee_params_at_timestamp(timestamp) + } + fn deposit_contract(&self) -> Option<&DepositContract> { self.inner.deposit_contract() } @@ -118,20 +120,161 @@ impl Hardforks for OpChainSpec { } impl EthereumHardforks for OpChainSpec { + fn get_final_paris_total_difficulty(&self) -> Option { + self.inner.get_final_paris_total_difficulty() + } + fn final_paris_total_difficulty(&self, block_number: u64) -> Option { self.inner.final_paris_total_difficulty(block_number) } +} - fn get_final_paris_total_difficulty(&self) -> Option { - self.inner.get_final_paris_total_difficulty() +impl From for OpChainSpec { + fn from(genesis: Genesis) -> Self { + use reth_optimism_forks::OptimismHardfork; + let optimism_genesis_info = OptimismGenesisInfo::extract_from(&genesis); + let genesis_info = + optimism_genesis_info.optimism_chain_info.genesis_info.unwrap_or_default(); + + // Block-based hardforks + let hardfork_opts = [ + (EthereumHardfork::Homestead.boxed(), genesis.config.homestead_block), + (EthereumHardfork::Tangerine.boxed(), genesis.config.eip150_block), + (EthereumHardfork::SpuriousDragon.boxed(), genesis.config.eip155_block), + (EthereumHardfork::Byzantium.boxed(), genesis.config.byzantium_block), + (EthereumHardfork::Constantinople.boxed(), genesis.config.constantinople_block), + (EthereumHardfork::Petersburg.boxed(), genesis.config.petersburg_block), + (EthereumHardfork::Istanbul.boxed(), genesis.config.istanbul_block), + (EthereumHardfork::MuirGlacier.boxed(), genesis.config.muir_glacier_block), + (EthereumHardfork::Berlin.boxed(), genesis.config.berlin_block), + (EthereumHardfork::London.boxed(), genesis.config.london_block), + (EthereumHardfork::ArrowGlacier.boxed(), genesis.config.arrow_glacier_block), + (EthereumHardfork::GrayGlacier.boxed(), genesis.config.gray_glacier_block), + (OptimismHardfork::Bedrock.boxed(), genesis_info.bedrock_block), + ]; + let mut block_hardforks = hardfork_opts + .into_iter() + .filter_map(|(hardfork, opt)| opt.map(|block| (hardfork, ForkCondition::Block(block)))) + .collect::>(); + + // Paris + let paris_block_and_final_difficulty = + if let Some(ttd) = genesis.config.terminal_total_difficulty { + block_hardforks.push(( + EthereumHardfork::Paris.boxed(), + ForkCondition::TTD { + total_difficulty: ttd, + fork_block: genesis.config.merge_netsplit_block, + }, + )); + + genesis.config.merge_netsplit_block.map(|block| (block, ttd)) + } else { + None + }; + + // Time-based hardforks + let time_hardfork_opts = [ + (EthereumHardfork::Shanghai.boxed(), genesis.config.shanghai_time), + (EthereumHardfork::Cancun.boxed(), genesis.config.cancun_time), + (EthereumHardfork::Prague.boxed(), genesis.config.prague_time), + (OptimismHardfork::Regolith.boxed(), genesis_info.regolith_time), + (OptimismHardfork::Canyon.boxed(), genesis_info.canyon_time), + (OptimismHardfork::Ecotone.boxed(), genesis_info.ecotone_time), + (OptimismHardfork::Fjord.boxed(), genesis_info.fjord_time), + (OptimismHardfork::Granite.boxed(), genesis_info.granite_time), + ]; + + let mut time_hardforks = time_hardfork_opts + .into_iter() + .filter_map(|(hardfork, opt)| { + opt.map(|time| (hardfork, ForkCondition::Timestamp(time))) + }) + .collect::>(); + + block_hardforks.append(&mut time_hardforks); + + // Ordered Hardforks + let mainnet_hardforks = OptimismHardfork::op_mainnet(); + let mainnet_order = mainnet_hardforks.forks_iter(); + + let mut ordered_hardforks = Vec::with_capacity(block_hardforks.len()); + for (hardfork, _) in mainnet_order { + if let Some(pos) = block_hardforks.iter().position(|(e, _)| **e == *hardfork) { + ordered_hardforks.push(block_hardforks.remove(pos)); + } + } + + // append the remaining unknown hardforks to ensure we don't filter any out + ordered_hardforks.append(&mut block_hardforks); + + Self { + inner: ChainSpec { + chain: genesis.config.chain_id.into(), + genesis, + genesis_hash: OnceCell::new(), + hardforks: ChainHardforks::new(ordered_hardforks), + paris_block_and_final_difficulty, + base_fee_params: optimism_genesis_info.base_fee_params, + ..Default::default() + }, + } + } +} + +#[derive(Default, Debug)] +struct OptimismGenesisInfo { + optimism_chain_info: op_alloy_rpc_types::genesis::OptimismChainInfo, + base_fee_params: BaseFeeParamsKind, +} + +impl OptimismGenesisInfo { + fn extract_from(genesis: &Genesis) -> Self { + let mut info = Self { + optimism_chain_info: op_alloy_rpc_types::genesis::OptimismChainInfo::extract_from( + &genesis.config.extra_fields, + ) + .unwrap_or_default(), + ..Default::default() + }; + if let Some(optimism_base_fee_info) = &info.optimism_chain_info.base_fee_info { + if let (Some(elasticity), Some(denominator)) = ( + optimism_base_fee_info.eip1559_elasticity, + optimism_base_fee_info.eip1559_denominator, + ) { + let base_fee_params = if let Some(canyon_denominator) = + optimism_base_fee_info.eip1559_denominator_canyon + { + BaseFeeParamsKind::Variable( + vec![ + ( + EthereumHardfork::London.boxed(), + BaseFeeParams::new(denominator as u128, elasticity as u128), + ), + ( + reth_optimism_forks::OptimismHardfork::Canyon.boxed(), + BaseFeeParams::new(canyon_denominator as u128, elasticity as u128), + ), + ] + .into(), + ) + } else { + BaseFeeParams::new(denominator as u128, elasticity as u128).into() + }; + + info.base_fee_params = base_fee_params; + } + } + + info } } #[cfg(test)] mod tests { - use alloy_genesis::Genesis; + use alloy_genesis::{ChainConfig, Genesis}; use alloy_primitives::b256; - use reth_chainspec::{test_fork_ids, BaseFeeParams, BaseFeeParamsKind, ChainSpec}; + use reth_chainspec::{test_fork_ids, BaseFeeParams, BaseFeeParamsKind}; use reth_ethereum_forks::{EthereumHardfork, ForkCondition, ForkHash, ForkId, Head}; use reth_optimism_forks::{OptimismHardfork, OptimismHardforks}; @@ -383,7 +526,7 @@ mod tests { }) ); - let chain_spec: ChainSpec = genesis.into(); + let chain_spec: OpChainSpec = genesis.into(); assert_eq!( chain_spec.base_fee_params, @@ -449,7 +592,7 @@ mod tests { }) ); - let chain_spec: ChainSpec = genesis.into(); + let chain_spec: OpChainSpec = genesis.into(); assert_eq!( chain_spec.base_fee_params, @@ -511,7 +654,7 @@ mod tests { } "#; let genesis: Genesis = serde_json::from_str(geth_genesis).unwrap(); - let chainspec = ChainSpec::from(genesis.clone()); + let chainspec = OpChainSpec::from(genesis.clone()); let actual_chain_id = genesis.config.chain_id; assert_eq!(actual_chain_id, 8453); @@ -552,4 +695,79 @@ mod tests { assert!(chainspec.is_fork_active_at_timestamp(OptimismHardfork::Regolith, 20)); } + + #[test] + fn test_fork_order_optimism_mainnet() { + use reth_optimism_forks::OptimismHardfork; + + let genesis = Genesis { + config: ChainConfig { + chain_id: 0, + homestead_block: Some(0), + dao_fork_block: Some(0), + dao_fork_support: false, + eip150_block: Some(0), + eip155_block: Some(0), + eip158_block: Some(0), + byzantium_block: Some(0), + constantinople_block: Some(0), + petersburg_block: Some(0), + istanbul_block: Some(0), + muir_glacier_block: Some(0), + berlin_block: Some(0), + london_block: Some(0), + arrow_glacier_block: Some(0), + gray_glacier_block: Some(0), + merge_netsplit_block: Some(0), + shanghai_time: Some(0), + cancun_time: Some(0), + terminal_total_difficulty: Some(U256::ZERO), + extra_fields: [ + (String::from("bedrockBlock"), 0.into()), + (String::from("regolithTime"), 0.into()), + (String::from("canyonTime"), 0.into()), + (String::from("ecotoneTime"), 0.into()), + (String::from("fjordTime"), 0.into()), + (String::from("graniteTime"), 0.into()), + ] + .into_iter() + .collect(), + ..Default::default() + }, + ..Default::default() + }; + + let chain_spec: OpChainSpec = genesis.into(); + + let hardforks: Vec<_> = chain_spec.hardforks.forks_iter().map(|(h, _)| h).collect(); + let expected_hardforks = vec![ + EthereumHardfork::Homestead.boxed(), + EthereumHardfork::Tangerine.boxed(), + EthereumHardfork::SpuriousDragon.boxed(), + EthereumHardfork::Byzantium.boxed(), + EthereumHardfork::Constantinople.boxed(), + EthereumHardfork::Petersburg.boxed(), + EthereumHardfork::Istanbul.boxed(), + EthereumHardfork::MuirGlacier.boxed(), + EthereumHardfork::Berlin.boxed(), + EthereumHardfork::London.boxed(), + EthereumHardfork::ArrowGlacier.boxed(), + EthereumHardfork::GrayGlacier.boxed(), + EthereumHardfork::Paris.boxed(), + OptimismHardfork::Bedrock.boxed(), + OptimismHardfork::Regolith.boxed(), + EthereumHardfork::Shanghai.boxed(), + OptimismHardfork::Canyon.boxed(), + EthereumHardfork::Cancun.boxed(), + OptimismHardfork::Ecotone.boxed(), + OptimismHardfork::Fjord.boxed(), + OptimismHardfork::Granite.boxed(), + ]; + + assert!(expected_hardforks + .iter() + .zip(hardforks.iter()) + .all(|(expected, actual)| &**expected == *actual)); + assert_eq!(expected_hardforks.len(), hardforks.len()); + } } diff --git a/crates/optimism/cli/Cargo.toml b/crates/optimism/cli/Cargo.toml index 99d1641e3643..d53270cd62f8 100644 --- a/crates/optimism/cli/Cargo.toml +++ b/crates/optimism/cli/Cargo.toml @@ -27,6 +27,9 @@ reth-node-core.workspace = true reth-optimism-node.workspace = true reth-primitives.workspace = true +# so jemalloc metrics can be included +reth-node-metrics.workspace = true + ## optimism reth-optimism-primitives.workspace = true reth-optimism-chainspec.workspace = true @@ -82,3 +85,9 @@ asm-keccak = [ "reth-optimism-node/asm-keccak", "reth-primitives/asm-keccak", ] + +# Jemalloc feature for vergen to generate correct env vars +jemalloc = [ + "reth-node-core/jemalloc", + "reth-node-metrics/jemalloc" +] diff --git a/crates/optimism/cli/src/chainspec.rs b/crates/optimism/cli/src/chainspec.rs index e76bfd5f0656..329669ab8c9c 100644 --- a/crates/optimism/cli/src/chainspec.rs +++ b/crates/optimism/cli/src/chainspec.rs @@ -1,28 +1,12 @@ -use std::sync::Arc; - -use reth_cli::chainspec::ChainSpecParser; -use reth_node_core::args::utils::parse_custom_chain_spec; +use reth_cli::chainspec::{parse_genesis, ChainSpecParser}; use reth_optimism_chainspec::{ OpChainSpec, BASE_MAINNET, BASE_SEPOLIA, OP_DEV, OP_MAINNET, OP_SEPOLIA, }; - -/// Clap value parser for [`OpChainSpec`]s. -/// -/// The value parser matches either a known chain, the path -/// to a json file, or a json formatted string in-memory. The json needs to be a Genesis struct. -fn chain_value_parser(s: &str) -> eyre::Result, eyre::Error> { - Ok(match s { - "dev" => OP_DEV.clone(), - "optimism" => OP_MAINNET.clone(), - "optimism_sepolia" | "optimism-sepolia" => OP_SEPOLIA.clone(), - "base" => BASE_MAINNET.clone(), - "base_sepolia" | "base-sepolia" => BASE_SEPOLIA.clone(), - _ => Arc::new(OpChainSpec { inner: parse_custom_chain_spec(s)? }), - }) -} +use std::sync::Arc; /// Optimism chain specification parser. #[derive(Debug, Clone, Default)] +#[non_exhaustive] pub struct OpChainSpecParser; impl ChainSpecParser for OpChainSpecParser { @@ -43,6 +27,21 @@ impl ChainSpecParser for OpChainSpecParser { } } +/// Clap value parser for [`OpChainSpec`]s. +/// +/// The value parser matches either a known chain, the path +/// to a json file, or a json formatted string in-memory. The json needs to be a Genesis struct. +pub fn chain_value_parser(s: &str) -> eyre::Result, eyre::Error> { + Ok(match s { + "dev" => OP_DEV.clone(), + "optimism" => OP_MAINNET.clone(), + "optimism_sepolia" | "optimism-sepolia" => OP_SEPOLIA.clone(), + "base" => BASE_MAINNET.clone(), + "base_sepolia" | "base-sepolia" => BASE_SEPOLIA.clone(), + _ => Arc::new(parse_genesis(s)?.into()), + }) +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/optimism/cli/src/lib.rs b/crates/optimism/cli/src/lib.rs index b17cefa63287..e6eed86bf7fc 100644 --- a/crates/optimism/cli/src/lib.rs +++ b/crates/optimism/cli/src/lib.rs @@ -51,6 +51,10 @@ use reth_optimism_node::OptimismNode; use reth_tracing::FileWorkerGuard; use tracing::info; +// This allows us to manually enable node metrics features, required for proper jemalloc metric +// reporting +use reth_node_metrics as _; + /// The main op-reth cli interface. /// /// This is the entrypoint to the executable. @@ -176,13 +180,14 @@ where #[cfg(test)] mod test { + use crate::chainspec::OpChainSpecParser; use clap::Parser; - use reth_cli_commands::NodeCommand; + use reth_cli_commands::{node::NoArgs, NodeCommand}; use reth_optimism_chainspec::OP_DEV; #[test] fn parse_dev() { - let cmd: NodeCommand = NodeCommand::parse_from(["op-reth", "--dev"]); + let cmd = NodeCommand::::parse_from(["op-reth", "--dev"]); let chain = OP_DEV.clone(); assert_eq!(cmd.chain.chain, chain.chain); assert_eq!(cmd.chain.genesis_hash, chain.genesis_hash); diff --git a/crates/optimism/evm/src/execute.rs b/crates/optimism/evm/src/execute.rs index 6ca9cec5260e..4e5918831f76 100644 --- a/crates/optimism/evm/src/execute.rs +++ b/crates/optimism/evm/src/execute.rs @@ -10,7 +10,7 @@ use reth_evm::{ BatchExecutor, BlockExecutionError, BlockExecutionInput, BlockExecutionOutput, BlockExecutorProvider, BlockValidationError, Executor, ProviderError, }, - system_calls::SystemCaller, + system_calls::{NoopHook, OnStateHook, SystemCaller}, ConfigureEvm, }; use reth_execution_types::ExecutionOutcome; @@ -108,18 +108,23 @@ where /// /// This applies the pre-execution changes, and executes the transactions. /// + /// The optional `state_hook` will be executed with the state changes if present. + /// /// # Note /// /// It does __not__ apply post-execution changes. - fn execute_pre_and_transactions( + fn execute_pre_and_transactions( &self, block: &BlockWithSenders, mut evm: Evm<'_, Ext, &mut State>, + state_hook: Option, ) -> Result<(Vec, u64), BlockExecutionError> where DB: Database + Display>, + F: OnStateHook, { - let mut system_caller = SystemCaller::new(&self.evm_config, &self.chain_spec); + let mut system_caller = + SystemCaller::new(&self.evm_config, &self.chain_spec).with_state_hook(state_hook); // apply pre execution changes system_caller.apply_beacon_root_contract_call( @@ -178,7 +183,7 @@ where self.evm_config.fill_tx_env(evm.tx_mut(), transaction, *sender); // Execute transaction. - let ResultAndState { result, state } = evm.transact().map_err(move |err| { + let result_and_state = evm.transact().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 { @@ -192,7 +197,8 @@ where ?transaction, "Executed transaction" ); - + system_caller.on_state(&result_and_state); + let ResultAndState { result, state } = result_and_state; evm.db_mut().commit(state); // append gas used @@ -278,16 +284,30 @@ where EnvWithHandlerCfg::new_with_cfg_env(cfg, block_env, Default::default()) } + /// Convenience method to invoke `execute_without_verification_with_state_hook` setting the + /// state hook as `None`. + fn execute_without_verification( + &mut self, + block: &BlockWithSenders, + total_difficulty: U256, + ) -> Result<(Vec, u64), BlockExecutionError> { + self.execute_without_verification_with_state_hook(block, total_difficulty, None::) + } + /// Execute a single block and apply the state changes to the internal state. /// /// Returns the receipts of the transactions in the block and the total gas used. /// /// Returns an error if execution fails. - fn execute_without_verification( + fn execute_without_verification_with_state_hook( &mut self, block: &BlockWithSenders, total_difficulty: U256, - ) -> Result<(Vec, u64), BlockExecutionError> { + state_hook: Option, + ) -> Result<(Vec, u64), BlockExecutionError> + where + F: OnStateHook, + { // 1. prepare state on new block self.on_new_block(&block.header); @@ -296,7 +316,7 @@ where let (receipts, gas_used) = { let evm = self.executor.evm_config.evm_with_env(&mut self.state, env); - self.executor.execute_pre_and_transactions(block, evm) + self.executor.execute_pre_and_transactions(block, evm, state_hook) }?; // 3. apply post execution changes @@ -383,6 +403,32 @@ where gas_used, }) } + + fn execute_with_state_hook( + mut self, + input: Self::Input<'_>, + state_hook: F, + ) -> Result + where + F: OnStateHook, + { + let BlockExecutionInput { block, total_difficulty } = input; + let (receipts, gas_used) = self.execute_without_verification_with_state_hook( + block, + total_difficulty, + Some(state_hook), + )?; + + // NOTE: we need to merge keep the reverts for the bundle retention + self.state.merge_transitions(BundleRetention::Reverts); + + Ok(BlockExecutionOutput { + state: self.state.take_bundle(), + receipts, + requests: vec![], + gas_used, + }) + } } /// An executor for a batch of blocks. diff --git a/crates/optimism/node/src/node.rs b/crates/optimism/node/src/node.rs index d82cb70a3ffe..caeab3741a2e 100644 --- a/crates/optimism/node/src/node.rs +++ b/crates/optimism/node/src/node.rs @@ -3,13 +3,14 @@ use std::sync::Arc; use reth_basic_payload_builder::{BasicPayloadJobGenerator, BasicPayloadJobGeneratorConfig}; +use reth_chainspec::{EthChainSpec, Hardforks}; use reth_evm::ConfigureEvm; -use reth_network::{NetworkHandle, NetworkManager}; +use reth_network::{NetworkConfig, NetworkHandle, NetworkManager}; use reth_node_api::{EngineValidator, FullNodeComponents, NodeAddOns}; use reth_node_builder::{ components::{ ComponentsBuilder, ConsensusBuilder, EngineValidatorBuilder, ExecutorBuilder, - NetworkBuilder, PayloadServiceBuilder, PoolBuilder, + NetworkBuilder, PayloadServiceBuilder, PoolBuilder, PoolBuilderConfigOverrides, }, node::{FullNodeTypes, NodeTypes, NodeTypesWithEngine}, BuilderContext, Node, PayloadBuilderConfig, @@ -102,6 +103,10 @@ where let Self { args } = self; Self::components(args.clone()) } + + fn add_ons(&self) -> Self::AddOns { + OptimismAddOns::new(self.args.sequencer_http.clone()) + } } impl NodeTypes for OptimismNode { @@ -115,7 +120,21 @@ impl NodeTypesWithEngine for OptimismNode { /// Add-ons w.r.t. optimism. #[derive(Debug, Clone)] -pub struct OptimismAddOns; +pub struct OptimismAddOns { + sequencer_http: Option, +} + +impl OptimismAddOns { + /// Create a new instance with the given `sequencer_http` URL. + pub const fn new(sequencer_http: Option) -> Self { + Self { sequencer_http } + } + + /// Returns the sequencer HTTP URL. + pub fn sequencer_http(&self) -> Option<&str> { + self.sequencer_http.as_deref() + } +} impl NodeAddOns for OptimismAddOns { type EthApi = OpEthApi; @@ -148,9 +167,11 @@ where /// /// This contains various settings that can be configured and take precedence over the node's /// config. -#[derive(Debug, Default, Clone, Copy)] -#[non_exhaustive] -pub struct OptimismPoolBuilder; +#[derive(Debug, Default, Clone)] +pub struct OptimismPoolBuilder { + /// Enforced overrides that are applied to the pool config. + pub pool_config_overrides: PoolBuilderConfigOverrides, +} impl PoolBuilder for OptimismPoolBuilder where @@ -159,6 +180,7 @@ where type Pool = OpTransactionPool; async fn build_pool(self, ctx: &BuilderContext) -> eyre::Result { + let Self { pool_config_overrides } = self; let data_dir = ctx.config().datadir(); let blob_store = DiskFileBlobStore::open(data_dir.blobstore(), Default::default())?; @@ -180,7 +202,7 @@ where validator, CoinbaseTipOrdering::default(), blob_store, - ctx.pool_config(), + pool_config_overrides.apply(ctx.pool_config()), ); info!(target: "reth::cli", "Transaction pool initialized"); let transactions_path = data_dir.txpool_transactions(); @@ -309,18 +331,18 @@ pub struct OptimismNetworkBuilder { pub disable_discovery_v4: bool, } -impl NetworkBuilder for OptimismNetworkBuilder -where - Node: FullNodeTypes>, - Pool: TransactionPool + Unpin + 'static, -{ - async fn build_network( - self, +impl OptimismNetworkBuilder { + /// Returns the [`NetworkConfig`] that contains the settings to launch the p2p network. + /// + /// This applies the configured [`OptimismNetworkBuilder`] settings. + pub fn network_config( + &self, ctx: &BuilderContext, - pool: Pool, - ) -> eyre::Result { - let Self { disable_txpool_gossip, disable_discovery_v4 } = self; - + ) -> eyre::Result::Provider>> + where + Node: FullNodeTypes>, + { + let Self { disable_txpool_gossip, disable_discovery_v4 } = self.clone(); let args = &ctx.config().network; let network_builder = ctx .network_config_builder()? @@ -353,8 +375,22 @@ where // gossip to prevent other parties in the network from learning about them. network_config.tx_gossip_disabled = disable_txpool_gossip; - let network = NetworkManager::builder(network_config).await?; + Ok(network_config) + } +} +impl NetworkBuilder for OptimismNetworkBuilder +where + Node: FullNodeTypes>, + Pool: TransactionPool + Unpin + 'static, +{ + async fn build_network( + self, + ctx: &BuilderContext, + pool: Pool, + ) -> eyre::Result { + let network_config = self.network_config(ctx)?; + let network = NetworkManager::builder(network_config).await?; let handle = ctx.start_network(network, pool); Ok(handle) diff --git a/crates/optimism/node/src/txpool.rs b/crates/optimism/node/src/txpool.rs index 811c37e91cb5..7ed2a161d0e4 100644 --- a/crates/optimism/node/src/txpool.rs +++ b/crates/optimism/node/src/txpool.rs @@ -140,7 +140,7 @@ where let l1_block_info = self.block_info.l1_block_info.read().clone(); let mut encoded = Vec::with_capacity(valid_tx.transaction().encoded_length()); - valid_tx.transaction().clone().into_consensus().encode_2718(&mut encoded); + valid_tx.transaction().clone().into_consensus().into().encode_2718(&mut encoded); let cost_addition = match l1_block_info.l1_tx_data_fee( &self.chain_spec(), diff --git a/crates/optimism/node/tests/it/builder.rs b/crates/optimism/node/tests/it/builder.rs index 20363828e861..f1dde4c2c0a8 100644 --- a/crates/optimism/node/tests/it/builder.rs +++ b/crates/optimism/node/tests/it/builder.rs @@ -15,7 +15,7 @@ fn test_basic_setup() { .with_database(db) .with_types::() .with_components(OptimismNode::components(Default::default())) - .with_add_ons::() + .with_add_ons(OptimismAddOns::new(None)) .on_component_initialized(move |ctx| { let _provider = ctx.provider(); Ok(()) diff --git a/crates/optimism/payload/src/payload.rs b/crates/optimism/payload/src/payload.rs index cb3b939136f3..f1ba24435092 100644 --- a/crates/optimism/payload/src/payload.rs +++ b/crates/optimism/payload/src/payload.rs @@ -183,7 +183,7 @@ impl BuiltPayload for OptimismBuiltPayload { } } -impl<'a> BuiltPayload for &'a OptimismBuiltPayload { +impl BuiltPayload for &OptimismBuiltPayload { fn block(&self) -> &SealedBlock { (**self).block() } diff --git a/crates/optimism/rpc/src/eth/transaction.rs b/crates/optimism/rpc/src/eth/transaction.rs index 2556c895783e..3ccda419cad8 100644 --- a/crates/optimism/rpc/src/eth/transaction.rs +++ b/crates/optimism/rpc/src/eth/transaction.rs @@ -34,7 +34,8 @@ where /// Returns the hash of the transaction. async fn send_raw_transaction(&self, tx: Bytes) -> Result { let recovered = recover_raw_transaction(tx.clone())?; - let pool_transaction = ::Transaction::from_pooled(recovered); + let pool_transaction = + ::Transaction::from_pooled(recovered.into()); // On optimism, transactions are forwarded directly to the sequencer to be included in // blocks that it builds. diff --git a/crates/payload/builder/src/database.rs b/crates/payload/builder/src/database.rs index ea1ae0854348..d63f7322dee2 100644 --- a/crates/payload/builder/src/database.rs +++ b/crates/payload/builder/src/database.rs @@ -67,7 +67,7 @@ pub struct CachedReadsDbMut<'a, DB> { pub db: DB, } -impl<'a, DB: DatabaseRef> Database for CachedReadsDbMut<'a, DB> { +impl Database for CachedReadsDbMut<'_, DB> { type Error = ::Error; fn basic(&mut self, address: Address) -> Result, Self::Error> { @@ -130,7 +130,7 @@ pub struct CachedReadsDBRef<'a, DB> { pub inner: RefCell>, } -impl<'a, DB: DatabaseRef> DatabaseRef for CachedReadsDBRef<'a, DB> { +impl DatabaseRef for CachedReadsDBRef<'_, DB> { type Error = ::Error; fn basic_ref(&self, address: Address) -> Result, Self::Error> { diff --git a/crates/primitives-traits/src/header/sealed.rs b/crates/primitives-traits/src/header/sealed.rs index f2047e079c8d..7119a37e742a 100644 --- a/crates/primitives-traits/src/header/sealed.rs +++ b/crates/primitives-traits/src/header/sealed.rs @@ -14,22 +14,24 @@ use serde::{Deserialize, Serialize}; /// to modify header. #[derive(Debug, Clone, PartialEq, Eq, Hash, AsRef, Deref, Serialize, Deserialize)] #[add_arbitrary_tests(rlp)] -pub struct SealedHeader { +pub struct SealedHeader { /// Locked Header hash. hash: BlockHash, /// Locked Header fields. #[as_ref] #[deref] - header: Header, + header: H, } -impl SealedHeader { +impl SealedHeader { /// Creates the sealed header with the corresponding block hash. #[inline] - pub const fn new(header: Header, hash: BlockHash) -> Self { + pub const fn new(header: H, hash: BlockHash) -> Self { Self { header, hash } } +} +impl SealedHeader { /// Returns the sealed Header fields. #[inline] pub const fn header(&self) -> &Header { @@ -182,7 +184,7 @@ pub(super) mod serde_bincode_compat { } } - impl<'a> SerializeAs for SealedHeader<'a> { + impl SerializeAs for SealedHeader<'_> { fn serialize_as(source: &super::SealedHeader, serializer: S) -> Result where S: Serializer, diff --git a/crates/primitives/src/block.rs b/crates/primitives/src/block.rs index 0464c28dee0f..de0817fb025d 100644 --- a/crates/primitives/src/block.rs +++ b/crates/primitives/src/block.rs @@ -756,7 +756,7 @@ pub(super) mod serde_bincode_compat { } } - impl<'a> SerializeAs for BlockBody<'a> { + impl SerializeAs for BlockBody<'_> { fn serialize_as(source: &super::BlockBody, serializer: S) -> Result where S: Serializer, @@ -807,7 +807,7 @@ pub(super) mod serde_bincode_compat { } } - impl<'a> SerializeAs for SealedBlock<'a> { + impl SerializeAs for SealedBlock<'_> { fn serialize_as(source: &super::SealedBlock, serializer: S) -> Result where S: Serializer, @@ -858,7 +858,7 @@ pub(super) mod serde_bincode_compat { } } - impl<'a> SerializeAs for SealedBlockWithSenders<'a> { + impl SerializeAs for SealedBlockWithSenders<'_> { fn serialize_as( source: &super::SealedBlockWithSenders, serializer: S, @@ -1130,4 +1130,13 @@ mod tests { Some(SealedBlockWithSenders { block: sealed, senders: vec![sender] }) ); } + + #[test] + fn test_default_seal() { + let block = SealedBlock::default(); + let sealed = block.hash(); + let block = block.unseal(); + let block = block.seal_slow(); + assert_eq!(sealed, block.hash()); + } } diff --git a/crates/primitives/src/receipt.rs b/crates/primitives/src/receipt.rs index 5c794be5061e..cfd831ed0f74 100644 --- a/crates/primitives/src/receipt.rs +++ b/crates/primitives/src/receipt.rs @@ -373,7 +373,7 @@ impl<'a> ReceiptWithBloomRef<'a> { } } -impl<'a> Encodable for ReceiptWithBloomRef<'a> { +impl Encodable for ReceiptWithBloomRef<'_> { fn encode(&self, out: &mut dyn BufMut) { self.as_encoder().encode_inner(out, true) } @@ -394,7 +394,7 @@ struct ReceiptWithBloomEncoder<'a> { receipt: &'a Receipt, } -impl<'a> ReceiptWithBloomEncoder<'a> { +impl ReceiptWithBloomEncoder<'_> { /// Returns the rlp header for the receipt payload. fn receipt_rlp_header(&self) -> alloy_rlp::Header { let mut rlp_head = alloy_rlp::Header { list: true, payload_length: 0 }; @@ -481,7 +481,7 @@ impl<'a> ReceiptWithBloomEncoder<'a> { } } -impl<'a> Encodable for ReceiptWithBloomEncoder<'a> { +impl Encodable for ReceiptWithBloomEncoder<'_> { fn encode(&self, out: &mut dyn BufMut) { self.encode_inner(out, true) } diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index b16c2c88ab52..7ef2c0c1fb76 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -1640,7 +1640,7 @@ pub mod serde_bincode_compat { } } - impl<'a> SerializeAs for Transaction<'a> { + impl SerializeAs for Transaction<'_> { fn serialize_as(source: &super::Transaction, serializer: S) -> Result where S: Serializer, @@ -1700,7 +1700,7 @@ pub mod serde_bincode_compat { } } - impl<'a> SerializeAs for TransactionSigned<'a> { + impl SerializeAs for TransactionSigned<'_> { fn serialize_as( source: &super::TransactionSigned, serializer: S, diff --git a/crates/prune/prune/src/segments/static_file/headers.rs b/crates/prune/prune/src/segments/static_file/headers.rs index a3daf504e667..8700a653b111 100644 --- a/crates/prune/prune/src/segments/static_file/headers.rs +++ b/crates/prune/prune/src/segments/static_file/headers.rs @@ -137,7 +137,7 @@ where } } -impl<'a, Provider> Iterator for HeaderTablesIter<'a, Provider> +impl Iterator for HeaderTablesIter<'_, Provider> where Provider: DBProvider, { diff --git a/crates/prune/types/src/limiter.rs b/crates/prune/types/src/limiter.rs index 09f0c4cd3f2d..3a1059949300 100644 --- a/crates/prune/types/src/limiter.rs +++ b/crates/prune/types/src/limiter.rs @@ -120,3 +120,270 @@ impl PruneLimiter { self.is_deleted_entries_limit_reached() || self.is_time_limit_reached() } } + +#[cfg(test)] +mod tests { + use super::*; + use std::thread::sleep; + + #[test] + fn test_prune_deleted_entries_limit_initial_state() { + let limit_tracker = PruneDeletedEntriesLimit::new(10); + // Limit should be set properly + assert_eq!(limit_tracker.limit, 10); + // No entries should be deleted + assert_eq!(limit_tracker.deleted, 0); + assert!(!limit_tracker.is_limit_reached()); + } + + #[test] + fn test_prune_deleted_entries_limit_is_limit_reached() { + // Test when the deleted entries are less than the limit + let mut limit_tracker = PruneDeletedEntriesLimit::new(5); + limit_tracker.deleted = 3; + assert!(!limit_tracker.is_limit_reached()); + + // Test when the deleted entries are equal to the limit + limit_tracker.deleted = 5; + assert!(limit_tracker.is_limit_reached()); + + // Test when the deleted entries exceed the limit + limit_tracker.deleted = 6; + assert!(limit_tracker.is_limit_reached()); + } + + #[test] + fn test_prune_time_limit_initial_state() { + let time_limit = PruneTimeLimit::new(Duration::from_secs(10)); + // The limit should be set correctly + assert_eq!(time_limit.limit, Duration::from_secs(10)); + // The elapsed time should be very small right after creation + assert!(time_limit.start.elapsed() < Duration::from_secs(1)); + // Limit should not be reached initially + assert!(!time_limit.is_limit_reached()); + } + + #[test] + fn test_prune_time_limit_is_limit_reached() { + let time_limit = PruneTimeLimit::new(Duration::from_millis(50)); + + // Simulate waiting for some time (less than the limit) + std::thread::sleep(Duration::from_millis(30)); + assert!(!time_limit.is_limit_reached()); + + // Simulate waiting for time greater than the limit + std::thread::sleep(Duration::from_millis(30)); + assert!(time_limit.is_limit_reached()); + } + + #[test] + fn test_set_deleted_entries_limit_initial_state() { + let pruner = PruneLimiter::default().set_deleted_entries_limit(100); + // The deleted_entries_limit should be set with the correct limit + assert!(pruner.deleted_entries_limit.is_some()); + let deleted_entries_limit = pruner.deleted_entries_limit.unwrap(); + assert_eq!(deleted_entries_limit.limit, 100); + // The deleted count should be initially zero + assert_eq!(deleted_entries_limit.deleted, 0); + // The limit should not be reached initially + assert!(!deleted_entries_limit.is_limit_reached()); + } + + #[test] + fn test_set_deleted_entries_limit_overwrite_existing() { + let mut pruner = PruneLimiter::default().set_deleted_entries_limit(50); + // Overwrite the existing limit + pruner = pruner.set_deleted_entries_limit(200); + + assert!(pruner.deleted_entries_limit.is_some()); + let deleted_entries_limit = pruner.deleted_entries_limit.unwrap(); + // Check that the limit has been overwritten correctly + assert_eq!(deleted_entries_limit.limit, 200); + // Deleted count should still be zero + assert_eq!(deleted_entries_limit.deleted, 0); + assert!(!deleted_entries_limit.is_limit_reached()); + } + + #[test] + fn test_set_deleted_entries_limit_when_limit_is_reached() { + let mut pruner = PruneLimiter::default().set_deleted_entries_limit(5); + assert!(pruner.deleted_entries_limit.is_some()); + let mut deleted_entries_limit = pruner.deleted_entries_limit.clone().unwrap(); + + // Simulate deletion of entries + deleted_entries_limit.deleted = 5; + assert!(deleted_entries_limit.is_limit_reached()); + + // Overwrite the limit and check if it resets correctly + pruner = pruner.set_deleted_entries_limit(10); + deleted_entries_limit = pruner.deleted_entries_limit.unwrap(); + assert_eq!(deleted_entries_limit.limit, 10); + // Deletion count should reset + assert_eq!(deleted_entries_limit.deleted, 0); + assert!(!deleted_entries_limit.is_limit_reached()); + } + + #[test] + fn test_floor_deleted_entries_limit_to_multiple_of() { + let limiter = PruneLimiter::default().set_deleted_entries_limit(15); + let denominator = NonZeroUsize::new(4).unwrap(); + + // Floor limit to the largest multiple of 4 less than or equal to 15 (that is 12) + let updated_limiter = limiter.floor_deleted_entries_limit_to_multiple_of(denominator); + assert_eq!(updated_limiter.deleted_entries_limit.unwrap().limit, 12); + + // Test when the limit is already a multiple of the denominator + let limiter = PruneLimiter::default().set_deleted_entries_limit(16); + let updated_limiter = limiter.floor_deleted_entries_limit_to_multiple_of(denominator); + assert_eq!(updated_limiter.deleted_entries_limit.unwrap().limit, 16); + + // Test when there's no limit set (should not panic) + let limiter = PruneLimiter::default(); + let updated_limiter = limiter.floor_deleted_entries_limit_to_multiple_of(denominator); + assert!(updated_limiter.deleted_entries_limit.is_none()); + } + + #[test] + fn test_is_deleted_entries_limit_reached() { + // Limit is not set, should return false + let limiter = PruneLimiter::default(); + assert!(!limiter.is_deleted_entries_limit_reached()); + + // Limit is set but not reached, should return false + let mut limiter = PruneLimiter::default().set_deleted_entries_limit(10); + limiter.deleted_entries_limit.as_mut().unwrap().deleted = 5; + // 5 entries deleted out of 10 + assert!(!limiter.is_deleted_entries_limit_reached()); + + // Limit is reached, should return true + limiter.deleted_entries_limit.as_mut().unwrap().deleted = 10; + // 10 entries deleted out of 10 + assert!(limiter.is_deleted_entries_limit_reached()); + + // Deleted entries exceed the limit, should return true + limiter.deleted_entries_limit.as_mut().unwrap().deleted = 12; + // 12 entries deleted out of 10 + assert!(limiter.is_deleted_entries_limit_reached()); + } + + #[test] + fn test_increment_deleted_entries_count_by() { + // Increment when no limit is set + let mut limiter = PruneLimiter::default(); + limiter.increment_deleted_entries_count_by(5); + assert_eq!(limiter.deleted_entries_limit.as_ref().map(|l| l.deleted), None); // Still None + + // Increment when limit is set + let mut limiter = PruneLimiter::default().set_deleted_entries_limit(10); + limiter.increment_deleted_entries_count_by(3); + assert_eq!(limiter.deleted_entries_limit.as_ref().unwrap().deleted, 3); // Now 3 deleted + + // Increment again + limiter.increment_deleted_entries_count_by(2); + assert_eq!(limiter.deleted_entries_limit.as_ref().unwrap().deleted, 5); // Now 5 deleted + } + + #[test] + fn test_increment_deleted_entries_count() { + let mut limiter = PruneLimiter::default().set_deleted_entries_limit(5); + assert_eq!(limiter.deleted_entries_limit.as_ref().unwrap().deleted, 0); // Initially 0 + + limiter.increment_deleted_entries_count(); // Increment by 1 + assert_eq!(limiter.deleted_entries_limit.as_ref().unwrap().deleted, 1); // Now 1 + } + + #[test] + fn test_deleted_entries_limit_left() { + // Test when limit is set and some entries are deleted + let mut limiter = PruneLimiter::default().set_deleted_entries_limit(10); + limiter.increment_deleted_entries_count_by(3); // Simulate 3 deleted entries + assert_eq!(limiter.deleted_entries_limit_left(), Some(7)); // 10 - 3 = 7 + + // Test when no entries are deleted + limiter = PruneLimiter::default().set_deleted_entries_limit(5); + assert_eq!(limiter.deleted_entries_limit_left(), Some(5)); // 5 - 0 = 5 + + // Test when limit is reached + limiter.increment_deleted_entries_count_by(5); // Simulate deleting 5 entries + assert_eq!(limiter.deleted_entries_limit_left(), Some(0)); // 5 - 5 = 0 + + // Test when limit is not set + limiter = PruneLimiter::default(); // No limit set + assert_eq!(limiter.deleted_entries_limit_left(), None); // Should be None + } + + #[test] + fn test_set_time_limit() { + // Create a PruneLimiter instance with no time limit set + let mut limiter = PruneLimiter::default(); + + // Set a time limit of 5 seconds + limiter = limiter.set_time_limit(Duration::new(5, 0)); + + // Verify that the time limit is set correctly + assert!(limiter.time_limit.is_some()); + let time_limit = limiter.time_limit.as_ref().unwrap(); + assert_eq!(time_limit.limit, Duration::new(5, 0)); + // Ensure the start time is recent + assert!(time_limit.start.elapsed() < Duration::new(1, 0)); + } + + #[test] + fn test_is_time_limit_reached() { + // Create a PruneLimiter instance and set a time limit of 10 milliseconds + let mut limiter = PruneLimiter::default(); + + // Time limit should not be reached initially + assert!(!limiter.is_time_limit_reached(), "Time limit should not be reached yet"); + + limiter = limiter.set_time_limit(Duration::new(0, 10_000_000)); // 10 milliseconds + + // Sleep for 5 milliseconds (less than the time limit) + sleep(Duration::new(0, 5_000_000)); // 5 milliseconds + assert!(!limiter.is_time_limit_reached(), "Time limit should not be reached yet"); + + // Sleep for an additional 10 milliseconds (totaling 15 milliseconds) + sleep(Duration::new(0, 10_000_000)); // 10 milliseconds + assert!(limiter.is_time_limit_reached(), "Time limit should be reached now"); + } + + #[test] + fn test_is_limit_reached() { + // Create a PruneLimiter instance + let mut limiter = PruneLimiter::default(); + + // Test when no limits are set + assert!(!limiter.is_limit_reached(), "Limit should not be reached with no limits set"); + + // Set a deleted entries limit + limiter = limiter.set_deleted_entries_limit(5); + assert!( + !limiter.is_limit_reached(), + "Limit should not be reached when deleted entries are less than limit" + ); + + // Increment deleted entries count to reach the limit + limiter.increment_deleted_entries_count_by(5); + assert!( + limiter.is_limit_reached(), + "Limit should be reached when deleted entries equal the limit" + ); + + // Reset the limiter + limiter = PruneLimiter::default(); + + // Set a time limit and check + limiter = limiter.set_time_limit(Duration::new(0, 10_000_000)); // 10 milliseconds + + // Sleep for 5 milliseconds (less than the time limit) + sleep(Duration::new(0, 5_000_000)); // 5 milliseconds + assert!( + !limiter.is_limit_reached(), + "Limit should not be reached when time limit not reached" + ); + + // Sleep for another 10 milliseconds (totaling 15 milliseconds) + sleep(Duration::new(0, 10_000_000)); // 10 milliseconds + assert!(limiter.is_limit_reached(), "Limit should be reached when time limit is reached"); + } +} diff --git a/crates/rpc/rpc-api/src/debug.rs b/crates/rpc/rpc-api/src/debug.rs index 4ea8c0f0e4df..3e03210f1ffd 100644 --- a/crates/rpc/rpc-api/src/debug.rs +++ b/crates/rpc/rpc-api/src/debug.rs @@ -140,11 +140,8 @@ pub trait DebugApi { /// The first argument is the block number or block hash. The second argument is a boolean /// indicating whether to include the preimages of keys in the response. #[method(name = "executionWitness")] - async fn debug_execution_witness( - &self, - block: BlockNumberOrTag, - include_preimages: bool, - ) -> RpcResult; + async fn debug_execution_witness(&self, block: BlockNumberOrTag) + -> RpcResult; /// Sets the logging backtrace location. When a backtrace location is set and a log message is /// emitted at that location, the stack of the goroutine executing the log statement will diff --git a/crates/rpc/rpc-builder/src/lib.rs b/crates/rpc/rpc-builder/src/lib.rs index 113cb93c4630..e49105ab7d7d 100644 --- a/crates/rpc/rpc-builder/src/lib.rs +++ b/crates/rpc/rpc-builder/src/lib.rs @@ -1832,6 +1832,13 @@ impl TransportRpcModules { } } + /// Removes the given methods from the configured http methods. + pub fn remove_http_methods(&mut self, methods: impl IntoIterator) { + for name in methods { + self.remove_http_method(name); + } + } + /// Removes the method with the given name from the configured ws methods. /// /// Returns `true` if the method was found and removed, `false` otherwise. @@ -1847,6 +1854,13 @@ impl TransportRpcModules { } } + /// Removes the given methods from the configured ws methods. + pub fn remove_ws_methods(&mut self, methods: impl IntoIterator) { + for name in methods { + self.remove_ws_method(name); + } + } + /// Removes the method with the given name from the configured ipc methods. /// /// Returns `true` if the method was found and removed, `false` otherwise. @@ -1862,6 +1876,13 @@ impl TransportRpcModules { } } + /// Removes the given methods from the configured ipc methods. + pub fn remove_ipc_methods(&mut self, methods: impl IntoIterator) { + for name in methods { + self.remove_ipc_method(name); + } + } + /// Removes the method with the given name from all configured transports. /// /// Returns `true` if the method was found and removed, `false` otherwise. @@ -1872,6 +1893,56 @@ impl TransportRpcModules { http_removed || ws_removed || ipc_removed } + + /// Replace the given [Methods] in the configured http methods. + /// + /// Fails if any of the methods in other is present already or if the method being removed is + /// not present + /// + /// Returns [Ok(false)] if no http transport is configured. + pub fn replace_http(&mut self, other: impl Into) -> Result { + let other = other.into(); + self.remove_http_methods(other.method_names()); + self.merge_http(other) + } + + /// Replace the given [Methods] in the configured ipc methods. + /// + /// Fails if any of the methods in other is present already or if the method being removed is + /// not present + /// + /// Returns [Ok(false)] if no ipc transport is configured. + pub fn replace_ipc(&mut self, other: impl Into) -> Result { + let other = other.into(); + self.remove_ipc_methods(other.method_names()); + self.merge_ipc(other) + } + + /// Replace the given [Methods] in the configured ws methods. + /// + /// Fails if any of the methods in other is present already or if the method being removed is + /// not present + /// + /// Returns [Ok(false)] if no ws transport is configured. + pub fn replace_ws(&mut self, other: impl Into) -> Result { + let other = other.into(); + self.remove_ws_methods(other.method_names()); + self.merge_ws(other) + } + + /// Replaces the method with the given name from all configured transports. + /// + /// Returns `true` if the method was found and replaced, `false` otherwise + pub fn replace_configured( + &mut self, + other: impl Into, + ) -> Result { + let other = other.into(); + self.replace_http(other.clone())?; + self.replace_ws(other.clone())?; + self.replace_ipc(other)?; + Ok(true) + } } /// A handle to the spawned servers. @@ -2121,81 +2192,152 @@ mod tests { ) } - mod remove_methods { - use super::*; + fn create_test_module() -> RpcModule<()> { + let mut module = RpcModule::new(()); + module.register_method("anything", |_, _, _| "succeed").unwrap(); + module + } - fn create_test_module() -> RpcModule<()> { - let mut module = RpcModule::new(()); - module.register_method("anything", |_, _, _| "succeed").unwrap(); - module - } + #[test] + fn test_remove_http_method() { + let mut modules = + TransportRpcModules { http: Some(create_test_module()), ..Default::default() }; + // Remove a method that exists + assert!(modules.remove_http_method("anything")); + + // Remove a method that does not exist + assert!(!modules.remove_http_method("non_existent_method")); - #[test] - fn test_remove_http_method() { - let mut modules = - TransportRpcModules { http: Some(create_test_module()), ..Default::default() }; - // Remove a method that exists - assert!(modules.remove_http_method("anything")); + // Verify that the method was removed + assert!(modules.http.as_ref().unwrap().method("anything").is_none()); + } - // Remove a method that does not exist - assert!(!modules.remove_http_method("non_existent_method")); + #[test] + fn test_remove_ws_method() { + let mut modules = + TransportRpcModules { ws: Some(create_test_module()), ..Default::default() }; - // Verify that the method was removed - assert!(modules.http.as_ref().unwrap().method("anything").is_none()); - } + // Remove a method that exists + assert!(modules.remove_ws_method("anything")); - #[test] - fn test_remove_ws_method() { - let mut modules = - TransportRpcModules { ws: Some(create_test_module()), ..Default::default() }; + // Remove a method that does not exist + assert!(!modules.remove_ws_method("non_existent_method")); - // Remove a method that exists - assert!(modules.remove_ws_method("anything")); + // Verify that the method was removed + assert!(modules.ws.as_ref().unwrap().method("anything").is_none()); + } - // Remove a method that does not exist - assert!(!modules.remove_ws_method("non_existent_method")); + #[test] + fn test_remove_ipc_method() { + let mut modules = + TransportRpcModules { ipc: Some(create_test_module()), ..Default::default() }; - // Verify that the method was removed - assert!(modules.ws.as_ref().unwrap().method("anything").is_none()); - } + // Remove a method that exists + assert!(modules.remove_ipc_method("anything")); - #[test] - fn test_remove_ipc_method() { - let mut modules = - TransportRpcModules { ipc: Some(create_test_module()), ..Default::default() }; + // Remove a method that does not exist + assert!(!modules.remove_ipc_method("non_existent_method")); - // Remove a method that exists - assert!(modules.remove_ipc_method("anything")); + // Verify that the method was removed + assert!(modules.ipc.as_ref().unwrap().method("anything").is_none()); + } - // Remove a method that does not exist - assert!(!modules.remove_ipc_method("non_existent_method")); + #[test] + fn test_remove_method_from_configured() { + let mut modules = TransportRpcModules { + http: Some(create_test_module()), + ws: Some(create_test_module()), + ipc: Some(create_test_module()), + ..Default::default() + }; - // Verify that the method was removed - assert!(modules.ipc.as_ref().unwrap().method("anything").is_none()); - } + // Remove a method that exists + assert!(modules.remove_method_from_configured("anything")); - #[test] - fn test_remove_method_from_configured() { - let mut modules = TransportRpcModules { - http: Some(create_test_module()), - ws: Some(create_test_module()), - ipc: Some(create_test_module()), - ..Default::default() - }; + // Remove a method that was just removed (it does not exist anymore) + assert!(!modules.remove_method_from_configured("anything")); - // Remove a method that exists - assert!(modules.remove_method_from_configured("anything")); + // Remove a method that does not exist + assert!(!modules.remove_method_from_configured("non_existent_method")); - // Remove a method that was just removed (it does not exist anymore) - assert!(!modules.remove_method_from_configured("anything")); + // Verify that the method was removed from all transports + assert!(modules.http.as_ref().unwrap().method("anything").is_none()); + assert!(modules.ws.as_ref().unwrap().method("anything").is_none()); + assert!(modules.ipc.as_ref().unwrap().method("anything").is_none()); + } - // Remove a method that does not exist - assert!(!modules.remove_method_from_configured("non_existent_method")); + #[test] + fn test_replace_http_method() { + let mut modules = + TransportRpcModules { http: Some(create_test_module()), ..Default::default() }; - // Verify that the method was removed from all transports - assert!(modules.http.as_ref().unwrap().method("anything").is_none()); - assert!(modules.ws.as_ref().unwrap().method("anything").is_none()); - assert!(modules.ipc.as_ref().unwrap().method("anything").is_none()); - } + let mut other_module = RpcModule::new(()); + other_module.register_method("something", |_, _, _| "fails").unwrap(); + + assert!(modules.replace_http(other_module.clone()).unwrap()); + + assert!(modules.http.as_ref().unwrap().method("something").is_some()); + + other_module.register_method("anything", |_, _, _| "fails").unwrap(); + assert!(modules.replace_http(other_module.clone()).unwrap()); + + assert!(modules.http.as_ref().unwrap().method("anything").is_some()); + } + #[test] + fn test_replace_ipc_method() { + let mut modules = + TransportRpcModules { ipc: Some(create_test_module()), ..Default::default() }; + + let mut other_module = RpcModule::new(()); + other_module.register_method("something", |_, _, _| "fails").unwrap(); + + assert!(modules.replace_ipc(other_module.clone()).unwrap()); + + assert!(modules.ipc.as_ref().unwrap().method("something").is_some()); + + other_module.register_method("anything", |_, _, _| "fails").unwrap(); + assert!(modules.replace_ipc(other_module.clone()).unwrap()); + + assert!(modules.ipc.as_ref().unwrap().method("anything").is_some()); + } + #[test] + fn test_replace_ws_method() { + let mut modules = + TransportRpcModules { ws: Some(create_test_module()), ..Default::default() }; + + let mut other_module = RpcModule::new(()); + other_module.register_method("something", |_, _, _| "fails").unwrap(); + + assert!(modules.replace_ws(other_module.clone()).unwrap()); + + assert!(modules.ws.as_ref().unwrap().method("something").is_some()); + + other_module.register_method("anything", |_, _, _| "fails").unwrap(); + assert!(modules.replace_ws(other_module.clone()).unwrap()); + + assert!(modules.ws.as_ref().unwrap().method("anything").is_some()); + } + + #[test] + fn test_replace_configured() { + let mut modules = TransportRpcModules { + http: Some(create_test_module()), + ws: Some(create_test_module()), + ipc: Some(create_test_module()), + ..Default::default() + }; + let mut other_module = RpcModule::new(()); + other_module.register_method("something", |_, _, _| "fails").unwrap(); + + assert!(modules.replace_configured(other_module).unwrap()); + + // Verify that the other_method was added + assert!(modules.http.as_ref().unwrap().method("something").is_some()); + assert!(modules.ipc.as_ref().unwrap().method("something").is_some()); + assert!(modules.ws.as_ref().unwrap().method("something").is_some()); + + assert!(modules.http.as_ref().unwrap().method("anything").is_some()); + assert!(modules.ipc.as_ref().unwrap().method("anything").is_some()); + assert!(modules.ws.as_ref().unwrap().method("anything").is_some()); } } diff --git a/crates/rpc/rpc-engine-api/src/engine_api.rs b/crates/rpc/rpc-engine-api/src/engine_api.rs index 907297de1776..252808c14a77 100644 --- a/crates/rpc/rpc-engine-api/src/engine_api.rs +++ b/crates/rpc/rpc-engine-api/src/engine_api.rs @@ -464,48 +464,59 @@ where } /// Called to retrieve execution payload bodies by hashes. - fn get_payload_bodies_by_hash_with( + async fn get_payload_bodies_by_hash_with( &self, hashes: Vec, f: F, ) -> EngineApiResult>> where - F: Fn(Block) -> R, + F: Fn(Block) -> R + Send + 'static, + R: Send + 'static, { let len = hashes.len() as u64; if len > MAX_PAYLOAD_BODIES_LIMIT { - return Err(EngineApiError::PayloadRequestTooLarge { len }) + return Err(EngineApiError::PayloadRequestTooLarge { len }); } - let mut result = Vec::with_capacity(hashes.len()); - for hash in hashes { - let block = self - .inner - .provider - .block(BlockHashOrNumber::Hash(hash)) - .map_err(|err| EngineApiError::Internal(Box::new(err)))?; - result.push(block.map(&f)); - } + let (tx, rx) = oneshot::channel(); + let inner = self.inner.clone(); - Ok(result) + self.inner.task_spawner.spawn_blocking(Box::pin(async move { + let mut result = Vec::with_capacity(hashes.len()); + for hash in hashes { + let block_result = inner.provider.block(BlockHashOrNumber::Hash(hash)); + match block_result { + Ok(block) => { + result.push(block.map(&f)); + } + Err(err) => { + let _ = tx.send(Err(EngineApiError::Internal(Box::new(err)))); + return; + } + } + } + tx.send(Ok(result)).ok(); + })); + + rx.await.map_err(|err| EngineApiError::Internal(Box::new(err)))? } /// Called to retrieve execution payload bodies by hashes. - pub fn get_payload_bodies_by_hash_v1( + pub async fn get_payload_bodies_by_hash_v1( &self, hashes: Vec, ) -> EngineApiResult { - self.get_payload_bodies_by_hash_with(hashes, convert_to_payload_body_v1) + self.get_payload_bodies_by_hash_with(hashes, convert_to_payload_body_v1).await } /// Called to retrieve execution payload bodies by hashes. /// /// Same as [`Self::get_payload_bodies_by_hash_v1`] but as [`ExecutionPayloadBodiesV2`]. - pub fn get_payload_bodies_by_hash_v2( + pub async fn get_payload_bodies_by_hash_v2( &self, hashes: Vec, ) -> EngineApiResult { - self.get_payload_bodies_by_hash_with(hashes, convert_to_payload_body_v2) + self.get_payload_bodies_by_hash_with(hashes, convert_to_payload_body_v2).await } /// Called to verify network configuration parameters and ensure that Consensus and Execution @@ -832,7 +843,7 @@ where let start = Instant::now(); let res = Self::get_payload_bodies_by_hash_v1(self, block_hashes); self.inner.metrics.latency.get_payload_bodies_by_hash_v1.record(start.elapsed()); - Ok(res?) + Ok(res.await?) } async fn get_payload_bodies_by_hash_v2( @@ -843,7 +854,7 @@ where let start = Instant::now(); let res = Self::get_payload_bodies_by_hash_v2(self, block_hashes); self.inner.metrics.latency.get_payload_bodies_by_hash_v2.record(start.elapsed()); - Ok(res?) + Ok(res.await?) } /// Handler for `engine_getPayloadBodiesByRangeV1` @@ -1147,7 +1158,7 @@ mod tests { .collect::>(); let hashes = blocks.iter().map(|b| b.hash()).collect(); - let res = api.get_payload_bodies_by_hash_v1(hashes).unwrap(); + let res = api.get_payload_bodies_by_hash_v1(hashes).await.unwrap(); assert_eq!(res, expected); } } diff --git a/crates/rpc/rpc-eth-api/src/helpers/block.rs b/crates/rpc/rpc-eth-api/src/helpers/block.rs index 3a26536cc7cf..d7e081d80f80 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/block.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/block.rs @@ -6,7 +6,7 @@ use alloy_rpc_types::{Header, Index}; use futures::Future; use reth_primitives::{BlockId, Receipt, SealedBlock, SealedBlockWithSenders}; use reth_provider::{BlockIdReader, BlockReader, BlockReaderIdExt, HeaderProvider}; -use reth_rpc_eth_types::{EthApiError, EthStateCache}; +use reth_rpc_eth_types::EthStateCache; use reth_rpc_types_compat::block::{from_block, uncle_block_from_header}; use crate::{FromEthApiError, FullEthApiTypes, RpcBlock, RpcReceipt}; @@ -47,13 +47,20 @@ pub trait EthBlocks: LoadBlock { async move { let Some(block) = self.block_with_senders(block_id).await? else { return Ok(None) }; let block_hash = block.hash(); - let total_difficulty = EthBlocks::provider(self) + let mut total_difficulty = EthBlocks::provider(self) .header_td_by_number(block.number) - .map_err(Self::Error::from_eth_err)? - .ok_or(EthApiError::HeaderNotFound(block_id))?; + .map_err(Self::Error::from_eth_err)?; + if total_difficulty.is_none() { + // if we failed to find td after we successfully loaded the block, try again using + // the hash this only matters if the chain is currently transitioning the merge block and there's a reorg: + total_difficulty = EthBlocks::provider(self) + .header_td(&block.hash()) + .map_err(Self::Error::from_eth_err)?; + } + let block = from_block::( block.unseal(), - total_difficulty, + total_difficulty.unwrap_or_default(), full.into(), Some(block_hash), ) diff --git a/crates/rpc/rpc-eth-api/src/helpers/call.rs b/crates/rpc/rpc-eth-api/src/helpers/call.rs index 8d34020d67bc..819aff546d42 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/call.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/call.rs @@ -256,7 +256,6 @@ pub trait EthCall: Call + LoadPendingBlock { )?; let block = block.ok_or(EthApiError::HeaderNotFound(target_block))?; - let gas_limit = self.call_gas_limit(); // we're essentially replaying the transactions in the block here, hence we need the // state that points to the beginning of the block, which is the state at @@ -302,14 +301,7 @@ pub trait EthCall: Call + LoadPendingBlock { let overrides = EvmOverrides::new(state_overrides, block_overrides.clone()); let env = this - .prepare_call_env( - cfg.clone(), - block_env.clone(), - tx, - gas_limit, - &mut db, - overrides, - ) + .prepare_call_env(cfg.clone(), block_env.clone(), tx, &mut db, overrides) .map(Into::into)?; let (res, _) = this.transact(&mut db, env)?; @@ -559,14 +551,7 @@ pub trait Call: LoadState + SpawnBlocking { let mut db = CacheDB::new(StateProviderDatabase::new(StateProviderTraitObjWrapper(&state))); - let env = this.prepare_call_env( - cfg, - block_env, - request, - this.call_gas_limit(), - &mut db, - overrides, - )?; + let env = this.prepare_call_env(cfg, block_env, request, &mut db, overrides)?; f(StateCacheDbRefMutWrapper(&mut db), env) }) @@ -1038,7 +1023,14 @@ pub trait Call: LoadState + SpawnBlocking { block_env.get_blob_gasprice().map(U256::from), )?; - let gas_limit = gas.unwrap_or_else(|| block_env.gas_limit.min(U256::from(u64::MAX)).to()); + 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 { @@ -1080,7 +1072,7 @@ pub trait Call: LoadState + SpawnBlocking { Ok(EnvWithHandlerCfg::new_with_cfg_env(cfg, block, tx)) } - /// Prepares the [`EnvWithHandlerCfg`] for execution. + /// Prepares the [`EnvWithHandlerCfg`] for execution of calls. /// /// Does not commit any changes to the underlying database. /// @@ -1091,15 +1083,11 @@ pub trait Call: LoadState + SpawnBlocking { /// - `disable_eip3607` is set to `true` /// - `disable_base_fee` is set to `true` /// - `nonce` is set to `None` - /// - /// Additionally, the block gas limit so that higher tx gas limits can be used in `eth_call`. - /// - `disable_block_gas_limit` is set to `true` fn prepare_call_env( &self, mut cfg: CfgEnvWithHandlerCfg, mut block: BlockEnv, mut request: TransactionRequest, - gas_limit: u64, db: &mut CacheDB, overrides: EvmOverrides, ) -> Result @@ -1107,9 +1095,12 @@ pub trait Call: LoadState + SpawnBlocking { DB: DatabaseRef, EthApiError: From<::Error>, { - // we want to disable this in eth_call, since this is common practice used by other node - // impls and providers - cfg.disable_block_gas_limit = true; + if request.gas > Some(self.call_gas_limit()) { + // configured gas exceeds limit + return Err( + EthApiError::InvalidTransaction(RpcInvalidTransactionError::GasTooHigh).into() + ) + } // Disabled because eth_call is sometimes used with eoa senders // See @@ -1138,15 +1129,9 @@ pub trait Call: LoadState + SpawnBlocking { if env.tx.gas_price > U256::ZERO { // If gas price is specified, cap transaction gas limit with caller allowance trace!(target: "rpc::eth::call", ?env, "Applying gas limit cap with caller allowance"); - cap_tx_gas_limit_with_caller_allowance(db, &mut env.tx)?; - } else { - // If no gas price is specified, 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: - // - trace!(target: "rpc::eth::call", ?env, "Applying gas limit cap as the maximum gas limit"); - env.tx.gas_limit = gas_limit; + let cap = caller_gas_allowance(db, &env.tx)?; + // ensure we cap gas_limit to the block's + env.tx.gas_limit = cap.min(env.block.gas_limit).saturating_to(); } } diff --git a/crates/rpc/rpc-eth-api/src/helpers/state.rs b/crates/rpc/rpc-eth-api/src/helpers/state.rs index dee4e5895638..d601e43d90a8 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/state.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/state.rs @@ -9,11 +9,12 @@ use reth_errors::RethError; use reth_evm::ConfigureEvmEnv; use reth_primitives::{BlockId, Header, KECCAK_EMPTY}; use reth_provider::{ - BlockIdReader, ChainSpecProvider, StateProvider, StateProviderBox, StateProviderFactory, + BlockIdReader, BlockNumReader, ChainSpecProvider, StateProvider, StateProviderBox, + StateProviderFactory, }; use reth_rpc_eth_types::{EthApiError, EthStateCache, PendingBlockEnv, RpcInvalidTransactionError}; use reth_rpc_types_compat::proof::from_primitive_account_proof; -use reth_transaction_pool::{PoolTransaction, TransactionPool}; +use reth_transaction_pool::TransactionPool; use revm_primitives::{BlockEnv, CfgEnvWithHandlerCfg, SpecId}; use crate::{EthApiTypes, FromEthApiError}; @@ -92,25 +93,26 @@ pub trait EthState: LoadState + SpawnBlocking { where Self: EthApiSpec, { - let chain_info = self.chain_info().map_err(Self::Error::from_eth_err)?; - let block_id = block_id.unwrap_or_default(); - - // Check whether the distance to the block exceeds the maximum configured window. - let block_number = LoadState::provider(self) - .block_number_for_id(block_id) - .map_err(Self::Error::from_eth_err)? - .ok_or(EthApiError::HeaderNotFound(block_id))?; - let max_window = self.max_proof_window(); - if chain_info.best_number.saturating_sub(block_number) > max_window { - return Err(EthApiError::ExceedsMaxProofWindow.into()) - } - Ok(async move { let _permit = self .acquire_owned() .await .map_err(RethError::other) .map_err(EthApiError::Internal)?; + + let chain_info = self.chain_info().map_err(Self::Error::from_eth_err)?; + let block_id = block_id.unwrap_or_default(); + + // Check whether the distance to the block exceeds the maximum configured window. + let block_number = LoadState::provider(self) + .block_number_for_id(block_id) + .map_err(Self::Error::from_eth_err)? + .ok_or(EthApiError::HeaderNotFound(block_id))?; + let max_window = self.max_proof_window(); + if chain_info.best_number.saturating_sub(block_number) > max_window { + return Err(EthApiError::ExceedsMaxProofWindow.into()) + } + self.spawn_blocking_io(move |this| { let state = this.state_at_block_id(block_id)?; let storage_keys = keys.iter().map(|key| key.0).collect::>(); @@ -131,10 +133,21 @@ pub trait EthState: LoadState + SpawnBlocking { ) -> impl Future, Self::Error>> + Send { self.spawn_blocking_io(move |this| { let state = this.state_at_block_id(block_id)?; - let account = state.basic_account(address).map_err(Self::Error::from_eth_err)?; let Some(account) = account else { return Ok(None) }; + // Check whether the distance to the block exceeds the maximum configured proof window. + let chain_info = + LoadState::provider(&this).chain_info().map_err(Self::Error::from_eth_err)?; + let block_number = LoadState::provider(&this) + .block_number_for_id(block_id) + .map_err(Self::Error::from_eth_err)? + .ok_or(EthApiError::HeaderNotFound(block_id))?; + let max_window = this.max_proof_window(); + if chain_info.best_number.saturating_sub(block_number) > max_window { + return Err(EthApiError::ExceedsMaxProofWindow.into()) + } + let balance = account.balance; let nonce = account.nonce; let code_hash = account.bytecode_hash.unwrap_or(KECCAK_EMPTY); @@ -280,23 +293,23 @@ pub trait LoadState: EthApiTypes { if block_id == Some(BlockId::pending()) { // for pending tag we need to find the highest nonce in the pool - let address_txs = this.pool().get_transactions_by_sender(address); - if let Some(highest_pool_nonce) = - address_txs.iter().map(|item| item.transaction.nonce()).max() + if let Some(highest_pool_tx) = + this.pool().get_highest_transaction_by_sender(address) { - // and the corresponding txcount is nonce + 1 - let next_nonce = - nonce.max(highest_pool_nonce).checked_add(1).ok_or_else(|| { - Self::Error::from(EthApiError::InvalidTransaction( - RpcInvalidTransactionError::NonceMaxValue, - )) - })?; - - let tx_count = nonce.max(next_nonce); - return Ok(U256::from(tx_count)) + { + // and the corresponding txcount is nonce + 1 + let next_nonce = + nonce.max(highest_pool_tx.nonce()).checked_add(1).ok_or_else(|| { + Self::Error::from(EthApiError::InvalidTransaction( + RpcInvalidTransactionError::NonceMaxValue, + )) + })?; + + let tx_count = nonce.max(next_nonce); + return Ok(U256::from(tx_count)); + } } } - Ok(U256::from(nonce)) }) } diff --git a/crates/rpc/rpc-eth-api/src/helpers/transaction.rs b/crates/rpc/rpc-eth-api/src/helpers/transaction.rs index d98cb69bfc30..d70ea7e541cb 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/transaction.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/transaction.rs @@ -231,7 +231,7 @@ pub trait EthTransactions: LoadTransaction { LoadState::pool(self).get_transaction_by_sender_and_nonce(sender, nonce) { let transaction = tx.transaction.clone().into_consensus(); - return Ok(Some(from_recovered::(transaction))); + return Ok(Some(from_recovered::(transaction.into()))); } } @@ -324,7 +324,7 @@ pub trait EthTransactions: LoadTransaction { async move { let recovered = recover_raw_transaction(tx.clone())?; let pool_transaction = - ::Transaction::from_pooled(recovered); + ::Transaction::from_pooled(recovered.into()); // submit the transaction to the pool with a `Local` origin let hash = self @@ -376,7 +376,7 @@ pub trait EthTransactions: LoadTransaction { let recovered = signed_tx.into_ecrecovered().ok_or(EthApiError::InvalidTransactionSignature)?; - let pool_transaction = <::Pool as TransactionPool>::Transaction::try_from_consensus(recovered).map_err(|_| EthApiError::TransactionConversionError)?; + let pool_transaction = <::Pool as TransactionPool>::Transaction::try_from_consensus(recovered.into()).map_err(|_| EthApiError::TransactionConversionError)?; // submit the transaction to the pool with a `Local` origin let hash = LoadTransaction::pool(self) @@ -518,7 +518,7 @@ pub trait LoadTransaction: SpawnBlocking + FullEthApiTypes { if let Some(tx) = self.pool().get(&hash).map(|tx| tx.transaction.clone().into_consensus()) { - resp = Some(TransactionSource::Pool(tx)); + resp = Some(TransactionSource::Pool(tx.into())); } } diff --git a/crates/rpc/rpc-eth-types/src/cache/db.rs b/crates/rpc/rpc-eth-types/src/cache/db.rs index ad9804893a70..9731e3845768 100644 --- a/crates/rpc/rpc-eth-types/src/cache/db.rs +++ b/crates/rpc/rpc-eth-types/src/cache/db.rs @@ -20,7 +20,7 @@ pub type StateCacheDb<'a> = CacheDB(pub &'a dyn StateProvider); -impl<'a> reth_storage_api::StateRootProvider for StateProviderTraitObjWrapper<'a> { +impl reth_storage_api::StateRootProvider for StateProviderTraitObjWrapper<'_> { fn state_root( &self, hashed_state: reth_trie::HashedPostState, @@ -50,7 +50,7 @@ impl<'a> reth_storage_api::StateRootProvider for StateProviderTraitObjWrapper<'a } } -impl<'a> reth_storage_api::StorageRootProvider for StateProviderTraitObjWrapper<'a> { +impl reth_storage_api::StorageRootProvider for StateProviderTraitObjWrapper<'_> { fn storage_root( &self, address: Address, @@ -60,7 +60,7 @@ impl<'a> reth_storage_api::StorageRootProvider for StateProviderTraitObjWrapper< } } -impl<'a> reth_storage_api::StateProofProvider for StateProviderTraitObjWrapper<'a> { +impl reth_storage_api::StateProofProvider for StateProviderTraitObjWrapper<'_> { fn proof( &self, input: reth_trie::TrieInput, @@ -88,7 +88,7 @@ impl<'a> reth_storage_api::StateProofProvider for StateProviderTraitObjWrapper<' } } -impl<'a> reth_storage_api::AccountReader for StateProviderTraitObjWrapper<'a> { +impl reth_storage_api::AccountReader for StateProviderTraitObjWrapper<'_> { fn basic_account( &self, address: revm_primitives::Address, @@ -97,7 +97,7 @@ impl<'a> reth_storage_api::AccountReader for StateProviderTraitObjWrapper<'a> { } } -impl<'a> reth_storage_api::BlockHashReader for StateProviderTraitObjWrapper<'a> { +impl reth_storage_api::BlockHashReader for StateProviderTraitObjWrapper<'_> { fn block_hash( &self, block_number: alloy_primitives::BlockNumber, @@ -121,7 +121,7 @@ impl<'a> reth_storage_api::BlockHashReader for StateProviderTraitObjWrapper<'a> } } -impl<'a> StateProvider for StateProviderTraitObjWrapper<'a> { +impl StateProvider for StateProviderTraitObjWrapper<'_> { fn account_balance( &self, addr: revm_primitives::Address, @@ -164,7 +164,7 @@ impl<'a> StateProvider for StateProviderTraitObjWrapper<'a> { #[allow(missing_debug_implementations)] pub struct StateCacheDbRefMutWrapper<'a, 'b>(pub &'b mut StateCacheDb<'a>); -impl<'a, 'b> Database for StateCacheDbRefMutWrapper<'a, 'b> { +impl<'a> Database for StateCacheDbRefMutWrapper<'a, '_> { type Error = as Database>::Error; fn basic( &mut self, @@ -190,7 +190,7 @@ impl<'a, 'b> Database for StateCacheDbRefMutWrapper<'a, 'b> { } } -impl<'a, 'b> DatabaseRef for StateCacheDbRefMutWrapper<'a, 'b> { +impl<'a> DatabaseRef for StateCacheDbRefMutWrapper<'a, '_> { type Error = as Database>::Error; fn basic_ref( diff --git a/crates/rpc/rpc-eth-types/src/error.rs b/crates/rpc/rpc-eth-types/src/error.rs index fbb93164ce9f..212fca36d9c9 100644 --- a/crates/rpc/rpc-eth-types/src/error.rs +++ b/crates/rpc/rpc-eth-types/src/error.rs @@ -365,7 +365,7 @@ pub enum RpcInvalidTransactionError { PrecompileOutOfGas(u64), /// An operand to an opcode was invalid or out of range. /// Contains the gas limit. - #[error("out of gas: invalid operand to an opcode; {0}")] + #[error("out of gas: invalid operand to an opcode: {0}")] InvalidOperandOutOfGas(u64), /// Thrown if executing a transaction failed during estimate/call #[error(transparent)] @@ -488,8 +488,14 @@ impl From for RpcInvalidTransactionError { InvalidTransaction::InvalidChainId => Self::InvalidChainId, InvalidTransaction::PriorityFeeGreaterThanMaxFee => Self::TipAboveFeeCap, InvalidTransaction::GasPriceLessThanBasefee => Self::FeeCapTooLow, - InvalidTransaction::CallerGasLimitMoreThanBlock | - InvalidTransaction::CallGasCostMoreThanGasLimit => Self::GasTooHigh, + InvalidTransaction::CallerGasLimitMoreThanBlock => { + // tx.gas > block.gas_limit + Self::GasTooHigh + } + InvalidTransaction::CallGasCostMoreThanGasLimit => { + // tx.gas < cost + Self::GasTooLow + } InvalidTransaction::RejectCallerWithCode => Self::SenderNoEOA, InvalidTransaction::LackOfFundForMaxFee { fee, balance } => { Self::InsufficientFunds { cost: *fee, balance: *balance } diff --git a/crates/rpc/rpc-eth-types/src/lib.rs b/crates/rpc/rpc-eth-types/src/lib.rs index fba893c15f59..fa36dae4c881 100644 --- a/crates/rpc/rpc-eth-types/src/lib.rs +++ b/crates/rpc/rpc-eth-types/src/lib.rs @@ -36,7 +36,6 @@ pub use gas_oracle::{ GasCap, GasPriceOracle, GasPriceOracleConfig, GasPriceOracleResult, RPC_DEFAULT_GAS_CAP, }; pub use id_provider::EthSubscriptionIdProvider; -pub use logs_utils::EthFilterError; pub use pending_block::{PendingBlock, PendingBlockEnv, PendingBlockEnvOrigin}; pub use receipt::ReceiptBuilder; pub use transaction::TransactionSource; diff --git a/crates/rpc/rpc-eth-types/src/logs_utils.rs b/crates/rpc/rpc-eth-types/src/logs_utils.rs index bb44dc0e6669..f26555bb70da 100644 --- a/crates/rpc/rpc-eth-types/src/logs_utils.rs +++ b/crates/rpc/rpc-eth-types/src/logs_utils.rs @@ -3,64 +3,12 @@ //! Log parsing for building filter. use alloy_primitives::TxHash; -use alloy_rpc_types::{FilterId, FilteredParams, Log}; +use alloy_rpc_types::{FilteredParams, Log}; use reth_chainspec::ChainInfo; use reth_errors::ProviderError; use reth_primitives::{BlockNumHash, Receipt}; -use reth_rpc_server_types::result::rpc_error_with_code; use reth_storage_api::BlockReader; -use crate::EthApiError; - -/// Errors that can occur in the handler implementation -#[derive(Debug, thiserror::Error)] -pub enum EthFilterError { - /// Filter not found. - #[error("filter not found")] - FilterNotFound(FilterId), - /// Invalid block range. - #[error("invalid block range params")] - InvalidBlockRangeParams, - /// Query scope is too broad. - #[error("query exceeds max block range {0}")] - QueryExceedsMaxBlocks(u64), - /// Query result is too large. - #[error("query exceeds max results {0}")] - QueryExceedsMaxResults(usize), - /// Error serving request in `eth_` namespace. - #[error(transparent)] - EthAPIError(#[from] EthApiError), - /// Error thrown when a spawned task failed to deliver a response. - #[error("internal filter error")] - InternalError, -} - -// convert the error -impl From for jsonrpsee_types::error::ErrorObject<'static> { - fn from(err: EthFilterError) -> Self { - match err { - EthFilterError::FilterNotFound(_) => { - rpc_error_with_code(jsonrpsee_types::error::INVALID_PARAMS_CODE, "filter not found") - } - err @ EthFilterError::InternalError => { - rpc_error_with_code(jsonrpsee_types::error::INTERNAL_ERROR_CODE, err.to_string()) - } - EthFilterError::EthAPIError(err) => err.into(), - err @ (EthFilterError::InvalidBlockRangeParams | - EthFilterError::QueryExceedsMaxBlocks(_) | - EthFilterError::QueryExceedsMaxResults(_)) => { - rpc_error_with_code(jsonrpsee_types::error::INVALID_PARAMS_CODE, err.to_string()) - } - } - } -} - -impl From for EthFilterError { - fn from(err: ProviderError) -> Self { - Self::EthAPIError(err.into()) - } -} - /// Returns all matching of a block's receipts when the transaction hashes are known. pub fn matching_block_logs_with_tx_hashes<'a, I>( filter: &FilteredParams, @@ -107,7 +55,7 @@ pub fn append_matching_block_logs( receipts: &[Receipt], removed: bool, block_timestamp: u64, -) -> Result<(), EthFilterError> { +) -> Result<(), ProviderError> { // Tracks the index of a log in the entire block. let mut log_index: u64 = 0; diff --git a/crates/rpc/rpc-layer/src/auth_layer.rs b/crates/rpc/rpc-layer/src/auth_layer.rs index 41ebce32dfb3..cdca181cbd06 100644 --- a/crates/rpc/rpc-layer/src/auth_layer.rs +++ b/crates/rpc/rpc-layer/src/auth_layer.rs @@ -176,7 +176,7 @@ mod tests { missing_jwt_error().await; wrong_jwt_signature_error().await; invalid_issuance_timestamp_error().await; - jwt_decode_error().await; + jwt_decode_error().await } async fn valid_jwt() { diff --git a/crates/rpc/rpc-testing-util/src/debug.rs b/crates/rpc/rpc-testing-util/src/debug.rs index cdcb454a2486..f50064e80ce9 100644 --- a/crates/rpc/rpc-testing-util/src/debug.rs +++ b/crates/rpc/rpc-testing-util/src/debug.rs @@ -304,7 +304,7 @@ impl<'a> DebugTraceTransactionsStream<'a> { } } -impl<'a> Stream for DebugTraceTransactionsStream<'a> { +impl Stream for DebugTraceTransactionsStream<'_> { type Item = TraceTransactionResult; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { @@ -312,7 +312,7 @@ impl<'a> Stream for DebugTraceTransactionsStream<'a> { } } -impl<'a> std::fmt::Debug for DebugTraceTransactionsStream<'a> { +impl std::fmt::Debug for DebugTraceTransactionsStream<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("DebugTraceTransactionsStream").finish_non_exhaustive() } @@ -336,7 +336,7 @@ impl<'a> DebugTraceBlockStream<'a> { } } -impl<'a> Stream for DebugTraceBlockStream<'a> { +impl Stream for DebugTraceBlockStream<'_> { type Item = DebugTraceBlockResult; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { @@ -344,7 +344,7 @@ impl<'a> Stream for DebugTraceBlockStream<'a> { } } -impl<'a> std::fmt::Debug for DebugTraceBlockStream<'a> { +impl std::fmt::Debug for DebugTraceBlockStream<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("DebugTraceBlockStream").finish_non_exhaustive() } diff --git a/crates/rpc/rpc-testing-util/src/trace.rs b/crates/rpc/rpc-testing-util/src/trace.rs index 13914a59eb3e..c6dc16cf1063 100644 --- a/crates/rpc/rpc-testing-util/src/trace.rs +++ b/crates/rpc/rpc-testing-util/src/trace.rs @@ -114,7 +114,7 @@ pub struct TraceCallStream<'a> { stream: Pin + 'a>>, } -impl<'a> Stream for TraceCallStream<'a> { +impl Stream for TraceCallStream<'_> { type Item = TraceCallResult; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { @@ -122,7 +122,7 @@ impl<'a> Stream for TraceCallStream<'a> { } } -impl<'a> std::fmt::Debug for TraceCallStream<'a> { +impl std::fmt::Debug for TraceCallStream<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("TraceCallStream").finish() } @@ -134,7 +134,7 @@ pub struct TraceFilterStream<'a> { stream: Pin + 'a>>, } -impl<'a> Stream for TraceFilterStream<'a> { +impl Stream for TraceFilterStream<'_> { type Item = TraceFilterResult; /// Attempts to pull out the next value of the stream. @@ -143,7 +143,7 @@ impl<'a> Stream for TraceFilterStream<'a> { } } -impl<'a> std::fmt::Debug for TraceFilterStream<'a> { +impl std::fmt::Debug for TraceFilterStream<'_> { /// Provides a debug representation of the `TraceFilterStream`. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("TraceFilterStream").finish_non_exhaustive() @@ -157,7 +157,7 @@ pub struct TraceGetStream<'a> { stream: Pin + 'a>>, } -impl<'a> Stream for TraceGetStream<'a> { +impl Stream for TraceGetStream<'_> { type Item = TraceGetResult; /// Attempts to pull out the next item of the stream @@ -166,7 +166,7 @@ impl<'a> Stream for TraceGetStream<'a> { } } -impl<'a> std::fmt::Debug for TraceGetStream<'a> { +impl std::fmt::Debug for TraceGetStream<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("TraceGetStream").finish_non_exhaustive() } @@ -180,7 +180,7 @@ pub struct CallManyTraceStream<'a> { stream: Pin + 'a>>, } -impl<'a> Stream for CallManyTraceStream<'a> { +impl Stream for CallManyTraceStream<'_> { type Item = CallManyTraceResult; /// Polls for the next item from the stream. @@ -189,7 +189,7 @@ impl<'a> Stream for CallManyTraceStream<'a> { } } -impl<'a> std::fmt::Debug for CallManyTraceStream<'a> { +impl std::fmt::Debug for CallManyTraceStream<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("CallManyTraceStream").finish() } @@ -201,7 +201,7 @@ pub struct RawTransactionTraceStream<'a> { stream: RawTransactionTraceResult<'a>, } -impl<'a> Stream for RawTransactionTraceStream<'a> { +impl Stream for RawTransactionTraceStream<'_> { type Item = Result<(TraceResults, Bytes), (RpcError, Bytes)>; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { @@ -209,7 +209,7 @@ impl<'a> Stream for RawTransactionTraceStream<'a> { } } -impl<'a> std::fmt::Debug for RawTransactionTraceStream<'a> { +impl std::fmt::Debug for RawTransactionTraceStream<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("RawTransactionTraceStream").finish() } @@ -221,7 +221,7 @@ pub struct ReplayTransactionStream<'a> { stream: Pin + 'a>>, } -impl<'a> Stream for ReplayTransactionStream<'a> { +impl Stream for ReplayTransactionStream<'_> { type Item = ReplayTransactionResult; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { @@ -229,7 +229,7 @@ impl<'a> Stream for ReplayTransactionStream<'a> { } } -impl<'a> std::fmt::Debug for ReplayTransactionStream<'a> { +impl std::fmt::Debug for ReplayTransactionStream<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("ReplayTransactionStream").finish() } @@ -393,7 +393,7 @@ impl<'a> TraceBlockStream<'a> { } } -impl<'a> Stream for TraceBlockStream<'a> { +impl Stream for TraceBlockStream<'_> { type Item = TraceBlockResult; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { @@ -401,7 +401,7 @@ impl<'a> Stream for TraceBlockStream<'a> { } } -impl<'a> std::fmt::Debug for TraceBlockStream<'a> { +impl std::fmt::Debug for TraceBlockStream<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("TraceBlockStream").finish_non_exhaustive() } diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index d7ee43720c19..a83ba68b42f5 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -208,8 +208,8 @@ where .ok_or(EthApiError::HeaderNotFound(block_id))?; let ((cfg, block_env, _), block) = futures::try_join!( - self.inner.eth_api.evm_env_at(block_hash.into()), - self.inner.eth_api.block_with_senders(block_id), + self.eth_api().evm_env_at(block_hash.into()), + self.eth_api().block_with_senders(block_id), )?; let block = block.ok_or(EthApiError::HeaderNotFound(block_id))?; @@ -235,11 +235,11 @@ where tx_hash: B256, opts: GethDebugTracingOptions, ) -> Result { - let (transaction, block) = match self.inner.eth_api.transaction_and_block(tx_hash).await? { + let (transaction, block) = match self.eth_api().transaction_and_block(tx_hash).await? { None => return Err(EthApiError::TransactionNotFound.into()), Some(res) => res, }; - let (cfg, block_env, _) = self.inner.eth_api.evm_env_at(block.hash().into()).await?; + let (cfg, block_env, _) = self.eth_api().evm_env_at(block.hash().into()).await?; // we need to get the state of the parent block because we're essentially replaying the // block the transaction is included in @@ -248,8 +248,7 @@ where let block_txs = block.into_transactions_ecrecovered(); let this = self.clone(); - self.inner - .eth_api + self.eth_api() .spawn_with_state_at_block(state_at, move |state| { // configure env for the target transaction let tx = transaction.into_recovered(); @@ -312,8 +311,7 @@ where GethDebugBuiltInTracerType::FourByteTracer => { let mut inspector = FourByteInspector::default(); let inspector = self - .inner - .eth_api + .eth_api() .spawn_with_call_at(call, at, overrides, move |db, env| { this.eth_api().inspect(db, env, &mut inspector)?; Ok(inspector) @@ -331,8 +329,7 @@ where ); let frame = self - .inner - .eth_api + .eth_api() .spawn_with_call_at(call, at, overrides, move |db, env| { let (res, env) = this.eth_api().inspect(db, env, &mut inspector)?; let frame = inspector @@ -353,8 +350,7 @@ where ); let frame = self - .inner - .eth_api + .eth_api() .spawn_with_call_at(call, at, overrides, move |db, env| { // wrapper is hack to get around 'higher-ranked lifetime error', // see @@ -434,11 +430,10 @@ where GethDebugTracerType::JsTracer(code) => { let config = tracer_config.into_json(); - let (_, _, at) = self.inner.eth_api.evm_env_at(at).await?; + let (_, _, at) = self.eth_api().evm_env_at(at).await?; let res = self - .inner - .eth_api + .eth_api() .spawn_with_call_at(call, at, overrides, move |db, env| { // wrapper is hack to get around 'higher-ranked lifetime error', see // @@ -464,8 +459,7 @@ where let mut inspector = TracingInspector::new(inspector_config); let (res, tx_gas_limit, inspector) = self - .inner - .eth_api + .eth_api() .spawn_with_call_at(call, at, overrides, move |db, env| { let (res, env) = this.eth_api().inspect(db, env, &mut inspector)?; Ok((res, env.tx.gas_limit, inspector)) @@ -499,14 +493,13 @@ where let target_block = block_number.unwrap_or_default(); let ((cfg, mut block_env, _), block) = futures::try_join!( - self.inner.eth_api.evm_env_at(target_block), - self.inner.eth_api.block_with_senders(target_block), + self.eth_api().evm_env_at(target_block), + self.eth_api().block_with_senders(target_block), )?; let opts = opts.unwrap_or_default(); let block = block.ok_or(EthApiError::HeaderNotFound(target_block))?; let GethDebugTracingCallOptions { tracing_options, mut state_overrides, .. } = opts; - let gas_limit = self.inner.eth_api.call_gas_limit(); // we're essentially replaying the transactions in the block here, hence we need the state // that points to the beginning of the block, which is the state at the parent block @@ -525,8 +518,7 @@ where let this = self.clone(); - self.inner - .eth_api + self.eth_api() .spawn_with_state_at_block(at.into(), move |state| { // the outer vec for the bundles let mut all_bundles = Vec::with_capacity(bundles.len()); @@ -547,7 +539,7 @@ where ), handler_cfg: cfg.handler_cfg, }; - let (res, _) = this.inner.eth_api.transact(&mut db, env)?; + let (res, _) = this.eth_api().transact(&mut db, env)?; db.commit(res.state); } } @@ -570,7 +562,6 @@ where cfg.clone(), block_env.clone(), tx, - gas_limit, &mut db, overrides, )?; @@ -603,28 +594,34 @@ where pub async fn debug_execution_witness( &self, block_id: BlockNumberOrTag, - include_preimages: bool, ) -> Result { let this = self.clone(); let block = this - .inner - .eth_api + .eth_api() .block_with_senders(block_id.into()) .await? .ok_or(EthApiError::HeaderNotFound(block_id.into()))?; - self.inner - .eth_api + self.eth_api() .spawn_with_state_at_block(block.parent_hash.into(), move |state_provider| { let db = StateProviderDatabase::new(&state_provider); let block_executor = this.inner.block_executor.executor(db); let mut hashed_state = HashedPostState::default(); let mut keys = HashMap::default(); + let mut codes = HashMap::default(); + let _ = block_executor .execute_with_state_witness( (&block.clone().unseal(), block.difficulty).into(), |statedb| { + codes = statedb + .cache + .contracts + .iter() + .map(|(hash, code)| (*hash, code.bytes())) + .collect(); + for (address, account) in &statedb.cache.accounts { let hashed_address = keccak256(address); hashed_state.accounts.insert( @@ -638,24 +635,14 @@ where ); if let Some(account) = &account.account { - if include_preimages { - keys.insert( - hashed_address, - alloy_rlp::encode(address).into(), - ); - } + keys.insert(hashed_address, address.to_vec().into()); for (slot, value) in &account.storage { let slot = B256::from(*slot); let hashed_slot = keccak256(slot); storage.storage.insert(hashed_slot, *value); - if include_preimages { - keys.insert( - hashed_slot, - alloy_rlp::encode(slot).into(), - ); - } + keys.insert(hashed_slot, slot.into()); } } } @@ -667,8 +654,8 @@ where state_provider.witness(Default::default(), hashed_state).map_err(Into::into)?; Ok(ExecutionWitness { state: HashMap::from_iter(state.into_iter()), - codes: Default::default(), - keys: include_preimages.then_some(keys), + codes, + keys: Some(keys), }) }) .await @@ -872,7 +859,7 @@ where /// /// Returns the bytes of the transaction for the given hash. async fn raw_transaction(&self, hash: B256) -> RpcResult> { - self.inner.eth_api.raw_transaction_by_hash(hash).await.map_err(Into::into) + self.eth_api().raw_transaction_by_hash(hash).await.map_err(Into::into) } /// Handler for `debug_getRawTransactions` @@ -966,10 +953,9 @@ where async fn debug_execution_witness( &self, block: BlockNumberOrTag, - include_preimages: bool, ) -> RpcResult { let _permit = self.acquire_trace_permit().await; - Self::debug_execution_witness(self, block, include_preimages).await.map_err(Into::into) + Self::debug_execution_witness(self, block).await.map_err(Into::into) } /// Handler for `debug_traceCall` diff --git a/crates/rpc/rpc/src/eth/bundle.rs b/crates/rpc/rpc/src/eth/bundle.rs index bede4599e1a1..e97497786ede 100644 --- a/crates/rpc/rpc/src/eth/bundle.rs +++ b/crates/rpc/rpc/src/eth/bundle.rs @@ -37,6 +37,11 @@ impl EthBundle { pub fn new(eth_api: Eth, blocking_task_guard: BlockingTaskGuard) -> Self { Self { inner: Arc::new(EthBundleInner { eth_api, blocking_task_guard }) } } + + /// Access the underlying `Eth` API. + pub fn eth_api(&self) -> &Eth { + &self.inner.eth_api + } } impl EthBundle @@ -103,7 +108,7 @@ where let block_id: alloy_rpc_types::BlockId = state_block_number.into(); // Note: the block number is considered the `parent` block: - let (cfg, mut block_env, at) = self.inner.eth_api.evm_env_at(block_id).await?; + let (cfg, mut block_env, at) = self.eth_api().evm_env_at(block_id).await?; // need to adjust the timestamp for the next block if let Some(timestamp) = timestamp { @@ -125,12 +130,12 @@ where } else if cfg.handler_cfg.spec_id.is_enabled_in(SpecId::LONDON) { let parent_block = block_env.number.saturating_to::(); // here we need to fetch the _next_ block's basefee based on the parent block - let parent = LoadPendingBlock::provider(&self.inner.eth_api) + let parent = LoadPendingBlock::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( - LoadPendingBlock::provider(&self.inner.eth_api) + LoadPendingBlock::provider(self.eth_api()) .chain_spec() .base_fee_params_at_block(parent_block), ) { @@ -142,10 +147,9 @@ where // use the block number of the request block_env.number = U256::from(block_number); - let eth_api = self.inner.eth_api.clone(); + let eth_api = self.eth_api().clone(); - self.inner - .eth_api + self.eth_api() .spawn_with_state_at_block(at, move |state| { let coinbase = block_env.coinbase; let basefee = Some(block_env.basefee.to::()); diff --git a/crates/rpc/rpc/src/eth/filter.rs b/crates/rpc/rpc/src/eth/filter.rs index 274347404c93..c5581f42a06c 100644 --- a/crates/rpc/rpc/src/eth/filter.rs +++ b/crates/rpc/rpc/src/eth/filter.rs @@ -24,9 +24,9 @@ use reth_provider::{BlockIdReader, BlockReader, EvmEnvProvider, ProviderError}; use reth_rpc_eth_api::{EthFilterApiServer, FullEthApiTypes, RpcTransaction, TransactionCompat}; use reth_rpc_eth_types::{ logs_utils::{self, append_matching_block_logs}, - EthApiError, EthFilterConfig, EthFilterError, EthStateCache, EthSubscriptionIdProvider, + EthApiError, EthFilterConfig, EthStateCache, EthSubscriptionIdProvider, }; -use reth_rpc_server_types::ToRpcResult; +use reth_rpc_server_types::{result::rpc_error_with_code, ToRpcResult}; use reth_rpc_types_compat::transaction::from_recovered; use reth_tasks::TaskSpawner; use reth_transaction_pool::{NewSubpoolTransactionStream, PoolTransaction, TransactionPool}; @@ -611,7 +611,7 @@ where /// Returns all new pending transactions received since the last poll. async fn drain(&self) -> FilterChanges where - T: PoolTransaction, + T: PoolTransaction>, { let mut pending_txs = Vec::new(); let mut prepared_stream = self.txs_stream.lock().await; @@ -633,7 +633,7 @@ trait FullTransactionsFilter: fmt::Debug + Send + Sync + Unpin + 'static { impl FullTransactionsFilter for FullTransactionsReceiver where - T: PoolTransaction + 'static, + T: PoolTransaction> + 'static, TxCompat: TransactionCompat + 'static, { async fn drain(&self) -> FilterChanges { @@ -695,6 +695,55 @@ impl Iterator for BlockRangeInclusiveIter { } } +/// Errors that can occur in the handler implementation +#[derive(Debug, thiserror::Error)] +pub enum EthFilterError { + /// Filter not found. + #[error("filter not found")] + FilterNotFound(FilterId), + /// Invalid block range. + #[error("invalid block range params")] + InvalidBlockRangeParams, + /// Query scope is too broad. + #[error("query exceeds max block range {0}")] + QueryExceedsMaxBlocks(u64), + /// Query result is too large. + #[error("query exceeds max results {0}")] + QueryExceedsMaxResults(usize), + /// Error serving request in `eth_` namespace. + #[error(transparent)] + EthAPIError(#[from] EthApiError), + /// Error thrown when a spawned task failed to deliver a response. + #[error("internal filter error")] + InternalError, +} + +impl From for jsonrpsee::types::error::ErrorObject<'static> { + fn from(err: EthFilterError) -> Self { + match err { + EthFilterError::FilterNotFound(_) => rpc_error_with_code( + jsonrpsee::types::error::INVALID_PARAMS_CODE, + "filter not found", + ), + err @ EthFilterError::InternalError => { + rpc_error_with_code(jsonrpsee::types::error::INTERNAL_ERROR_CODE, err.to_string()) + } + EthFilterError::EthAPIError(err) => err.into(), + err @ (EthFilterError::InvalidBlockRangeParams | + EthFilterError::QueryExceedsMaxBlocks(_) | + EthFilterError::QueryExceedsMaxResults(_)) => { + rpc_error_with_code(jsonrpsee::types::error::INVALID_PARAMS_CODE, err.to_string()) + } + } + } +} + +impl From for EthFilterError { + fn from(err: ProviderError) -> Self { + Self::EthAPIError(err.into()) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/rpc/rpc/src/eth/helpers/signer.rs b/crates/rpc/rpc/src/eth/helpers/signer.rs index b5109d09017a..a5818aa494fd 100644 --- a/crates/rpc/rpc/src/eth/helpers/signer.rs +++ b/crates/rpc/rpc/src/eth/helpers/signer.rs @@ -3,7 +3,6 @@ use std::collections::HashMap; use crate::EthApi; -use alloy_consensus::TxEnvelope; use alloy_dyn_abi::TypedData; use alloy_eips::eip2718::Decodable2718; use alloy_network::{eip2718::Encodable2718, EthereumWallet, TransactionBuilder}; @@ -91,7 +90,7 @@ impl EthSigner for DevSigner { let wallet = EthereumWallet::from(signer); // build and sign transaction with signer - let txn_envelope: TxEnvelope = + let txn_envelope = request.build(&wallet).await.map_err(|_| SignError::InvalidTransactionRequest)?; // decode transaction into signed transaction type diff --git a/crates/rpc/rpc/src/eth/helpers/state.rs b/crates/rpc/rpc/src/eth/helpers/state.rs index 006a0192f73a..8a35842798bc 100644 --- a/crates/rpc/rpc/src/eth/helpers/state.rs +++ b/crates/rpc/rpc/src/eth/helpers/state.rs @@ -48,7 +48,8 @@ mod tests { use alloy_primitives::{Address, StorageKey, StorageValue, U256}; use reth_chainspec::MAINNET; use reth_evm_ethereum::EthEvmConfig; - use reth_primitives::{constants::ETHEREUM_BLOCK_GAS_LIMIT, KECCAK_EMPTY}; + use reth_network_api::noop::NoopNetwork; + use reth_primitives::constants::ETHEREUM_BLOCK_GAS_LIMIT; use reth_provider::test_utils::{ExtendedAccount, MockEthProvider, NoopProvider}; use reth_rpc_eth_api::helpers::EthState; use reth_rpc_eth_types::{ @@ -61,7 +62,7 @@ mod tests { use reth_transaction_pool::test_utils::{testing_pool, TestPool}; use std::collections::HashMap; - fn noop_eth_api() -> EthApi { + fn noop_eth_api() -> EthApi { let pool = testing_pool(); let evm_config = EthEvmConfig::new(MAINNET.clone()); @@ -70,7 +71,7 @@ mod tests { EthApi::new( NoopProvider::default(), pool, - (), + NoopNetwork::default(), cache.clone(), GasPriceOracle::new(NoopProvider::default(), Default::default(), cache.clone()), ETHEREUM_BLOCK_GAS_LIMIT, @@ -102,7 +103,7 @@ mod tests { GasPriceOracle::new(mock_provider, Default::default(), cache.clone()), ETHEREUM_BLOCK_GAS_LIMIT, DEFAULT_MAX_SIMULATE_BLOCKS, - DEFAULT_ETH_PROOF_WINDOW, + DEFAULT_ETH_PROOF_WINDOW + 1, BlockingTaskPool::build().expect("failed to build tracing pool"), FeeHistoryCache::new(cache, FeeHistoryCacheConfig::default()), evm_config, @@ -139,16 +140,4 @@ mod tests { let account = eth_api.get_account(address, Default::default()).await.unwrap(); assert!(account.is_none()); } - - #[tokio::test] - async fn test_get_account_empty() { - let address = Address::random(); - let accounts = HashMap::from([(address, ExtendedAccount::new(0, U256::ZERO))]); - let eth_api = mock_eth_api(accounts); - - let account = eth_api.get_account(address, Default::default()).await.unwrap(); - let expected_account = - alloy_rpc_types::Account { code_hash: KECCAK_EMPTY, ..Default::default() }; - assert_eq!(Some(expected_account), account); - } } diff --git a/crates/rpc/rpc/src/otterscan.rs b/crates/rpc/rpc/src/otterscan.rs index 35972d9136b9..31db343a104f 100644 --- a/crates/rpc/rpc/src/otterscan.rs +++ b/crates/rpc/rpc/src/otterscan.rs @@ -106,11 +106,10 @@ where value: op.value, r#type: match op.kind { TransferKind::Call => OperationType::OpTransfer, - TransferKind::Create | TransferKind::EofCreate => { - OperationType::OpCreate - } + TransferKind::Create => OperationType::OpCreate, TransferKind::Create2 => OperationType::OpCreate2, TransferKind::SelfDestruct => OperationType::OpSelfDestruct, + TransferKind::EofCreate => OperationType::OpEofCreate, }, }) .collect::>() diff --git a/crates/rpc/rpc/src/trace.rs b/crates/rpc/rpc/src/trace.rs index 185383d811b7..687762b74b5e 100644 --- a/crates/rpc/rpc/src/trace.rs +++ b/crates/rpc/rpc/src/trace.rs @@ -120,7 +120,7 @@ where ) -> Result { let tx = recover_raw_transaction(tx)?; - let (cfg, block, at) = self.inner.eth_api.evm_env_at(block_id.unwrap_or_default()).await?; + let (cfg, block, at) = self.eth_api().evm_env_at(block_id.unwrap_or_default()).await?; let env = EnvWithHandlerCfg::new_with_cfg_env( cfg, @@ -130,8 +130,7 @@ where let config = TracingInspectorConfig::from_parity_config(&trace_types); - self.inner - .eth_api + self.eth_api() .spawn_trace_at_with_state(env, config, at, move |inspector, res, db| { inspector .into_parity_builder() @@ -151,9 +150,8 @@ where block_id: Option, ) -> Result, Eth::Error> { let at = block_id.unwrap_or(BlockId::pending()); - let (cfg, block_env, at) = self.inner.eth_api.evm_env_at(at).await?; + let (cfg, block_env, at) = self.eth_api().evm_env_at(at).await?; - let gas_limit = self.inner.eth_api.call_gas_limit(); let this = self.clone(); // execute all transactions on top of each other and record the traces self.eth_api() @@ -168,7 +166,6 @@ where cfg.clone(), block_env.clone(), call, - gas_limit, &mut db, Default::default(), )?; @@ -204,8 +201,7 @@ where trace_types: HashSet, ) -> Result { let config = TracingInspectorConfig::from_parity_config(&trace_types); - self.inner - .eth_api + self.eth_api() .spawn_trace_transaction_in_block(hash, config, move |_, inspector, res, db| { let trace_res = inspector .into_parity_builder() @@ -287,7 +283,7 @@ where let mut block_traces = Vec::with_capacity(blocks.len()); for block in &blocks { let matcher = matcher.clone(); - let traces = self.inner.eth_api.trace_block_until( + let traces = self.eth_api().trace_block_until( block.number.into(), None, TracingInspectorConfig::default_parity(), @@ -347,8 +343,7 @@ where &self, hash: B256, ) -> Result>, Eth::Error> { - self.inner - .eth_api + self.eth_api() .spawn_trace_transaction_in_block( hash, TracingInspectorConfig::default_parity(), @@ -366,7 +361,7 @@ where &self, block_id: BlockId, ) -> Result>, Eth::Error> { - let traces = self.inner.eth_api.trace_block_with( + let traces = self.eth_api().trace_block_with( block_id, TracingInspectorConfig::default_parity(), |tx_info, inspector, _, _, _| { @@ -376,7 +371,7 @@ where }, ); - let block = self.inner.eth_api.block(block_id); + let block = self.eth_api().block(block_id); let (maybe_traces, maybe_block) = futures::try_join!(traces, block)?; let mut maybe_traces = @@ -401,8 +396,7 @@ where block_id: BlockId, trace_types: HashSet, ) -> Result>, Eth::Error> { - self.inner - .eth_api + self.eth_api() .trace_block_with( block_id, TracingInspectorConfig::from_parity_config(&trace_types), @@ -433,8 +427,7 @@ where &self, tx_hash: B256, ) -> Result, Eth::Error> { - self.inner - .eth_api + self.eth_api() .spawn_trace_transaction_in_block_with_inspector( tx_hash, OpcodeGasInspector::default(), @@ -458,8 +451,7 @@ where block_id: BlockId, ) -> Result, Eth::Error> { let res = self - .inner - .eth_api + .eth_api() .trace_block_inspector( block_id, OpcodeGasInspector::default, @@ -475,7 +467,7 @@ where let Some(transactions) = res else { return Ok(None) }; - let Some(block) = self.inner.eth_api.block(block_id).await? else { return Ok(None) }; + let Some(block) = self.eth_api().block(block_id).await? else { return Ok(None) }; Ok(Some(BlockOpcodeGas { block_hash: block.hash(), diff --git a/crates/rpc/rpc/src/txpool.rs b/crates/rpc/rpc/src/txpool.rs index 4932a563409e..5e26935ca1ba 100644 --- a/crates/rpc/rpc/src/txpool.rs +++ b/crates/rpc/rpc/src/txpool.rs @@ -41,12 +41,12 @@ where tx: &Tx, content: &mut BTreeMap>, ) where - Tx: PoolTransaction, + Tx: PoolTransaction>, RpcTxB: TransactionCompat, { content.entry(tx.sender()).or_default().insert( tx.nonce().to_string(), - from_recovered::(tx.clone().into_consensus()), + from_recovered::(tx.clone().into_consensus().into()), ); } @@ -91,12 +91,12 @@ where trace!(target: "rpc::eth", "Serving txpool_inspect"); #[inline] - fn insert>( + fn insert>>( tx: &T, inspect: &mut BTreeMap>, ) { let entry = inspect.entry(tx.sender()).or_default(); - let tx = tx.clone().into_consensus(); + let tx: TransactionSignedEcRecovered = tx.clone().into_consensus().into(); entry.insert( tx.nonce().to_string(), TxpoolInspectSummary { diff --git a/crates/storage/db-api/src/cursor.rs b/crates/storage/db-api/src/cursor.rs index 134819a8e2cd..585aa4947a28 100644 --- a/crates/storage/db-api/src/cursor.rs +++ b/crates/storage/db-api/src/cursor.rs @@ -149,7 +149,7 @@ where } } -impl<'cursor, T: Table, CURSOR: DbCursorRO> Iterator for Walker<'cursor, T, CURSOR> { +impl> Iterator for Walker<'_, T, CURSOR> { type Item = Result, DatabaseError>; fn next(&mut self) -> Option { let start = self.start.take(); @@ -174,7 +174,7 @@ impl<'cursor, T: Table, CURSOR: DbCursorRO> Walker<'cursor, T, CURSOR> { } } -impl<'cursor, T: Table, CURSOR: DbCursorRW + DbCursorRO> Walker<'cursor, T, CURSOR> { +impl + DbCursorRO> Walker<'_, T, CURSOR> { /// Delete current item that walker points to. pub fn delete_current(&mut self) -> Result<(), DatabaseError> { self.start.take(); @@ -217,7 +217,7 @@ impl<'cursor, T: Table, CURSOR: DbCursorRO> ReverseWalker<'cursor, T, CURSOR> } } -impl<'cursor, T: Table, CURSOR: DbCursorRW + DbCursorRO> ReverseWalker<'cursor, T, CURSOR> { +impl + DbCursorRO> ReverseWalker<'_, T, CURSOR> { /// Delete current item that walker points to. pub fn delete_current(&mut self) -> Result<(), DatabaseError> { self.start.take(); @@ -225,7 +225,7 @@ impl<'cursor, T: Table, CURSOR: DbCursorRW + DbCursorRO> ReverseWalker<'cu } } -impl<'cursor, T: Table, CURSOR: DbCursorRO> Iterator for ReverseWalker<'cursor, T, CURSOR> { +impl> Iterator for ReverseWalker<'_, T, CURSOR> { type Item = Result, DatabaseError>; fn next(&mut self) -> Option { @@ -266,7 +266,7 @@ where } } -impl<'cursor, T: Table, CURSOR: DbCursorRO> Iterator for RangeWalker<'cursor, T, CURSOR> { +impl> Iterator for RangeWalker<'_, T, CURSOR> { type Item = Result, DatabaseError>; fn next(&mut self) -> Option { @@ -316,7 +316,7 @@ impl<'cursor, T: Table, CURSOR: DbCursorRO> RangeWalker<'cursor, T, CURSOR> { } } -impl<'cursor, T: Table, CURSOR: DbCursorRW + DbCursorRO> RangeWalker<'cursor, T, CURSOR> { +impl + DbCursorRO> RangeWalker<'_, T, CURSOR> { /// Delete current item that walker points to. pub fn delete_current(&mut self) -> Result<(), DatabaseError> { self.start.take(); @@ -349,7 +349,7 @@ where } } -impl<'cursor, T: DupSort, CURSOR: DbCursorRW + DbDupCursorRO> DupWalker<'cursor, T, CURSOR> { +impl + DbDupCursorRO> DupWalker<'_, T, CURSOR> { /// Delete current item that walker points to. pub fn delete_current(&mut self) -> Result<(), DatabaseError> { self.start.take(); @@ -357,7 +357,7 @@ impl<'cursor, T: DupSort, CURSOR: DbCursorRW + DbDupCursorRO> DupWalker<'c } } -impl<'cursor, T: DupSort, CURSOR: DbDupCursorRO> Iterator for DupWalker<'cursor, T, CURSOR> { +impl> Iterator for DupWalker<'_, T, CURSOR> { type Item = Result, DatabaseError>; fn next(&mut self) -> Option { let start = self.start.take(); diff --git a/crates/storage/db/Cargo.toml b/crates/storage/db/Cargo.toml index ba012cf68af1..a075f7724637 100644 --- a/crates/storage/db/Cargo.toml +++ b/crates/storage/db/Cargo.toml @@ -50,6 +50,7 @@ derive_more.workspace = true paste.workspace = true rustc-hash = { workspace = true, optional = true } sysinfo = { version = "0.31", default-features = false, features = ["system"] } +parking_lot = { workspace = true, optional = true } # arbitrary utils strum = { workspace = true, features = ["derive"], optional = true } @@ -61,6 +62,7 @@ rand.workspace = true serde_json.workspace = true tempfile.workspace = true test-fuzz.workspace = true +parking_lot.workspace = true pprof = { workspace = true, features = [ "flamegraph", @@ -88,7 +90,7 @@ mdbx = [ "dep:strum", "dep:rustc-hash", ] -test-utils = ["dep:tempfile", "arbitrary"] +test-utils = ["dep:tempfile", "arbitrary", "parking_lot"] bench = [] arbitrary = ["reth-primitives/arbitrary", "reth-db-api/arbitrary"] optimism = [] diff --git a/crates/storage/db/src/implementation/mdbx/tx.rs b/crates/storage/db/src/implementation/mdbx/tx.rs index 8feb6c90ab27..2ff2789ea698 100644 --- a/crates/storage/db/src/implementation/mdbx/tx.rs +++ b/crates/storage/db/src/implementation/mdbx/tx.rs @@ -180,7 +180,12 @@ struct MetricsHandler { /// If `true`, the backtrace of transaction has already been recorded and logged. /// See [`MetricsHandler::log_backtrace_on_long_read_transaction`]. backtrace_recorded: AtomicBool, + /// Shared database environment metrics. env_metrics: Arc, + /// Backtrace of the location where the transaction has been opened. Reported only with debug + /// assertions, because capturing the backtrace on every transaction opening is expensive. + #[cfg(debug_assertions)] + open_backtrace: Backtrace, _marker: PhantomData, } @@ -193,6 +198,8 @@ impl MetricsHandler { close_recorded: false, record_backtrace: true, backtrace_recorded: AtomicBool::new(false), + #[cfg(debug_assertions)] + open_backtrace: Backtrace::force_capture(), env_metrics, _marker: PhantomData, } @@ -232,11 +239,22 @@ impl MetricsHandler { let open_duration = self.start.elapsed(); if open_duration >= self.long_transaction_duration { self.backtrace_recorded.store(true, Ordering::Relaxed); + #[cfg(debug_assertions)] + let message = format!( + "The database read transaction has been open for too long. Open backtrace:\n{}\n\nCurrent backtrace:\n{}", + self.open_backtrace, + Backtrace::force_capture() + ); + #[cfg(not(debug_assertions))] + let message = format!( + "The database read transaction has been open for too long. Backtrace:\n{}", + Backtrace::force_capture() + ); warn!( target: "storage::db::mdbx", ?open_duration, %self.txn_id, - "The database read transaction has been open for too long. Backtrace:\n{}", Backtrace::force_capture() + "{message}" ); } } diff --git a/crates/storage/db/src/lib.rs b/crates/storage/db/src/lib.rs index a9f073d7b546..7090b4262fd7 100644 --- a/crates/storage/db/src/lib.rs +++ b/crates/storage/db/src/lib.rs @@ -44,6 +44,7 @@ pub use reth_db_api::*; pub mod test_utils { use super::*; use crate::mdbx::DatabaseArguments; + use parking_lot::RwLock; use reth_db_api::{ database::Database, database_metrics::{DatabaseMetadata, DatabaseMetadataValue, DatabaseMetrics}, @@ -52,6 +53,7 @@ pub mod test_utils { use reth_fs_util; use reth_libmdbx::MaxReadTransactionDuration; use std::{ + fmt::Formatter, path::{Path, PathBuf}, sync::Arc, }; @@ -69,10 +71,19 @@ pub mod test_utils { pub const ERROR_TEMPDIR: &str = "Not able to create a temporary directory."; /// A database will delete the db dir when dropped. - #[derive(Debug)] pub struct TempDatabase { db: Option, path: PathBuf, + /// Executed right before a database transaction is created. + pre_tx_hook: RwLock>, + /// Executed right after a database transaction is created. + post_tx_hook: RwLock>, + } + + impl std::fmt::Debug for TempDatabase { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("TempDatabase").field("db", &self.db).field("path", &self.path).finish() + } } impl Drop for TempDatabase { @@ -85,6 +96,16 @@ pub mod test_utils { } impl TempDatabase { + /// Create new [`TempDatabase`] instance. + pub fn new(db: DB, path: PathBuf) -> Self { + Self { + db: Some(db), + path, + pre_tx_hook: RwLock::new(Box::new(|| ())), + post_tx_hook: RwLock::new(Box::new(|| ())), + } + } + /// Returns the reference to inner db. pub fn db(&self) -> &DB { self.db.as_ref().unwrap() @@ -99,13 +120,28 @@ pub mod test_utils { pub fn into_inner_db(mut self) -> DB { self.db.take().unwrap() // take out db to avoid clean path in drop fn } + + /// Sets [`TempDatabase`] new pre transaction creation hook. + pub fn set_pre_transaction_hook(&self, hook: Box) { + let mut db_hook = self.pre_tx_hook.write(); + *db_hook = hook; + } + + /// Sets [`TempDatabase`] new post transaction creation hook. + pub fn set_post_transaction_hook(&self, hook: Box) { + let mut db_hook = self.post_tx_hook.write(); + *db_hook = hook; + } } impl Database for TempDatabase { type TX = ::TX; type TXMut = ::TXMut; fn tx(&self) -> Result { - self.db().tx() + self.pre_tx_hook.read()(); + let tx = self.db().tx()?; + self.post_tx_hook.read()(); + Ok(tx) } fn tx_mut(&self) -> Result { @@ -150,7 +186,7 @@ pub mod test_utils { ) .expect(&emsg); - Arc::new(TempDatabase { db: Some(db), path }) + Arc::new(TempDatabase::new(db, path)) } /// Create read/write database for testing @@ -162,7 +198,7 @@ pub mod test_utils { .with_max_read_transaction_duration(Some(MaxReadTransactionDuration::Unbounded)), ) .expect(ERROR_DB_CREATION); - Arc::new(TempDatabase { db: Some(db), path }) + Arc::new(TempDatabase::new(db, path)) } /// Create read only database for testing @@ -175,7 +211,7 @@ pub mod test_utils { init_db(path.as_path(), args.clone()).expect(ERROR_DB_CREATION); } let db = open_db_read_only(path.as_path(), args).expect(ERROR_DB_OPEN); - Arc::new(TempDatabase { db: Some(db), path }) + Arc::new(TempDatabase::new(db, path)) } } diff --git a/crates/storage/db/src/static_file/cursor.rs b/crates/storage/db/src/static_file/cursor.rs index f14e02308773..a9eadd9c12a6 100644 --- a/crates/storage/db/src/static_file/cursor.rs +++ b/crates/storage/db/src/static_file/cursor.rs @@ -115,7 +115,7 @@ impl<'a> From<&'a B256> for KeyOrNumber<'a> { } } -impl<'a> From for KeyOrNumber<'a> { +impl From for KeyOrNumber<'_> { fn from(value: u64) -> Self { KeyOrNumber::Number(value) } diff --git a/crates/storage/libmdbx-rs/src/codec.rs b/crates/storage/libmdbx-rs/src/codec.rs index a97ea28ca2ae..c78f79db9f94 100644 --- a/crates/storage/libmdbx-rs/src/codec.rs +++ b/crates/storage/libmdbx-rs/src/codec.rs @@ -22,7 +22,7 @@ pub trait TableObject: Sized { } } -impl<'tx> TableObject for Cow<'tx, [u8]> { +impl TableObject for Cow<'_, [u8]> { fn decode(_: &[u8]) -> Result { unreachable!() } diff --git a/crates/storage/libmdbx-rs/src/cursor.rs b/crates/storage/libmdbx-rs/src/cursor.rs index d007cc03e490..3deff0c249bc 100644 --- a/crates/storage/libmdbx-rs/src/cursor.rs +++ b/crates/storage/libmdbx-rs/src/cursor.rs @@ -539,7 +539,7 @@ where }, } -impl<'cur, K, Key, Value> IntoIter<'cur, K, Key, Value> +impl IntoIter<'_, K, Key, Value> where K: TransactionKind, Key: TableObject, @@ -551,7 +551,7 @@ where } } -impl<'cur, K, Key, Value> Iterator for IntoIter<'cur, K, Key, Value> +impl Iterator for IntoIter<'_, K, Key, Value> where K: TransactionKind, Key: TableObject, @@ -646,7 +646,7 @@ where } } -impl<'cur, K, Key, Value> Iterator for Iter<'cur, K, Key, Value> +impl Iterator for Iter<'_, K, Key, Value> where K: TransactionKind, Key: TableObject, @@ -736,7 +736,7 @@ where } } -impl<'cur, K, Key, Value> fmt::Debug for IterDup<'cur, K, Key, Value> +impl fmt::Debug for IterDup<'_, K, Key, Value> where K: TransactionKind, Key: TableObject, diff --git a/crates/storage/nippy-jar/src/compression/zstd.rs b/crates/storage/nippy-jar/src/compression/zstd.rs index 494d79de52f6..500247d17677 100644 --- a/crates/storage/nippy-jar/src/compression/zstd.rs +++ b/crates/storage/nippy-jar/src/compression/zstd.rs @@ -266,13 +266,13 @@ mod dictionaries_serde { #[derive(Serialize, Deserialize, Deref)] pub(crate) struct ZstdDictionaries<'a>(Vec>); -impl<'a> std::fmt::Debug for ZstdDictionaries<'a> { +impl std::fmt::Debug for ZstdDictionaries<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("ZstdDictionaries").field("num", &self.len()).finish_non_exhaustive() } } -impl<'a> ZstdDictionaries<'a> { +impl ZstdDictionaries<'_> { #[cfg(test)] /// Creates [`ZstdDictionaries`]. pub(crate) fn new(raw: Vec) -> Self { @@ -321,7 +321,7 @@ pub(crate) enum ZstdDictionary<'a> { Loaded(DecoderDictionary<'a>), } -impl<'a> ZstdDictionary<'a> { +impl ZstdDictionary<'_> { /// Returns a reference to the expected `RawDictionary` pub(crate) const fn raw(&self) -> Option<&RawDictionary> { match self { @@ -339,7 +339,7 @@ impl<'a> ZstdDictionary<'a> { } } -impl<'de, 'a> Deserialize<'de> for ZstdDictionary<'a> { +impl<'de> Deserialize<'de> for ZstdDictionary<'_> { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, @@ -349,7 +349,7 @@ impl<'de, 'a> Deserialize<'de> for ZstdDictionary<'a> { } } -impl<'a> Serialize for ZstdDictionary<'a> { +impl Serialize for ZstdDictionary<'_> { fn serialize(&self, serializer: S) -> Result where S: Serializer, @@ -362,7 +362,7 @@ impl<'a> Serialize for ZstdDictionary<'a> { } #[cfg(test)] -impl<'a> PartialEq for ZstdDictionary<'a> { +impl PartialEq for ZstdDictionary<'_> { fn eq(&self, other: &Self) -> bool { if let (Self::Raw(a), Self::Raw(b)) = (self, &other) { return a == b diff --git a/crates/storage/nippy-jar/src/cursor.rs b/crates/storage/nippy-jar/src/cursor.rs index 7af55fd436e4..267764827299 100644 --- a/crates/storage/nippy-jar/src/cursor.rs +++ b/crates/storage/nippy-jar/src/cursor.rs @@ -18,7 +18,7 @@ pub struct NippyJarCursor<'a, H = ()> { row: u64, } -impl<'a, H: NippyJarHeader> std::fmt::Debug for NippyJarCursor<'a, H> { +impl std::fmt::Debug for NippyJarCursor<'_, H> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("NippyJarCursor").field("config", &self.jar).finish_non_exhaustive() } diff --git a/crates/storage/provider/src/providers/blockchain_provider.rs b/crates/storage/provider/src/providers/blockchain_provider.rs index f5f838293eb4..9d075015397f 100644 --- a/crates/storage/provider/src/providers/blockchain_provider.rs +++ b/crates/storage/provider/src/providers/blockchain_provider.rs @@ -14,21 +14,27 @@ use reth_chain_state::{ BlockState, CanonicalInMemoryState, ForkChoiceNotifications, ForkChoiceSubscriptions, MemoryOverlayStateProvider, }; -use reth_chainspec::ChainInfo; +use reth_chainspec::{ChainInfo, EthereumHardforks}; +use reth_db::models::BlockNumberAddress; use reth_db_api::models::{AccountBeforeTx, StoredBlockBodyIndices}; use reth_evm::ConfigureEvmEnv; -use reth_execution_types::ExecutionOutcome; +use reth_execution_types::{BundleStateInit, ExecutionOutcome, RevertsInit}; use reth_node_types::NodeTypesWithDB; use reth_primitives::{ - Account, Block, BlockWithSenders, EthereumHardforks, Header, Receipt, SealedBlock, - SealedBlockWithSenders, SealedHeader, TransactionMeta, TransactionSigned, - TransactionSignedNoHash, Withdrawal, Withdrawals, + Account, Block, BlockWithSenders, Header, Receipt, SealedBlock, SealedBlockWithSenders, + SealedHeader, StorageEntry, TransactionMeta, TransactionSigned, TransactionSignedNoHash, + Withdrawal, Withdrawals, }; use reth_prune_types::{PruneCheckpoint, PruneSegment}; use reth_stages_types::{StageCheckpoint, StageId}; +use reth_storage_api::StorageChangeSetReader; use reth_storage_errors::provider::ProviderResult; -use revm::primitives::{BlockEnv, CfgEnvWithHandlerCfg}; +use revm::{ + db::states::PlainStorageRevert, + primitives::{BlockEnv, CfgEnvWithHandlerCfg}, +}; use std::{ + collections::{hash_map, HashMap}, ops::{Add, Bound, RangeBounds, RangeInclusive, Sub}, sync::Arc, time::Instant, @@ -122,6 +128,145 @@ impl BlockchainProvider2 { (start, end) } + /// Return the last N blocks of state, recreating the [`ExecutionOutcome`]. + /// + /// If the range is empty, or there are no blocks for the given range, then this returns `None`. + pub fn get_state( + &self, + range: RangeInclusive, + ) -> ProviderResult> { + if range.is_empty() { + return Ok(None) + } + let start_block_number = *range.start(); + let end_block_number = *range.end(); + + // We are not removing block meta as it is used to get block changesets. + let mut block_bodies = Vec::new(); + for block_num in range.clone() { + let block_body = self + .block_body_indices(block_num)? + .ok_or(ProviderError::BlockBodyIndicesNotFound(block_num))?; + block_bodies.push((block_num, block_body)) + } + + // get transaction receipts + let Some(from_transaction_num) = block_bodies.first().map(|body| body.1.first_tx_num()) + else { + return Ok(None) + }; + let Some(to_transaction_num) = block_bodies.last().map(|body| body.1.last_tx_num()) else { + return Ok(None) + }; + + let mut account_changeset = Vec::new(); + for block_num in range.clone() { + let changeset = + self.account_block_changeset(block_num)?.into_iter().map(|elem| (block_num, elem)); + account_changeset.extend(changeset); + } + + let mut storage_changeset = Vec::new(); + for block_num in range { + let changeset = self.storage_changeset(block_num)?; + storage_changeset.extend(changeset); + } + + let (state, reverts) = + self.populate_bundle_state(account_changeset, storage_changeset, end_block_number)?; + + let mut receipt_iter = + self.receipts_by_tx_range(from_transaction_num..=to_transaction_num)?.into_iter(); + + let mut receipts = Vec::with_capacity(block_bodies.len()); + // loop break if we are at the end of the blocks. + for (_, block_body) in block_bodies { + let mut block_receipts = Vec::with_capacity(block_body.tx_count as usize); + for tx_num in block_body.tx_num_range() { + let receipt = + receipt_iter.next().ok_or(ProviderError::ReceiptNotFound(tx_num.into()))?; + block_receipts.push(Some(receipt)); + } + receipts.push(block_receipts); + } + + Ok(Some(ExecutionOutcome::new_init( + state, + reverts, + // We skip new contracts since we never delete them from the database + Vec::new(), + receipts.into(), + start_block_number, + Vec::new(), + ))) + } + + /// Populate a [`BundleStateInit`] and [`RevertsInit`] using cursors over the + /// [`reth_db::PlainAccountState`] and [`reth_db::PlainStorageState`] tables, based on the given + /// storage and account changesets. + fn populate_bundle_state( + &self, + account_changeset: Vec<(u64, AccountBeforeTx)>, + storage_changeset: Vec<(BlockNumberAddress, StorageEntry)>, + block_range_end: BlockNumber, + ) -> ProviderResult<(BundleStateInit, RevertsInit)> { + let mut state: BundleStateInit = HashMap::new(); + let mut reverts: RevertsInit = HashMap::new(); + let state_provider = self.state_by_block_number_or_tag(block_range_end.into())?; + + // add account changeset changes + for (block_number, account_before) in account_changeset.into_iter().rev() { + let AccountBeforeTx { info: old_info, address } = account_before; + match state.entry(address) { + hash_map::Entry::Vacant(entry) => { + let new_info = state_provider.basic_account(address)?; + entry.insert((old_info, new_info, HashMap::new())); + } + hash_map::Entry::Occupied(mut entry) => { + // overwrite old account state. + entry.get_mut().0 = old_info; + } + } + // insert old info into reverts. + reverts.entry(block_number).or_default().entry(address).or_default().0 = Some(old_info); + } + + // add storage changeset changes + for (block_and_address, old_storage) in storage_changeset.into_iter().rev() { + let BlockNumberAddress((block_number, address)) = block_and_address; + // get account state or insert from plain state. + let account_state = match state.entry(address) { + hash_map::Entry::Vacant(entry) => { + let present_info = state_provider.basic_account(address)?; + entry.insert((present_info, present_info, HashMap::new())) + } + hash_map::Entry::Occupied(entry) => entry.into_mut(), + }; + + // match storage. + match account_state.2.entry(old_storage.key) { + hash_map::Entry::Vacant(entry) => { + let new_storage_value = + state_provider.storage(address, old_storage.key)?.unwrap_or_default(); + entry.insert((old_storage.value, new_storage_value)); + } + hash_map::Entry::Occupied(mut entry) => { + entry.get_mut().0 = old_storage.value; + } + }; + + reverts + .entry(block_number) + .or_default() + .entry(address) + .or_default() + .1 + .push(old_storage); + } + + Ok((state, reverts)) + } + /// Fetches a range of data from both in-memory state and persistent storage while a predicate /// is met. /// @@ -268,12 +413,10 @@ impl BlockchainProvider2 { // Get the last block number stored in the storage which does NOT overlap with in-memory // chain. - let mut last_database_block_number = provider.last_block_number()?; - if let Some(lowest_in_mem_block) = in_mem_chain.last() { - if lowest_in_mem_block.number() <= last_database_block_number { - last_database_block_number = lowest_in_mem_block.number().saturating_sub(1); - } - } + let last_database_block_number = in_mem_chain + .last() + .map(|b| Ok(b.anchor().number)) + .unwrap_or_else(|| provider.last_block_number())?; // Get the next tx number for the last block stored in the storage, which marks the start of // the in-memory state. @@ -320,11 +463,17 @@ impl BlockchainProvider2 { let block_tx_count = block_state.block_ref().block().body.transactions.len(); let remaining = (tx_range.end() - tx_range.start() + 1) as usize; - // This should only be more than 0 in the first iteration, in case of a partial range + // If the transaction range start is higher than this block last transaction, advance + if *tx_range.start() > in_memory_tx_num + block_tx_count as u64 - 1 { + in_memory_tx_num += block_tx_count as u64; + continue + } + + // This should only be more than 0 once, in case of a partial range inside a block. let skip = (tx_range.start() - in_memory_tx_num) as usize; items.extend(fetch_from_block_state( - skip..=(remaining.min(block_tx_count) - 1), + skip..=skip + (remaining.min(block_tx_count - skip) - 1), block_state, )?); @@ -361,12 +510,10 @@ impl BlockchainProvider2 { // Get the last block number stored in the database which does NOT overlap with in-memory // chain. - let mut last_database_block_number = provider.last_block_number()?; - if let Some(lowest_in_mem_block) = in_mem_chain.last() { - if lowest_in_mem_block.number() <= last_database_block_number { - last_database_block_number = lowest_in_mem_block.number().saturating_sub(1); - } - } + let last_database_block_number = in_mem_chain + .last() + .map(|b| Ok(b.anchor().number)) + .unwrap_or_else(|| provider.last_block_number())?; // Get the next tx number for the last block stored in the database and consider it the // first tx number of the in-memory state @@ -385,7 +532,7 @@ impl BlockchainProvider2 { // Iterate from the lowest block to the highest for block_state in in_mem_chain.into_iter().rev() { - let executed_block = block_state.block(); + let executed_block = block_state.block_ref(); let block = executed_block.block(); for tx_index in 0..block.body.transactions.len() { @@ -486,7 +633,7 @@ impl HeaderProvider for BlockchainProvider2 { self.get_in_memory_or_storage_by_block( (*block_hash).into(), |db_provider| db_provider.header(block_hash), - |block_state| Ok(Some(block_state.block().block().header.header().clone())), + |block_state| Ok(Some(block_state.block_ref().block().header.header().clone())), ) } @@ -494,7 +641,7 @@ impl HeaderProvider for BlockchainProvider2 { self.get_in_memory_or_storage_by_block( num.into(), |db_provider| db_provider.header_by_number(num), - |block_state| Ok(Some(block_state.block().block().header.header().clone())), + |block_state| Ok(Some(block_state.block_ref().block().header.header().clone())), ) } @@ -532,7 +679,7 @@ impl HeaderProvider for BlockchainProvider2 { self.get_in_memory_or_storage_by_block_range_while( range, |db_provider, range, _| db_provider.headers_range(range), - |block_state, _| Some(block_state.block().block().header.header().clone()), + |block_state, _| Some(block_state.block_ref().block().header.header().clone()), |_| true, ) } @@ -541,7 +688,7 @@ impl HeaderProvider for BlockchainProvider2 { self.get_in_memory_or_storage_by_block( number.into(), |db_provider| db_provider.sealed_header(number), - |block_state| Ok(Some(block_state.block().block().header.clone())), + |block_state| Ok(Some(block_state.block_ref().block().header.clone())), ) } @@ -552,7 +699,7 @@ impl HeaderProvider for BlockchainProvider2 { self.get_in_memory_or_storage_by_block_range_while( range, |db_provider, range, _| db_provider.sealed_headers_range(range), - |block_state, _| Some(block_state.block().block().header.clone()), + |block_state, _| Some(block_state.block_ref().block().header.clone()), |_| true, ) } @@ -566,7 +713,8 @@ impl HeaderProvider for BlockchainProvider2 { range, |db_provider, range, predicate| db_provider.sealed_headers_while(range, predicate), |block_state, predicate| { - Some(block_state.block().block().header.clone()).filter(|header| predicate(header)) + let header = &block_state.block_ref().block().header; + predicate(header).then(|| header.clone()) }, predicate, ) @@ -644,7 +792,7 @@ impl BlockReader for BlockchainProvider2 { self.get_in_memory_or_storage_by_block( hash.into(), |db_provider| db_provider.find_block_by_hash(hash, source), - |block_state| Ok(Some(block_state.block().block().clone().unseal())), + |block_state| Ok(Some(block_state.block_ref().block().clone().unseal())), ) } BlockSource::Pending => { @@ -657,7 +805,7 @@ impl BlockReader for BlockchainProvider2 { self.get_in_memory_or_storage_by_block( id, |db_provider| db_provider.block(id), - |block_state| Ok(Some(block_state.block().block().clone().unseal())), + |block_state| Ok(Some(block_state.block_ref().block().clone().unseal())), ) } @@ -687,7 +835,7 @@ impl BlockReader for BlockchainProvider2 { return Ok(Some(Vec::new())) } - Ok(Some(block_state.block().block().body.ommers.clone())) + Ok(Some(block_state.block_ref().block().body.ommers.clone())) }, ) } @@ -696,34 +844,34 @@ impl BlockReader for BlockchainProvider2 { &self, number: BlockNumber, ) -> ProviderResult> { - if let Some(indices) = self.database.block_body_indices(number)? { - Ok(Some(indices)) - } else if let Some(state) = self.canonical_in_memory_state.state_by_number(number) { - // we have to construct the stored indices for the in memory blocks - // - // To calculate this we will fetch the anchor block and walk forward from all parents - let mut parent_chain = state.parent_state_chain(); - parent_chain.reverse(); - let anchor_num = state.anchor().number; - let mut stored_indices = self - .database - .block_body_indices(anchor_num)? - .ok_or(ProviderError::BlockBodyIndicesNotFound(anchor_num))?; - stored_indices.first_tx_num = stored_indices.next_tx_num(); - - for state in parent_chain { - let txs = state.block().block.body.transactions.len() as u64; - if state.block().block().number == number { - stored_indices.tx_count = txs; - } else { - stored_indices.first_tx_num += txs; + self.get_in_memory_or_storage_by_block( + number.into(), + |db_provider| db_provider.block_body_indices(number), + |block_state| { + // Find the last block indices on database + let last_storage_block_number = block_state.anchor().number; + let mut stored_indices = self + .database + .block_body_indices(last_storage_block_number)? + .ok_or(ProviderError::BlockBodyIndicesNotFound(last_storage_block_number))?; + + // Prepare our block indices + stored_indices.first_tx_num = stored_indices.next_tx_num(); + stored_indices.tx_count = 0; + + // Iterate from the lowest block in memory until our target block + for state in block_state.chain().into_iter().rev() { + let block_tx_count = state.block_ref().block.body.transactions.len() as u64; + if state.block_ref().block().number == number { + stored_indices.tx_count = block_tx_count; + } else { + stored_indices.first_tx_num += block_tx_count; + } } - } - Ok(Some(stored_indices)) - } else { - Ok(None) - } + Ok(Some(stored_indices)) + }, + ) } /// Returns the block with senders with matching number or hash from database. @@ -760,7 +908,7 @@ impl BlockReader for BlockchainProvider2 { self.get_in_memory_or_storage_by_block_range_while( range, |db_provider, range, _| db_provider.block_range(range), - |block_state, _| Some(block_state.block().block().clone().unseal()), + |block_state, _| Some(block_state.block_ref().block().clone().unseal()), |_| true, ) } @@ -804,7 +952,7 @@ impl TransactionsProvider for BlockchainProvider2 { id.into(), |provider| provider.transaction_by_id(id), |tx_index, _, block_state| { - Ok(block_state.block().block().body.transactions.get(tx_index).cloned()) + Ok(block_state.block_ref().block().body.transactions.get(tx_index).cloned()) }, ) } @@ -818,7 +966,7 @@ impl TransactionsProvider for BlockchainProvider2 { |provider| provider.transaction_by_id_no_hash(id), |tx_index, _, block_state| { Ok(block_state - .block() + .block_ref() .block() .body .transactions @@ -854,7 +1002,7 @@ impl TransactionsProvider for BlockchainProvider2 { self.get_in_memory_or_storage_by_tx( id.into(), |provider| provider.transaction_block(id), - |_, _, block_state| Ok(Some(block_state.block().block().number)), + |_, _, block_state| Ok(Some(block_state.block_ref().block().number)), ) } @@ -865,7 +1013,7 @@ impl TransactionsProvider for BlockchainProvider2 { self.get_in_memory_or_storage_by_block( id, |provider| provider.transactions_by_block(id), - |block_state| Ok(Some(block_state.block().block().body.transactions.clone())), + |block_state| Ok(Some(block_state.block_ref().block().body.transactions.clone())), ) } @@ -873,35 +1021,12 @@ impl TransactionsProvider for BlockchainProvider2 { &self, range: impl RangeBounds, ) -> ProviderResult>> { - let (start, end) = self.convert_range_bounds(range, || { - self.canonical_in_memory_state.get_canonical_block_number() - }); - - let mut transactions = Vec::new(); - let mut last_in_memory_block = None; - - for number in start..=end { - if let Some(block_state) = self.canonical_in_memory_state.state_by_number(number) { - // TODO: there might be an update between loop iterations, we - // need to handle that situation. - transactions.push(block_state.block().block().body.transactions.clone()); - last_in_memory_block = Some(number); - } else { - break - } - } - - if let Some(last_block) = last_in_memory_block { - if last_block < end { - let mut db_transactions = - self.database.transactions_by_block_range((last_block + 1)..=end)?; - transactions.append(&mut db_transactions); - } - } else { - transactions = self.database.transactions_by_block_range(start..=end)?; - } - - Ok(transactions) + self.get_in_memory_or_storage_by_block_range_while( + range, + |db_provider, range, _| db_provider.transactions_by_block_range(range), + |block_state, _| Some(block_state.block_ref().block().body.transactions.clone()), + |_| true, + ) } fn transactions_by_tx_range( @@ -936,7 +1061,7 @@ impl TransactionsProvider for BlockchainProvider2 { self.get_in_memory_or_storage_by_tx( id.into(), |provider| provider.transaction_sender(id), - |tx_index, _, block_state| Ok(block_state.block().senders.get(tx_index).copied()), + |tx_index, _, block_state| Ok(block_state.block_ref().senders.get(tx_index).copied()), ) } } @@ -954,7 +1079,7 @@ impl ReceiptProvider for BlockchainProvider2 { fn receipt_by_hash(&self, hash: TxHash) -> ProviderResult> { for block_state in self.canonical_in_memory_state.canonical_chain() { - let executed_block = block_state.block(); + let executed_block = block_state.block_ref(); let block = executed_block.block(); let receipts = block_state.executed_block_receipts(); @@ -1041,7 +1166,7 @@ impl WithdrawalsProvider for BlockchainProvider2 { self.get_in_memory_or_storage_by_block( id, |db_provider| db_provider.withdrawals_by_block(id, timestamp), - |block_state| Ok(block_state.block().block().body.withdrawals.clone()), + |block_state| Ok(block_state.block_ref().block().body.withdrawals.clone()), ) } @@ -1052,7 +1177,13 @@ impl WithdrawalsProvider for BlockchainProvider2 { best_block_num.into(), |db_provider| db_provider.latest_withdrawal(), |block_state| { - Ok(block_state.block().block().body.withdrawals.clone().and_then(|mut w| w.pop())) + Ok(block_state + .block_ref() + .block() + .body + .withdrawals + .clone() + .and_then(|mut w| w.pop())) }, ) } @@ -1071,7 +1202,7 @@ impl RequestsProvider for BlockchainProvider2 { self.get_in_memory_or_storage_by_block( id, |db_provider| db_provider.requests_by_block(id, timestamp), - |block_state| Ok(block_state.block().block().body.requests.clone()), + |block_state| Ok(block_state.block_ref().block().body.requests.clone()), ) } } @@ -1440,6 +1571,57 @@ impl ForkChoiceSubscriptions for BlockchainProvider2 { } } +impl StorageChangeSetReader for BlockchainProvider2 { + fn storage_changeset( + &self, + block_number: BlockNumber, + ) -> ProviderResult> { + if let Some(state) = self.canonical_in_memory_state.state_by_number(block_number) { + let changesets = state + .block() + .execution_output + .bundle + .reverts + .clone() + .into_plain_state_reverts() + .storage + .into_iter() + .flatten() + .flat_map(|revert: PlainStorageRevert| { + revert.storage_revert.into_iter().map(move |(key, value)| { + ( + BlockNumberAddress((block_number, revert.address)), + StorageEntry { key: key.into(), value: value.to_previous_value() }, + ) + }) + }) + .collect(); + Ok(changesets) + } else { + // Perform checks on whether or not changesets exist for the block. + let provider = self.database.provider()?; + + // No prune checkpoint means history should exist and we should `unwrap_or(true)` + let storage_history_exists = provider + .get_prune_checkpoint(PruneSegment::StorageHistory)? + .and_then(|checkpoint| { + // return true if the block number is ahead of the prune checkpoint. + // + // The checkpoint stores the highest pruned block number, so we should make + // sure the block_number is strictly greater. + checkpoint.block_number.map(|checkpoint| block_number > checkpoint) + }) + .unwrap_or(true); + + if !storage_history_exists { + return Err(ProviderError::StateAtBlockPruned(block_number)) + } + + provider.storage_changeset(block_number) + } + } +} + impl ChangeSetReader for BlockchainProvider2 { fn account_block_changeset( &self, @@ -1447,7 +1629,7 @@ impl ChangeSetReader for BlockchainProvider2 { ) -> ProviderResult> { if let Some(state) = self.canonical_in_memory_state.state_by_number(block_number) { let changesets = state - .block() + .block_ref() .execution_output .bundle .reverts @@ -1460,7 +1642,25 @@ impl ChangeSetReader for BlockchainProvider2 { .collect(); Ok(changesets) } else { - self.database.provider()?.account_block_changeset(block_number) + // Perform checks on whether or not changesets exist for the block. + let provider = self.database.provider()?; + // No prune checkpoint means history should exist and we should `unwrap_or(true)` + let account_history_exists = provider + .get_prune_checkpoint(PruneSegment::AccountHistory)? + .and_then(|checkpoint| { + // return true if the block number is ahead of the prune checkpoint. + // + // The checkpoint stores the highest pruned block number, so we should make + // sure the block_number is strictly greater. + checkpoint.block_number.map(|checkpoint| block_number > checkpoint) + }) + .unwrap_or(true); + + if !account_history_exists { + return Err(ProviderError::StateAtBlockPruned(block_number)) + } + + provider.account_block_changeset(block_number) } } } @@ -1475,12 +1675,21 @@ impl AccountReader for BlockchainProvider2 { } impl StateReader for BlockchainProvider2 { + /// Re-constructs the [`ExecutionOutcome`] from in-memory and database state, if necessary. + /// + /// If data for the block does not exist, this will return [`None`]. + /// + /// NOTE: This cannot be called safely in a loop outside of the blockchain tree thread. This is + /// because the [`CanonicalInMemoryState`] could change during a reorg, causing results to be + /// inconsistent. Currently this can safely be called within the blockchain tree thread, + /// because the tree thread is responsible for modifying the [`CanonicalInMemoryState`] in the + /// first place. fn get_state(&self, block: BlockNumber) -> ProviderResult> { if let Some(state) = self.canonical_in_memory_state.state_by_number(block) { - let state = state.block().execution_outcome().clone(); + let state = state.block_ref().execution_outcome().clone(); Ok(Some(state)) } else { - self.database.provider()?.get_state(block..=block) + self.get_state(block..=block) } } } @@ -1500,24 +1709,30 @@ mod tests { MockNodeTypesWithDB, }, writer::UnifiedStorageWriter, - BlockWriter, CanonChainTracker, StaticFileProviderFactory, StaticFileWriter, + BlockWriter, CanonChainTracker, ProviderFactory, StaticFileProviderFactory, + StaticFileWriter, }; use alloy_eips::{BlockHashOrNumber, BlockNumHash, BlockNumberOrTag}; - use alloy_primitives::B256; + use alloy_primitives::{BlockNumber, B256}; use itertools::Itertools; use rand::Rng; use reth_chain_state::{ test_utils::TestBlockBuilder, CanonStateNotification, CanonStateSubscriptions, - ExecutedBlock, NewCanonicalChain, + CanonicalInMemoryState, ExecutedBlock, NewCanonicalChain, }; use reth_chainspec::{ ChainSpec, ChainSpecBuilder, ChainSpecProvider, EthereumHardfork, MAINNET, }; - use reth_db::models::{AccountBeforeTx, StoredBlockBodyIndices}; + use reth_db::{ + models::{AccountBeforeTx, StoredBlockBodyIndices}, + tables, + }; + use reth_db_api::{cursor::DbCursorRO, transaction::DbTx}; + use reth_errors::ProviderError; use reth_execution_types::{Chain, ExecutionOutcome}; use reth_primitives::{ - BlockWithSenders, Receipt, SealedBlock, SealedBlockWithSenders, StaticFileSegment, - TransactionMeta, TransactionSignedNoHash, Withdrawals, + Receipt, SealedBlock, StaticFileSegment, TransactionMeta, TransactionSignedNoHash, + Withdrawals, }; use reth_storage_api::{ BlockHashReader, BlockIdReader, BlockNumReader, BlockReader, BlockReaderIdExt, BlockSource, @@ -1600,9 +1815,27 @@ mod tests { let factory = create_test_provider_factory_with_chain_spec(chain_spec); let provider_rw = factory.database_provider_rw()?; + let static_file_provider = factory.static_file_provider(); + + // Write transactions to static files with the right `tx_num`` + let mut bodies_cursor = provider_rw.tx_ref().cursor_read::()?; + let mut tx_num = bodies_cursor + .seek_exact(database_blocks.first().as_ref().unwrap().number.saturating_sub(1))? + .map(|(_, indices)| indices.next_tx_num()) + .unwrap_or_default(); // Insert blocks into the database for block in &database_blocks { + // TODO: this should be moved inside `insert_historical_block`: + let mut transactions_writer = + static_file_provider.latest_writer(StaticFileSegment::Transactions)?; + transactions_writer.increment_block(block.number)?; + for tx in block.body.transactions() { + let tx: TransactionSignedNoHash = tx.clone().into(); + transactions_writer.append_transaction(tx_num, &tx)?; + tx_num += 1; + } + provider_rw.insert_historical_block( block.clone().seal_with_senders().expect("failed to seal block with senders"), )?; @@ -1616,7 +1849,9 @@ mod tests { .append_receipts_from_blocks( // The initial block number is required database_blocks.first().map(|b| b.number).unwrap_or_default(), - receipts.iter().map(|vec| vec.clone().into_iter().map(Some).collect::>()), + receipts[..database_blocks.len()] + .iter() + .map(|vec| vec.clone().into_iter().map(Some).collect::>()), )?; // Commit to both storages: database and static files @@ -1682,6 +1917,42 @@ mod tests { ) } + /// This will persist the last block in-memory and delete it from + /// `canonical_in_memory_state` right after a database read transaction is created. + /// + /// This simulates a RPC method having a different view than when its database transaction was + /// created. + fn persist_block_after_db_tx_creation( + provider: Arc>, + block_number: BlockNumber, + ) { + let hook_provider = provider.clone(); + provider.database.db_ref().set_post_transaction_hook(Box::new(move || { + if let Some(state) = hook_provider.canonical_in_memory_state.head_state() { + if state.anchor().number + 1 == block_number { + let mut lowest_memory_block = + state.parent_state_chain().last().expect("qed").block(); + let num_hash = lowest_memory_block.block().num_hash(); + + let mut execution_output = (*lowest_memory_block.execution_output).clone(); + execution_output.first_block = lowest_memory_block.block().number; + lowest_memory_block.execution_output = Arc::new(execution_output); + + // Push to disk + let provider_rw = hook_provider.database_provider_rw().unwrap(); + UnifiedStorageWriter::from(&provider_rw, &hook_provider.static_file_provider()) + .save_blocks(&[lowest_memory_block]) + .unwrap(); + UnifiedStorageWriter::commit(provider_rw, hook_provider.static_file_provider()) + .unwrap(); + + // Remove from memory + hook_provider.canonical_in_memory_state.remove_persisted_blocks(num_hash); + } + } + })); + } + #[test] fn test_block_reader_find_block_by_hash() -> eyre::Result<()> { // Initialize random number generator and provider factory @@ -2264,459 +2535,6 @@ mod tests { Ok(()) } - #[test] - fn test_block_range_in_memory_only() -> eyre::Result<()> { - let mut rng = generators::rng(); - let (provider, _, in_memory_blocks, _) = provider_with_random_blocks( - &mut rng, - TEST_BLOCKS_COUNT, - TEST_BLOCKS_COUNT, - BlockRangeParams::default(), - )?; - - // Get the range of in-memory blocks - let start_block_number = in_memory_blocks.first().unwrap().number; - let end_block_number = in_memory_blocks.last().unwrap().number; - - let range = start_block_number..=end_block_number; - let blocks = provider.block_range(range)?; - - // Check if the retrieved blocks match the in-memory blocks - assert_eq!(blocks.len(), in_memory_blocks.len()); - // Check if the blocks are equal - for (retrieved_block, expected_block) in blocks.iter().zip(in_memory_blocks.iter()) { - assert_eq!(retrieved_block, &expected_block.clone().unseal()); - } - - // Check for partial in-memory ranges - let blocks = provider.block_range(start_block_number + 1..=end_block_number)?; - assert_eq!(blocks.len(), in_memory_blocks.len() - 1); - for (retrieved_block, expected_block) in blocks.iter().zip(in_memory_blocks.iter().skip(1)) - { - assert_eq!(retrieved_block, &expected_block.clone().unseal()); - } - - let blocks = provider.block_range(start_block_number + 1..=end_block_number - 1)?; - assert_eq!(blocks.len(), in_memory_blocks.len() - 2); - for (retrieved_block, expected_block) in blocks.iter().zip(in_memory_blocks.iter().skip(1)) - { - assert_eq!(retrieved_block, &expected_block.clone().unseal()); - } - - let blocks = provider.block_range(start_block_number + 1..=end_block_number + 1)?; - assert_eq!(blocks.len(), in_memory_blocks.len() - 1); - for (retrieved_block, expected_block) in blocks.iter().zip(in_memory_blocks.iter().skip(1)) - { - assert_eq!(retrieved_block, &expected_block.clone().unseal()); - } - - Ok(()) - } - - #[test] - fn test_block_range_in_database_only() -> eyre::Result<()> { - let mut rng = generators::rng(); - let (provider, database_blocks, _, _) = provider_with_random_blocks( - &mut rng, - TEST_BLOCKS_COUNT, - 0, // No blocks in memory - BlockRangeParams::default(), - )?; - - // Get the range of database blocks - let start_block_number = database_blocks.first().unwrap().number; - let end_block_number = database_blocks.last().unwrap().number; - - let range = start_block_number..=end_block_number; - let blocks = provider.block_range(range)?; - - // Check if the retrieved blocks match the database blocks - assert_eq!(blocks.len(), database_blocks.len()); - // Check if the blocks are equal - for (retrieved_block, expected_block) in blocks.iter().zip(database_blocks.iter()) { - assert_eq!(retrieved_block, &expected_block.clone().unseal()); - } - - Ok(()) - } - - #[test] - fn test_block_range_across_memory_and_database() -> eyre::Result<()> { - let mut rng = generators::rng(); - let mid_point = TEST_BLOCKS_COUNT / 2; - let (provider, database_blocks, in_memory_blocks, _) = provider_with_random_blocks( - &mut rng, - mid_point, - TEST_BLOCKS_COUNT - mid_point, - BlockRangeParams::default(), - )?; - - // Get the range of blocks across memory and database - let start_block_number = database_blocks.first().unwrap().number; - let end_block_number = in_memory_blocks.last().unwrap().number; - - let range = start_block_number..=end_block_number; - let blocks = provider.block_range(range)?; - - // Check if the retrieved blocks match the database and in-memory blocks - assert_eq!(blocks.len(), TEST_BLOCKS_COUNT); - let all_expected_blocks = - database_blocks.iter().chain(in_memory_blocks.iter()).collect::>(); - // Check if the blocks are equal - for (retrieved_block, expected_block) in blocks.iter().zip(all_expected_blocks.iter()) { - assert_eq!(retrieved_block.clone(), (*expected_block).clone().unseal()); - } - - Ok(()) - } - - #[test] - fn test_block_range_non_existent_range() -> eyre::Result<()> { - let mut rng = generators::rng(); - let (provider, _, _, _) = provider_with_random_blocks( - &mut rng, - TEST_BLOCKS_COUNT, - TEST_BLOCKS_COUNT, - BlockRangeParams::default(), - )?; - - // Generate a non-existent range - let non_existent_range = 9999..=10000; - let blocks = provider.block_range(non_existent_range)?; - - // The range is non-existent, so the blocks should be empty - assert!(blocks.is_empty()); - - Ok(()) - } - - #[test] - fn test_block_range_partial_overlap() -> eyre::Result<()> { - let mut rng = generators::rng(); - let mid_point = TEST_BLOCKS_COUNT / 2; - let (provider, database_blocks, in_memory_blocks, _) = provider_with_random_blocks( - &mut rng, - mid_point, - mid_point, - BlockRangeParams::default(), - )?; - - // Get the range of blocks across memory and database - let start_block_number = database_blocks.last().unwrap().number; - let end_block_number = in_memory_blocks.first().unwrap().number; - - let range = start_block_number..=end_block_number; - let blocks = provider.block_range(range)?; - - assert_eq!(blocks.len(), 2); // Only one block from each side of the overlap - assert_eq!(blocks[0], database_blocks.last().unwrap().clone().unseal()); - assert_eq!(blocks[1], in_memory_blocks.first().unwrap().clone().unseal()); - - Ok(()) - } - - #[test] - fn test_block_with_senders_range_across_memory_and_database() -> eyre::Result<()> { - let mut rng = generators::rng(); - let mid_point = TEST_BLOCKS_COUNT / 2; - let (provider, database_blocks, in_memory_blocks, _) = provider_with_random_blocks( - &mut rng, - mid_point, - TEST_BLOCKS_COUNT - mid_point, - BlockRangeParams::default(), - )?; - - // Get the range of blocks across memory and database - let start_block_number = database_blocks.first().unwrap().number; - let end_block_number = in_memory_blocks.last().unwrap().number; - - let range = start_block_number..=end_block_number; - let blocks_with_senders = provider.block_with_senders_range(range)?; - - // Check if the retrieved blocks match the database and in-memory blocks - assert_eq!(blocks_with_senders.len(), TEST_BLOCKS_COUNT); - - let all_expected_blocks_with_senders = database_blocks - .iter() - .chain(in_memory_blocks.iter()) - .map(|sealed_block| BlockWithSenders { - block: sealed_block.clone().unseal(), - senders: sealed_block.senders().unwrap(), - }) - .collect::>(); - - // Check if the blocks are equal - for (retrieved_block_with_senders, expected_block_with_senders) in - blocks_with_senders.iter().zip(all_expected_blocks_with_senders.iter()) - { - assert_eq!(retrieved_block_with_senders.block, expected_block_with_senders.block); - assert_eq!(retrieved_block_with_senders.senders, expected_block_with_senders.senders); - } - - Ok(()) - } - - #[test] - fn test_block_with_senders_range_only_in_memory() -> eyre::Result<()> { - let mut rng = generators::rng(); - let (provider, _, in_memory_blocks, _) = provider_with_random_blocks( - &mut rng, - TEST_BLOCKS_COUNT, - TEST_BLOCKS_COUNT, - BlockRangeParams::default(), - )?; - - // Get the range of in-memory blocks - let start_block_number = in_memory_blocks.first().unwrap().number; - let end_block_number = in_memory_blocks.last().unwrap().number; - - let range = start_block_number..=end_block_number; - let blocks_with_senders = provider.block_with_senders_range(range)?; - - // Check if the retrieved blocks match the in-memory blocks - assert_eq!(blocks_with_senders.len(), TEST_BLOCKS_COUNT); - - let expected_blocks_with_senders = in_memory_blocks - .iter() - .map(|sealed_block| BlockWithSenders { - block: sealed_block.clone().unseal(), - senders: sealed_block.senders().unwrap(), - }) - .collect::>(); - - // Check if the blocks are equal - for (retrieved_block_with_senders, expected_block_with_senders) in - blocks_with_senders.iter().zip(expected_blocks_with_senders.iter()) - { - assert_eq!(retrieved_block_with_senders.block, expected_block_with_senders.block); - assert_eq!(retrieved_block_with_senders.senders, expected_block_with_senders.senders); - } - - Ok(()) - } - - #[test] - fn test_block_with_senders_range_only_in_database() -> eyre::Result<()> { - let mut rng = generators::rng(); - let (provider, database_blocks, _, _) = provider_with_random_blocks( - &mut rng, - TEST_BLOCKS_COUNT, - 0, - BlockRangeParams::default(), - )?; - - // Get the range of database blocks - let start_block_number = database_blocks.first().unwrap().number; - let end_block_number = database_blocks.last().unwrap().number; - - let range = start_block_number..=end_block_number; - let blocks_with_senders = provider.block_with_senders_range(range)?; - - // Check if the retrieved blocks match the database blocks - assert_eq!(blocks_with_senders.len(), TEST_BLOCKS_COUNT); - - let expected_blocks_with_senders = database_blocks - .iter() - .map(|sealed_block| BlockWithSenders { - block: sealed_block.clone().unseal(), - senders: sealed_block.senders().unwrap(), - }) - .collect::>(); - - // Check if the blocks are equal - for (retrieved_block_with_senders, expected_block_with_senders) in - blocks_with_senders.iter().zip(expected_blocks_with_senders.iter()) - { - assert_eq!(retrieved_block_with_senders.block, expected_block_with_senders.block); - assert_eq!(retrieved_block_with_senders.senders, expected_block_with_senders.senders); - } - - Ok(()) - } - - #[test] - fn test_block_with_senders_range_non_existent_range() -> eyre::Result<()> { - let mut rng = generators::rng(); - let (provider, _, _, _) = provider_with_random_blocks( - &mut rng, - TEST_BLOCKS_COUNT, - TEST_BLOCKS_COUNT, - BlockRangeParams::default(), - )?; - - // Assuming this range does not exist - let start_block_number = 1000; - let end_block_number = 2000; - - let range = start_block_number..=end_block_number; - let blocks_with_senders = provider.block_with_senders_range(range)?; - - // The range is non-existent, so the blocks should be empty - assert!(blocks_with_senders.is_empty()); - - Ok(()) - } - - #[test] - fn test_sealed_block_with_senders_range_across_memory_and_database() -> eyre::Result<()> { - let mut rng = generators::rng(); - let mid_point = TEST_BLOCKS_COUNT / 2; - let (provider, database_blocks, in_memory_blocks, _) = provider_with_random_blocks( - &mut rng, - mid_point, - TEST_BLOCKS_COUNT - mid_point, - BlockRangeParams::default(), - )?; - - // Get the range of blocks across memory and database - let start_block_number = database_blocks.first().unwrap().number; - let end_block_number = in_memory_blocks.last().unwrap().number; - - let range = start_block_number..=end_block_number; - let sealed_blocks_with_senders = provider.sealed_block_with_senders_range(range)?; - - // Check if the retrieved blocks match the database and in-memory blocks - assert_eq!(sealed_blocks_with_senders.len(), TEST_BLOCKS_COUNT); - - let all_expected_sealed_blocks_with_senders = database_blocks - .iter() - .chain(in_memory_blocks.iter()) - .map(|sealed_block| SealedBlockWithSenders { - block: sealed_block.clone(), - senders: sealed_block.senders().unwrap(), - }) - .collect::>(); - - // Check if the blocks are equal - for (retrieved_sealed_block_with_senders, expected_sealed_block_with_senders) in - sealed_blocks_with_senders.iter().zip(all_expected_sealed_blocks_with_senders.iter()) - { - assert_eq!( - retrieved_sealed_block_with_senders.block, - expected_sealed_block_with_senders.block - ); - assert_eq!( - retrieved_sealed_block_with_senders.senders, - expected_sealed_block_with_senders.senders - ); - } - - Ok(()) - } - - #[test] - fn test_sealed_block_with_senders_range_only_in_memory() -> eyre::Result<()> { - let mut rng = generators::rng(); - let (provider, _, in_memory_blocks, _) = provider_with_random_blocks( - &mut rng, - TEST_BLOCKS_COUNT, - TEST_BLOCKS_COUNT, - BlockRangeParams::default(), - )?; - - // Get the range of in-memory blocks - let start_block_number = in_memory_blocks.first().unwrap().number; - let end_block_number = in_memory_blocks.last().unwrap().number; - - let range = start_block_number..=end_block_number; - let sealed_blocks_with_senders = provider.sealed_block_with_senders_range(range)?; - - // Check if the retrieved blocks match the in-memory blocks - assert_eq!(sealed_blocks_with_senders.len(), TEST_BLOCKS_COUNT); - - let expected_sealed_blocks_with_senders = in_memory_blocks - .iter() - .map(|sealed_block| SealedBlockWithSenders { - block: sealed_block.clone(), - senders: sealed_block.senders().unwrap(), - }) - .collect::>(); - - // Check if the blocks are equal - for (retrieved_sealed_block_with_senders, expected_sealed_block_with_senders) in - sealed_blocks_with_senders.iter().zip(expected_sealed_blocks_with_senders.iter()) - { - assert_eq!( - retrieved_sealed_block_with_senders.block, - expected_sealed_block_with_senders.block - ); - assert_eq!( - retrieved_sealed_block_with_senders.senders, - expected_sealed_block_with_senders.senders - ); - } - - Ok(()) - } - - #[test] - fn test_sealed_block_with_senders_range_only_in_database() -> eyre::Result<()> { - let mut rng = generators::rng(); - let (provider, database_blocks, _, _) = provider_with_random_blocks( - &mut rng, - TEST_BLOCKS_COUNT, - 0, - BlockRangeParams::default(), - )?; - - // Get the range of database blocks - let start_block_number = database_blocks.first().unwrap().number; - let end_block_number = database_blocks.last().unwrap().number; - - let range = start_block_number..=end_block_number; - let sealed_blocks_with_senders = provider.sealed_block_with_senders_range(range)?; - - // Check if the retrieved blocks match the database blocks - assert_eq!(sealed_blocks_with_senders.len(), TEST_BLOCKS_COUNT); - - let expected_sealed_blocks_with_senders = database_blocks - .iter() - .map(|sealed_block| SealedBlockWithSenders { - block: sealed_block.clone(), - senders: sealed_block.senders().unwrap(), - }) - .collect::>(); - - // Check if the blocks are equal - for (retrieved_sealed_block_with_senders, expected_sealed_block_with_senders) in - sealed_blocks_with_senders.iter().zip(expected_sealed_blocks_with_senders.iter()) - { - assert_eq!( - retrieved_sealed_block_with_senders.block, - expected_sealed_block_with_senders.block - ); - assert_eq!( - retrieved_sealed_block_with_senders.senders, - expected_sealed_block_with_senders.senders - ); - } - - Ok(()) - } - - #[test] - fn test_sealed_block_with_senders_range_non_existent_range() -> eyre::Result<()> { - let mut rng = generators::rng(); - let (provider, _, _, _) = provider_with_random_blocks( - &mut rng, - TEST_BLOCKS_COUNT, - TEST_BLOCKS_COUNT, - BlockRangeParams::default(), - )?; - - // Assuming this range does not exist - let start_block_number = 1000; - let end_block_number = 2000; - - let range = start_block_number..=end_block_number; - let sealed_blocks_with_senders = provider.sealed_block_with_senders_range(range)?; - - // The range is non-existent, so the blocks should be empty - assert!(sealed_blocks_with_senders.is_empty()); - - Ok(()) - } - #[test] fn test_block_hash_reader() -> eyre::Result<()> { let mut rng = generators::rng(); @@ -2787,11 +2605,6 @@ mod tests { Some(in_memory_block.difficulty) ); - assert_eq!( - provider.headers_range(0..=10)?, - blocks.iter().map(|b| b.header().clone()).collect::>() - ); - assert_eq!( provider.sealed_header(database_block.number)?, Some(database_block.header.clone()) @@ -2801,11 +2614,6 @@ mod tests { Some(in_memory_block.header.clone()) ); - assert_eq!( - provider.sealed_headers_range(0..=10)?, - blocks.iter().map(|b| b.header.clone()).collect::>() - ); - assert_eq!( provider.sealed_headers_while(0..=10, |header| header.number <= 8)?, blocks @@ -4135,49 +3943,155 @@ mod tests { Ok(()) } + macro_rules! test_by_tx_range { + ($provider:expr, $database_blocks:expr, $in_memory_blocks:expr, [$(($method:ident, $data_extractor:expr)),* $(,)?]) => {{ + let db_tx_count = + $database_blocks.iter().map(|b| b.body.transactions.len()).sum::() as u64; + let in_mem_tx_count = + $in_memory_blocks.iter().map(|b| b.body.transactions.len()).sum::() as u64; + + let db_range = 0..=(db_tx_count - 1); + let in_mem_range = db_tx_count..=(in_mem_tx_count + db_range.end()); + + $( + // Retrieve the expected database data + let database_data = + $database_blocks.iter().flat_map(|b| $data_extractor(b)).collect::>(); + assert_eq!($provider.$method(db_range.clone())?, database_data); + + // Retrieve the expected in-memory data + let in_memory_data = + $in_memory_blocks.iter().flat_map(|b| $data_extractor(b)).collect::>(); + assert_eq!($provider.$method(in_mem_range.clone())?, in_memory_data); + + // Test partial in-memory range + assert_eq!( + &$provider.$method(in_mem_range.start() + 1..=in_mem_range.end() - 1)?, + &in_memory_data[1..in_memory_data.len() - 1] + ); + + // Test range in in-memory to unbounded end + assert_eq!($provider.$method(in_mem_range.start() + 1..)?, &in_memory_data[1..]); + + // Test last element in-memory + assert_eq!($provider.$method(in_mem_range.end()..)?, &in_memory_data[in_memory_data.len() -1 ..]); + + // Test range that spans database and in-memory + assert_eq!( + $provider.$method(in_mem_range.start() - 2..=in_mem_range.end() - 1)?, + database_data[database_data.len() - 2..] + .iter() + .chain(&in_memory_data[..in_memory_data.len() - 1]) + .cloned() + .collect::>() + ); + + // Test range that spans database and in-memory with unbounded end + assert_eq!( + $provider.$method(in_mem_range.start() - 2..)?, + database_data[database_data.len() - 2..] + .iter() + .chain(&in_memory_data[..]) + .cloned() + .collect::>() + ); + + // Test invalid range + let start_tx_num = u64::MAX; + let end_tx_num = u64::MAX; + let result = $provider.$method(start_tx_num..end_tx_num)?; + assert!(result.is_empty(), "No data should be found for an invalid transaction range"); + + // Test empty range + let result = $provider.$method(in_mem_range.end()+10..in_mem_range.end()+20)?; + assert!(result.is_empty(), "No data should be found for an empty transaction range"); + )* + }}; + } + #[test] - fn test_transactions_by_tx_range() -> eyre::Result<()> { + fn test_methods_by_tx_range() -> eyre::Result<()> { let mut rng = generators::rng(); - let (provider, database_blocks, _, _) = provider_with_random_blocks( + let (provider, database_blocks, in_memory_blocks, receipts) = provider_with_random_blocks( &mut rng, TEST_BLOCKS_COUNT, - 0, + TEST_BLOCKS_COUNT, BlockRangeParams { tx_count: TEST_TRANSACTIONS_COUNT..TEST_TRANSACTIONS_COUNT, ..Default::default() }, )?; - // Define a valid transaction range within the database - let start_tx_num = 0; - let end_tx_num = 1; + test_by_tx_range!( + provider, + database_blocks, + in_memory_blocks, + [ + (senders_by_tx_range, |block: &SealedBlock| block.senders().unwrap()), + (transactions_by_tx_range, |block: &SealedBlock| block + .body + .transactions + .iter() + .map(|tx| Into::::into(tx.clone())) + .collect::>()), + (receipts_by_tx_range, |block: &SealedBlock| receipts[block.number as usize] + .clone()) + ] + ); - // Retrieve the transactions for this transaction number range - let result = provider.transactions_by_tx_range(start_tx_num..=end_tx_num)?; + Ok(()) + } - // Ensure the transactions match the expected transactions in the database - assert_eq!(result.len(), 2); - assert_eq!(result[0], database_blocks[0].body.transactions[0].clone().into()); - assert_eq!(result[1], database_blocks[0].body.transactions[1].clone().into()); + macro_rules! test_by_block_range { + ($provider:expr, $database_blocks:expr, $in_memory_blocks:expr, [$(($method:ident, $data_extractor:expr)),* $(,)?]) => {{ + let db_block_count = $database_blocks.len() as u64; + let in_mem_block_count = $in_memory_blocks.len() as u64; - // Define an empty range that should return no transactions - let start_tx_num = u64::MAX; - let end_tx_num = u64::MAX; + let db_range = 0..=db_block_count - 1; + let in_mem_range = db_block_count..=(in_mem_block_count + db_range.end()); - // Retrieve the transactions for this range - let result = provider.transactions_by_tx_range(start_tx_num..end_tx_num)?; + $( + // Retrieve the expected database data + let database_data = + $database_blocks.iter().map(|b| $data_extractor(b)).collect::>(); + assert_eq!($provider.$method(db_range.clone())?, database_data); - // Ensure no transactions are returned - assert!( - result.is_empty(), - "No transactions should be found for an empty transaction range" - ); + // Retrieve the expected in-memory data + let in_memory_data = + $in_memory_blocks.iter().map(|b| $data_extractor(b)).collect::>(); + assert_eq!($provider.$method(in_mem_range.clone())?, in_memory_data); - Ok(()) + // Test partial in-memory range + assert_eq!( + &$provider.$method(in_mem_range.start() + 1..=in_mem_range.end() - 1)?, + &in_memory_data[1..in_memory_data.len() - 1] + ); + + // Test range that spans database and in-memory + assert_eq!( + $provider.$method(in_mem_range.start() - 2..=in_mem_range.end() - 1)?, + database_data[database_data.len() - 2..] + .iter() + .chain(&in_memory_data[..in_memory_data.len() - 1]) + .cloned() + .collect::>() + ); + + // Test invalid range + let start_block_num = u64::MAX; + let end_block_num = u64::MAX; + let result = $provider.$method(start_block_num..=end_block_num-1)?; + assert!(result.is_empty(), "No data should be found for an invalid block range"); + + // Test valid range with empty results + let result = $provider.$method(in_mem_range.end() + 10..=in_mem_range.end() + 20)?; + assert!(result.is_empty(), "No data should be found for an empty block range"); + )* + }}; } #[test] - fn test_senders_by_tx_range() -> eyre::Result<()> { + fn test_methods_by_block_range() -> eyre::Result<()> { let mut rng = generators::rng(); let (provider, database_blocks, in_memory_blocks, _) = provider_with_random_blocks( &mut rng, @@ -4189,51 +4103,28 @@ mod tests { }, )?; - let db_tx_count = - database_blocks.iter().map(|b| b.body.transactions.len()).sum::() as u64; - let in_mem_tx_count = - in_memory_blocks.iter().map(|b| b.body.transactions.len()).sum::() as u64; - - let db_range = 0..=(db_tx_count - 1); - let in_mem_range = db_tx_count..=(in_mem_tx_count + db_range.end()); - - // Retrieve the senders for the whole database range - let database_senders = - database_blocks.iter().flat_map(|b| b.senders().unwrap()).collect::>(); - assert_eq!(provider.senders_by_tx_range(db_range)?, database_senders); - - // Retrieve the senders for the whole in-memory range - let in_memory_senders = - in_memory_blocks.iter().flat_map(|b| b.senders().unwrap()).collect::>(); - assert_eq!(provider.senders_by_tx_range(in_mem_range.clone())?, in_memory_senders); - - // Retrieve the senders for a partial in-memory range - assert_eq!( - &provider.senders_by_tx_range(in_mem_range.start() + 1..=in_mem_range.end() - 1)?, - &in_memory_senders[1..in_memory_senders.len() - 1] - ); - - // Retrieve the senders for a range that spans database and in-memory - assert_eq!( - provider.senders_by_tx_range(in_mem_range.start() - 2..=in_mem_range.end() - 1)?, - database_senders[database_senders.len() - 2..] - .iter() - .chain(&in_memory_senders[..in_memory_senders.len() - 1]) - .copied() - .collect::>() - ); - - // Define an empty range that should return no sender addresses - let start_tx_num = u64::MAX; - let end_tx_num = u64::MAX; - - // Retrieve the senders for this range - let result = provider.senders_by_tx_range(start_tx_num..end_tx_num)?; - - // Ensure no sender addresses are returned - assert!( - result.is_empty(), - "No sender addresses should be found for an empty transaction range" + // todo(joshie) add canonical_hashes_range below after changing its interface into range + // instead start end + test_by_block_range!( + provider, + database_blocks, + in_memory_blocks, + [ + (headers_range, |block: &SealedBlock| block.header().clone()), + (sealed_headers_range, |block: &SealedBlock| block.header.clone()), + (block_range, |block: &SealedBlock| block.clone().unseal()), + (block_with_senders_range, |block: &SealedBlock| block + .clone() + .unseal() + .with_senders_unchecked(vec![])), + (sealed_block_with_senders_range, |block: &SealedBlock| block + .clone() + .with_senders_unchecked(vec![])), + (transactions_by_block_range, |block: &SealedBlock| block + .body + .transactions + .clone()), + ] ); Ok(()) @@ -4302,4 +4193,80 @@ mod tests { Ok(()) } + + #[test] + fn test_race() -> eyre::Result<()> { + let mut rng = generators::rng(); + let (provider, _, in_memory_blocks, _) = provider_with_random_blocks( + &mut rng, + TEST_BLOCKS_COUNT - 1, + TEST_BLOCKS_COUNT + 1, + BlockRangeParams { + tx_count: TEST_TRANSACTIONS_COUNT..TEST_TRANSACTIONS_COUNT, + ..Default::default() + }, + )?; + + let provider = Arc::new(provider); + + // Old implementation was querying the database first. This is problematic, if there are + // changes AFTER the database transaction is created. + let old_transaction_hash_fn = + |hash: B256, + canonical_in_memory_state: CanonicalInMemoryState, + factory: ProviderFactory| { + assert!(factory.transaction_by_hash(hash)?.is_none(), "should not be in database"); + Ok::<_, ProviderError>(canonical_in_memory_state.transaction_by_hash(hash)) + }; + + // Correct implementation queries in-memory first + let correct_transaction_hash_fn = + |hash: B256, + canonical_in_memory_state: CanonicalInMemoryState, + _factory: ProviderFactory| { + if let Some(tx) = canonical_in_memory_state.transaction_by_hash(hash) { + return Ok::<_, ProviderError>(Some(tx)) + } + panic!("should not be in database"); + // _factory.transaction_by_hash(hash) + }; + + // OLD BEHAVIOUR + { + // This will persist block 1 AFTER a database is created. Moving it from memory to + // storage. + persist_block_after_db_tx_creation(provider.clone(), in_memory_blocks[0].number); + let to_be_persisted_tx = in_memory_blocks[0].body.transactions[0].clone(); + + // Even though the block exists, given the order of provider queries done in the method + // above, we do not see it. + assert_eq!( + old_transaction_hash_fn( + to_be_persisted_tx.hash(), + provider.canonical_in_memory_state(), + provider.database.clone() + ), + Ok(None) + ); + } + + // CORRECT BEHAVIOUR + { + // This will persist block 1 AFTER a database is created. Moving it from memory to + // storage. + persist_block_after_db_tx_creation(provider.clone(), in_memory_blocks[1].number); + let to_be_persisted_tx = in_memory_blocks[1].body.transactions[0].clone(); + + assert_eq!( + correct_transaction_hash_fn( + to_be_persisted_tx.hash(), + provider.canonical_in_memory_state(), + provider.database.clone() + ), + Ok(Some(to_be_persisted_tx)) + ); + } + + Ok(()) + } } diff --git a/crates/storage/provider/src/providers/database/provider.rs b/crates/storage/provider/src/providers/database/provider.rs index 8627cacabb4c..1afd4da3fa8c 100644 --- a/crates/storage/provider/src/providers/database/provider.rs +++ b/crates/storage/provider/src/providers/database/provider.rs @@ -46,7 +46,7 @@ use reth_primitives::{ }; use reth_prune_types::{PruneCheckpoint, PruneModes, PruneSegment}; use reth_stages_types::{StageCheckpoint, StageId}; -use reth_storage_api::TryIntoHistoricalStateProvider; +use reth_storage_api::{StorageChangeSetReader, TryIntoHistoricalStateProvider}; use reth_storage_errors::provider::{ProviderResult, RootMismatch}; use reth_trie::{ prefix_set::{PrefixSet, PrefixSetMut, TriePrefixSets}, @@ -1414,6 +1414,21 @@ impl AccountExtReader for DatabaseProvider StorageChangeSetReader for DatabaseProvider { + fn storage_changeset( + &self, + block_number: BlockNumber, + ) -> ProviderResult> { + let range = block_number..=block_number; + let storage_range = BlockNumberAddress::range(range); + self.tx + .cursor_dup_read::()? + .walk_range(storage_range)? + .map(|result| -> ProviderResult<_> { Ok(result?) }) + .collect() + } +} + impl ChangeSetReader for DatabaseProvider { fn account_block_changeset( &self, diff --git a/crates/storage/provider/src/providers/state/historical.rs b/crates/storage/provider/src/providers/state/historical.rs index de30f89c98ee..22f40f6f951f 100644 --- a/crates/storage/provider/src/providers/state/historical.rs +++ b/crates/storage/provider/src/providers/state/historical.rs @@ -227,7 +227,7 @@ impl<'b, TX: DbTx> HistoricalStateProviderRef<'b, TX> { } } -impl<'b, TX: DbTx> AccountReader for HistoricalStateProviderRef<'b, TX> { +impl AccountReader for HistoricalStateProviderRef<'_, TX> { /// Get basic account information. fn basic_account(&self, address: Address) -> ProviderResult> { match self.account_history_lookup(address)? { @@ -249,7 +249,7 @@ impl<'b, TX: DbTx> AccountReader for HistoricalStateProviderRef<'b, TX> { } } -impl<'b, TX: DbTx> BlockHashReader for HistoricalStateProviderRef<'b, TX> { +impl BlockHashReader for HistoricalStateProviderRef<'_, TX> { /// Get block hash by number. fn block_hash(&self, number: u64) -> ProviderResult> { self.static_file_provider.get_with_static_file_or_database( @@ -285,7 +285,7 @@ impl<'b, TX: DbTx> BlockHashReader for HistoricalStateProviderRef<'b, TX> { } } -impl<'b, TX: DbTx> StateRootProvider for HistoricalStateProviderRef<'b, TX> { +impl StateRootProvider for HistoricalStateProviderRef<'_, TX> { fn state_root(&self, hashed_state: HashedPostState) -> ProviderResult { let mut revert_state = self.revert_state()?; revert_state.extend(hashed_state); @@ -319,7 +319,7 @@ impl<'b, TX: DbTx> StateRootProvider for HistoricalStateProviderRef<'b, TX> { } } -impl<'b, TX: DbTx> StorageRootProvider for HistoricalStateProviderRef<'b, TX> { +impl StorageRootProvider for HistoricalStateProviderRef<'_, TX> { fn storage_root( &self, address: Address, @@ -332,7 +332,7 @@ impl<'b, TX: DbTx> StorageRootProvider for HistoricalStateProviderRef<'b, TX> { } } -impl<'b, TX: DbTx> StateProofProvider for HistoricalStateProviderRef<'b, TX> { +impl StateProofProvider for HistoricalStateProviderRef<'_, TX> { /// Get account and storage proofs. fn proof( &self, @@ -364,7 +364,7 @@ impl<'b, TX: DbTx> StateProofProvider for HistoricalStateProviderRef<'b, TX> { } } -impl<'b, TX: DbTx> StateProvider for HistoricalStateProviderRef<'b, TX> { +impl StateProvider for HistoricalStateProviderRef<'_, TX> { /// Get storage. fn storage( &self, diff --git a/crates/storage/provider/src/providers/state/latest.rs b/crates/storage/provider/src/providers/state/latest.rs index f63eaee23862..9fbe00cbd5ee 100644 --- a/crates/storage/provider/src/providers/state/latest.rs +++ b/crates/storage/provider/src/providers/state/latest.rs @@ -36,14 +36,14 @@ impl<'b, TX: DbTx> LatestStateProviderRef<'b, TX> { } } -impl<'b, TX: DbTx> AccountReader for LatestStateProviderRef<'b, TX> { +impl AccountReader for LatestStateProviderRef<'_, TX> { /// Get basic account information. fn basic_account(&self, address: Address) -> ProviderResult> { self.tx.get::(address).map_err(Into::into) } } -impl<'b, TX: DbTx> BlockHashReader for LatestStateProviderRef<'b, TX> { +impl BlockHashReader for LatestStateProviderRef<'_, TX> { /// Get block hash by number. fn block_hash(&self, number: u64) -> ProviderResult> { self.static_file_provider.get_with_static_file_or_database( @@ -79,7 +79,7 @@ impl<'b, TX: DbTx> BlockHashReader for LatestStateProviderRef<'b, TX> { } } -impl<'b, TX: DbTx> StateRootProvider for LatestStateProviderRef<'b, TX> { +impl StateRootProvider for LatestStateProviderRef<'_, TX> { fn state_root(&self, hashed_state: HashedPostState) -> ProviderResult { StateRoot::overlay_root(self.tx, hashed_state) .map_err(|err| ProviderError::Database(err.into())) @@ -107,7 +107,7 @@ impl<'b, TX: DbTx> StateRootProvider for LatestStateProviderRef<'b, TX> { } } -impl<'b, TX: DbTx> StorageRootProvider for LatestStateProviderRef<'b, TX> { +impl StorageRootProvider for LatestStateProviderRef<'_, TX> { fn storage_root( &self, address: Address, @@ -118,7 +118,7 @@ impl<'b, TX: DbTx> StorageRootProvider for LatestStateProviderRef<'b, TX> { } } -impl<'b, TX: DbTx> StateProofProvider for LatestStateProviderRef<'b, TX> { +impl StateProofProvider for LatestStateProviderRef<'_, TX> { fn proof( &self, input: TrieInput, @@ -146,7 +146,7 @@ impl<'b, TX: DbTx> StateProofProvider for LatestStateProviderRef<'b, TX> { } } -impl<'b, TX: DbTx> StateProvider for LatestStateProviderRef<'b, TX> { +impl StateProvider for LatestStateProviderRef<'_, TX> { /// Get storage. fn storage( &self, diff --git a/crates/storage/provider/src/providers/static_file/jar.rs b/crates/storage/provider/src/providers/static_file/jar.rs index 6372bad24424..8d1dbd117cfb 100644 --- a/crates/storage/provider/src/providers/static_file/jar.rs +++ b/crates/storage/provider/src/providers/static_file/jar.rs @@ -75,7 +75,7 @@ impl<'a> StaticFileJarProvider<'a> { } } -impl<'a> HeaderProvider for StaticFileJarProvider<'a> { +impl HeaderProvider for StaticFileJarProvider<'_> { fn header(&self, block_hash: &BlockHash) -> ProviderResult> { Ok(self .cursor()? @@ -147,7 +147,7 @@ impl<'a> HeaderProvider for StaticFileJarProvider<'a> { } } -impl<'a> BlockHashReader for StaticFileJarProvider<'a> { +impl BlockHashReader for StaticFileJarProvider<'_> { fn block_hash(&self, number: u64) -> ProviderResult> { self.cursor()?.get_one::>(number.into()) } @@ -169,7 +169,7 @@ impl<'a> BlockHashReader for StaticFileJarProvider<'a> { } } -impl<'a> BlockNumReader for StaticFileJarProvider<'a> { +impl BlockNumReader for StaticFileJarProvider<'_> { fn chain_info(&self) -> ProviderResult { // Information on live database Err(ProviderError::UnsupportedProvider) @@ -194,7 +194,7 @@ impl<'a> BlockNumReader for StaticFileJarProvider<'a> { } } -impl<'a> TransactionsProvider for StaticFileJarProvider<'a> { +impl TransactionsProvider for StaticFileJarProvider<'_> { fn transaction_id(&self, hash: TxHash) -> ProviderResult> { let mut cursor = self.cursor()?; @@ -290,7 +290,7 @@ impl<'a> TransactionsProvider for StaticFileJarProvider<'a> { } } -impl<'a> ReceiptProvider for StaticFileJarProvider<'a> { +impl ReceiptProvider for StaticFileJarProvider<'_> { fn receipt(&self, num: TxNumber) -> ProviderResult> { self.cursor()?.get_one::>(num.into()) } diff --git a/crates/storage/provider/src/providers/static_file/writer.rs b/crates/storage/provider/src/providers/static_file/writer.rs index d086c5693ca5..3858f1b14023 100644 --- a/crates/storage/provider/src/providers/static_file/writer.rs +++ b/crates/storage/provider/src/providers/static_file/writer.rs @@ -67,14 +67,14 @@ pub struct StaticFileProviderRWRefMut<'a>( pub(crate) RwLockWriteGuard<'a, RawRwLock, Option>, ); -impl<'a> std::ops::DerefMut for StaticFileProviderRWRefMut<'a> { +impl std::ops::DerefMut for StaticFileProviderRWRefMut<'_> { fn deref_mut(&mut self) -> &mut Self::Target { // This is always created by [`StaticFileWriters::get_or_create`] self.0.as_mut().expect("static file writer provider should be init") } } -impl<'a> std::ops::Deref for StaticFileProviderRWRefMut<'a> { +impl std::ops::Deref for StaticFileProviderRWRefMut<'_> { type Target = StaticFileProviderRW; fn deref(&self) -> &Self::Target { diff --git a/crates/storage/provider/src/writer/database.rs b/crates/storage/provider/src/writer/database.rs index 3ae42b4bf1cb..1436fb8a6ab9 100644 --- a/crates/storage/provider/src/writer/database.rs +++ b/crates/storage/provider/src/writer/database.rs @@ -9,7 +9,7 @@ use reth_storage_api::ReceiptWriter; pub(crate) struct DatabaseWriter<'a, W>(pub(crate) &'a mut W); -impl<'a, W> ReceiptWriter for DatabaseWriter<'a, W> +impl ReceiptWriter for DatabaseWriter<'_, W> where W: DbCursorRO + DbCursorRW, { diff --git a/crates/storage/provider/src/writer/mod.rs b/crates/storage/provider/src/writer/mod.rs index ecb1de335559..5b16b2da4e5a 100644 --- a/crates/storage/provider/src/writer/mod.rs +++ b/crates/storage/provider/src/writer/mod.rs @@ -147,7 +147,7 @@ impl UnifiedStorageWriter<'_, (), ()> { } } -impl<'a, 'b, ProviderDB> UnifiedStorageWriter<'a, ProviderDB, &'b StaticFileProvider> +impl UnifiedStorageWriter<'_, ProviderDB, &StaticFileProvider> where ProviderDB: DBProvider + BlockWriter @@ -318,7 +318,7 @@ where } } -impl<'a, 'b, ProviderDB> UnifiedStorageWriter<'a, ProviderDB, StaticFileProviderRWRefMut<'b>> +impl UnifiedStorageWriter<'_, ProviderDB, StaticFileProviderRWRefMut<'_>> where ProviderDB: DBProvider + HeaderProvider, { @@ -429,7 +429,7 @@ where } } -impl<'a, 'b, ProviderDB> UnifiedStorageWriter<'a, ProviderDB, StaticFileProviderRWRefMut<'b>> +impl UnifiedStorageWriter<'_, ProviderDB, StaticFileProviderRWRefMut<'_>> where ProviderDB: DBProvider + HeaderProvider, { @@ -510,8 +510,8 @@ where } } -impl<'a, 'b, ProviderDB> StateWriter - for UnifiedStorageWriter<'a, ProviderDB, StaticFileProviderRWRefMut<'b>> +impl StateWriter + for UnifiedStorageWriter<'_, ProviderDB, StaticFileProviderRWRefMut<'_>> where ProviderDB: DBProvider + StateChangeWriter + HeaderProvider, { diff --git a/crates/storage/provider/src/writer/static_file.rs b/crates/storage/provider/src/writer/static_file.rs index aca226ca9b75..5514e211e58f 100644 --- a/crates/storage/provider/src/writer/static_file.rs +++ b/crates/storage/provider/src/writer/static_file.rs @@ -6,7 +6,7 @@ use reth_storage_api::ReceiptWriter; pub(crate) struct StaticFileWriter<'a, W>(pub(crate) &'a mut W); -impl<'a> ReceiptWriter for StaticFileWriter<'a, StaticFileProviderRWRefMut<'_>> { +impl ReceiptWriter for StaticFileWriter<'_, StaticFileProviderRWRefMut<'_>> { fn append_block_receipts( &mut self, first_tx_index: TxNumber, diff --git a/crates/storage/storage-api/src/storage.rs b/crates/storage/storage-api/src/storage.rs index 91d0bc8c7353..e1443347e4bb 100644 --- a/crates/storage/storage-api/src/storage.rs +++ b/crates/storage/storage-api/src/storage.rs @@ -1,4 +1,5 @@ use alloy_primitives::{Address, BlockNumber, B256}; +use reth_db_api::models::BlockNumberAddress; use reth_primitives::StorageEntry; use reth_storage_errors::provider::ProviderResult; use std::{ @@ -30,3 +31,13 @@ pub trait StorageReader: Send + Sync { range: RangeInclusive, ) -> ProviderResult>>; } + +/// Storage ChangeSet reader +#[auto_impl::auto_impl(&, Arc, Box)] +pub trait StorageChangeSetReader: Send + Sync { + /// Iterate over storage changesets and return the storage state from before this block. + fn storage_changeset( + &self, + block_number: BlockNumber, + ) -> ProviderResult>; +} diff --git a/crates/transaction-pool/src/config.rs b/crates/transaction-pool/src/config.rs index 623493e6c9d4..67328c7f1be1 100644 --- a/crates/transaction-pool/src/config.rs +++ b/crates/transaction-pool/src/config.rs @@ -157,7 +157,7 @@ pub struct LocalTransactionConfig { /// - no price exemptions /// - no eviction exemptions pub no_exemptions: bool, - /// Addresses that will be considered as local . Above exemptions apply + /// Addresses that will be considered as local. Above exemptions apply. pub local_addresses: HashSet
, /// Flag indicating whether local transactions should be propagated. pub propagate_local_transactions: bool, diff --git a/crates/transaction-pool/src/lib.rs b/crates/transaction-pool/src/lib.rs index 4aec8ab40858..a5acd6edba5a 100644 --- a/crates/transaction-pool/src/lib.rs +++ b/crates/transaction-pool/src/lib.rs @@ -324,6 +324,7 @@ where impl TransactionPool for Pool where V: TransactionValidator, + ::Transaction: EthPoolTransaction, T: TransactionOrdering::Transaction>, S: BlobStore, { @@ -488,6 +489,13 @@ where self.pool.get_transactions_by_sender(sender) } + fn get_highest_transaction_by_sender( + &self, + sender: Address, + ) -> Option>> { + self.pool.get_highest_transaction_by_sender(sender) + } + fn get_transaction_by_sender_and_nonce( &self, sender: Address, @@ -546,6 +554,7 @@ where impl TransactionPoolExt for Pool where V: TransactionValidator, + ::Transaction: EthPoolTransaction, T: TransactionOrdering::Transaction>, S: BlobStore, { diff --git a/crates/transaction-pool/src/maintain.rs b/crates/transaction-pool/src/maintain.rs index da416fd2d43f..523151956dd1 100644 --- a/crates/transaction-pool/src/maintain.rs +++ b/crates/transaction-pool/src/maintain.rs @@ -18,6 +18,7 @@ use reth_execution_types::ChangedAccount; use reth_fs_util::FsPathError; use reth_primitives::{ BlockNumberOrTag, PooledTransactionsElementEcRecovered, SealedHeader, TransactionSigned, + TransactionSignedEcRecovered, }; use reth_storage_api::{errors::provider::ProviderError, BlockReaderIdExt, StateProviderFactory}; use reth_tasks::TaskSpawner; @@ -334,11 +335,10 @@ pub async fn maintain_transaction_pool( .ok() }) .map(|tx| { - <

::Transaction as PoolTransaction>::from_pooled(tx) +

::Transaction::from_pooled(tx.into()) }) } else { - - ::try_from_consensus(tx).ok() +

::Transaction::try_from_consensus(tx.into()).ok() } }) .collect::>(); @@ -583,7 +583,7 @@ where .filter_map(|tx| tx.try_ecrecovered()) .filter_map(|tx| { // Filter out errors - ::try_from_consensus(tx).ok() + ::try_from_consensus(tx.into()).ok() }) .collect::>(); @@ -606,7 +606,11 @@ where let local_transactions = local_transactions .into_iter() - .map(|tx| tx.to_recovered_transaction().into_signed()) + .map(|tx| { + let recovered: TransactionSignedEcRecovered = + tx.transaction.clone().into_consensus().into(); + recovered.into_signed() + }) .collect::>(); let num_txs = local_transactions.len(); diff --git a/crates/transaction-pool/src/metrics.rs b/crates/transaction-pool/src/metrics.rs index d323d47e459a..f5d269b361fc 100644 --- a/crates/transaction-pool/src/metrics.rs +++ b/crates/transaction-pool/src/metrics.rs @@ -36,8 +36,18 @@ pub struct TxPoolMetrics { /// Total amount of memory used by the transactions in the blob sub-pool in bytes pub(crate) blob_pool_size_bytes: Gauge, - /// Number of all transactions of all sub-pools: pending + basefee + queued + /// Number of all transactions of all sub-pools: pending + basefee + queued + blob pub(crate) total_transactions: Gauge, + /// Number of all legacy transactions in the pool + pub(crate) total_legacy_transactions: Gauge, + /// Number of all EIP-2930 transactions in the pool + pub(crate) total_eip2930_transactions: Gauge, + /// Number of all EIP-1559 transactions in the pool + pub(crate) total_eip1559_transactions: Gauge, + /// Number of all EIP-4844 transactions in the pool + pub(crate) total_eip4844_transactions: Gauge, + /// Number of all EIP-7702 transactions in the pool + pub(crate) total_eip7702_transactions: Gauge, /// How often the pool was updated after the canonical state changed pub(crate) performed_state_updates: Counter, diff --git a/crates/transaction-pool/src/noop.rs b/crates/transaction-pool/src/noop.rs index 0c4caa573140..ddab4f622741 100644 --- a/crates/transaction-pool/src/noop.rs +++ b/crates/transaction-pool/src/noop.rs @@ -206,6 +206,13 @@ impl TransactionPool for NoopTransactionPool { vec![] } + fn get_highest_transaction_by_sender( + &self, + _sender: Address, + ) -> Option>> { + None + } + fn get_transaction_by_sender_and_nonce( &self, _sender: Address, diff --git a/crates/transaction-pool/src/ordering.rs b/crates/transaction-pool/src/ordering.rs index 3381bb027947..0ee0c1004a13 100644 --- a/crates/transaction-pool/src/ordering.rs +++ b/crates/transaction-pool/src/ordering.rs @@ -1,6 +1,5 @@ use crate::traits::PoolTransaction; use alloy_primitives::U256; -use reth_primitives::{PooledTransactionsElementEcRecovered, TransactionSignedEcRecovered}; use std::{fmt, marker::PhantomData}; /// Priority of the transaction that can be missing. @@ -32,10 +31,7 @@ pub trait TransactionOrdering: Send + Sync + 'static { type PriorityValue: Ord + Clone + Default + fmt::Debug + Send + Sync; /// The transaction type to determine the priority of. - type Transaction: PoolTransaction< - Pooled = PooledTransactionsElementEcRecovered, - Consensus = TransactionSignedEcRecovered, - >; + type Transaction: PoolTransaction; /// Returns the priority score for the given transaction. fn priority( @@ -55,10 +51,7 @@ pub struct CoinbaseTipOrdering(PhantomData); impl TransactionOrdering for CoinbaseTipOrdering where - T: PoolTransaction< - Pooled = PooledTransactionsElementEcRecovered, - Consensus = TransactionSignedEcRecovered, - > + 'static, + T: PoolTransaction + 'static, { type PriorityValue = U256; type Transaction = T; diff --git a/crates/transaction-pool/src/pool/mod.rs b/crates/transaction-pool/src/pool/mod.rs index 4c1a7f2c29bb..090b92fb6594 100644 --- a/crates/transaction-pool/src/pool/mod.rs +++ b/crates/transaction-pool/src/pool/mod.rs @@ -88,6 +88,7 @@ use reth_execution_types::ChangedAccount; use reth_primitives::{ BlobTransaction, BlobTransactionSidecar, PooledTransactionsElement, TransactionSigned, + TransactionSignedEcRecovered, }; use std::{ collections::{HashMap, HashSet}, @@ -318,13 +319,19 @@ where &self, tx_hashes: Vec, limit: GetPooledTransactionLimit, - ) -> Vec { + ) -> Vec + where + ::Transaction: + PoolTransaction>, + { let transactions = self.get_all(tx_hashes); let mut elements = Vec::with_capacity(transactions.len()); let mut size = 0; for transaction in transactions { let encoded_len = transaction.encoded_length(); - let tx = transaction.to_recovered_transaction().into_signed(); + let recovered: TransactionSignedEcRecovered = + transaction.transaction.clone().into_consensus().into(); + let tx = recovered.into_signed(); let pooled = if tx.is_eip4844() { // for EIP-4844 transactions, we need to fetch the blob sidecar from the blob store if let Some(blob) = self.get_blob_transaction(tx) { @@ -360,9 +367,15 @@ where pub(crate) fn get_pooled_transaction_element( &self, tx_hash: TxHash, - ) -> Option { + ) -> Option + where + ::Transaction: + PoolTransaction>, + { self.get(&tx_hash).and_then(|transaction| { - let tx = transaction.to_recovered_transaction().into_signed(); + let recovered: TransactionSignedEcRecovered = + transaction.transaction.clone().into_consensus().into(); + let tx = recovered.into_signed(); if tx.is_eip4844() { self.get_blob_transaction(tx).map(PooledTransactionsElement::BlobTransaction) } else { @@ -731,6 +744,15 @@ where self.get_pool_data().get_transactions_by_sender(sender_id) } + /// Returns the highest transaction of the address + pub(crate) fn get_highest_transaction_by_sender( + &self, + sender: Address, + ) -> Option>> { + let sender_id = self.get_sender_id(sender); + self.get_pool_data().get_highest_transaction_by_sender(sender_id) + } + /// Returns all transactions that where submitted with the given [`TransactionOrigin`] pub(crate) fn get_transactions_by_origin( &self, diff --git a/crates/transaction-pool/src/pool/txpool.rs b/crates/transaction-pool/src/pool/txpool.rs index 912e04506a19..10605565c85d 100644 --- a/crates/transaction-pool/src/pool/txpool.rs +++ b/crates/transaction-pool/src/pool/txpool.rs @@ -19,8 +19,12 @@ use crate::{ ValidPoolTransaction, U256, }; use alloy_primitives::{Address, TxHash, B256}; -use reth_primitives::constants::{ - eip4844::BLOB_TX_MIN_BLOB_GASPRICE, ETHEREUM_BLOCK_GAS_LIMIT, MIN_PROTOCOL_BASE_FEE, +use reth_primitives::{ + constants::{ + eip4844::BLOB_TX_MIN_BLOB_GASPRICE, ETHEREUM_BLOCK_GAS_LIMIT, MIN_PROTOCOL_BASE_FEE, + }, + EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID, EIP4844_TX_TYPE_ID, EIP7702_TX_TYPE_ID, + LEGACY_TX_TYPE_ID, }; use rustc_hash::FxHashMap; use smallvec::SmallVec; @@ -429,6 +433,7 @@ impl TxPool { let UpdateOutcome { promoted, discarded } = self.update_accounts(changed_senders); + self.update_transaction_type_metrics(); self.metrics.performed_state_updates.increment(1); OnNewCanonicalStateOutcome { block_hash, mined: mined_transactions, promoted, discarded } @@ -448,6 +453,32 @@ impl TxPool { self.metrics.total_transactions.set(stats.total as f64); } + /// Updates transaction type metrics for the entire pool. + pub(crate) fn update_transaction_type_metrics(&self) { + let mut legacy_count = 0; + let mut eip2930_count = 0; + let mut eip1559_count = 0; + let mut eip4844_count = 0; + let mut eip7702_count = 0; + + for tx in self.all_transactions.transactions_iter() { + match tx.transaction.tx_type() { + LEGACY_TX_TYPE_ID => legacy_count += 1, + EIP2930_TX_TYPE_ID => eip2930_count += 1, + EIP1559_TX_TYPE_ID => eip1559_count += 1, + EIP4844_TX_TYPE_ID => eip4844_count += 1, + EIP7702_TX_TYPE_ID => eip7702_count += 1, + _ => {} // Ignore other types + } + } + + self.metrics.total_legacy_transactions.set(legacy_count as f64); + self.metrics.total_eip2930_transactions.set(eip2930_count as f64); + self.metrics.total_eip1559_transactions.set(eip1559_count as f64); + self.metrics.total_eip4844_transactions.set(eip4844_count as f64); + self.metrics.total_eip7702_transactions.set(eip7702_count as f64); + } + /// Adds the transaction into the pool. /// /// This pool consists of four sub-pools: `Queued`, `Pending`, `BaseFee`, and `Blob`. diff --git a/crates/transaction-pool/src/traits.rs b/crates/transaction-pool/src/traits.rs index d4eabc73bbce..1598877ff3b6 100644 --- a/crates/transaction-pool/src/traits.rs +++ b/crates/transaction-pool/src/traits.rs @@ -44,10 +44,7 @@ pub type PeerId = alloy_primitives::B512; #[auto_impl::auto_impl(&, Arc)] pub trait TransactionPool: Send + Sync + Clone { /// The transaction type of the pool - type Transaction: PoolTransaction< - Pooled = PooledTransactionsElementEcRecovered, - Consensus = TransactionSignedEcRecovered, - >; + type Transaction: EthPoolTransaction; /// Returns stats about the pool and all sub-pools. fn pool_size(&self) -> PoolSize; @@ -337,6 +334,12 @@ pub trait TransactionPool: Send + Sync + Clone { sender: Address, ) -> Vec>>; + /// Returns the highest transaction sent by a given user + fn get_highest_transaction_by_sender( + &self, + sender: Address, + ) -> Option>>; + /// Returns a transaction sent by a given user and a nonce fn get_transaction_by_sender_and_nonce( &self, @@ -496,12 +499,12 @@ pub struct AllPoolTransactions { impl AllPoolTransactions { /// Returns an iterator over all pending [`TransactionSignedEcRecovered`] transactions. pub fn pending_recovered(&self) -> impl Iterator + '_ { - self.pending.iter().map(|tx| tx.transaction.clone().into_consensus()) + self.pending.iter().map(|tx| tx.transaction.clone().into()) } /// Returns an iterator over all queued [`TransactionSignedEcRecovered`] transactions. pub fn queued_recovered(&self) -> impl Iterator + '_ { - self.queued.iter().map(|tx| tx.transaction.clone().into_consensus()) + self.queued.iter().map(|tx| tx.transaction.clone().into()) } } @@ -647,7 +650,7 @@ pub struct CanonicalStateUpdate<'a> { pub mined_transactions: Vec, } -impl<'a> CanonicalStateUpdate<'a> { +impl CanonicalStateUpdate<'_> { /// Returns the number of the tip block. pub fn number(&self) -> u64 { self.new_tip.number @@ -813,19 +816,25 @@ pub trait PoolTransaction: fmt::Debug + Send + Sync + Clone { type TryFromConsensusError; /// Associated type representing the raw consensus variant of the transaction. - type Consensus: From + TryInto; + type Consensus: From + TryInto; /// Associated type representing the recovered pooled variant of the transaction. type Pooled: Into; /// Define a method to convert from the `Consensus` type to `Self` - fn try_from_consensus(tx: Self::Consensus) -> Result; + fn try_from_consensus(tx: Self::Consensus) -> Result { + tx.try_into() + } /// Define a method to convert from the `Self` type to `Consensus` - fn into_consensus(self) -> Self::Consensus; + fn into_consensus(self) -> Self::Consensus { + self.into() + } /// Define a method to convert from the `Pooled` type to `Self` - fn from_pooled(pooled: Self::Pooled) -> Self; + fn from_pooled(pooled: Self::Pooled) -> Self { + pooled.into() + } /// Hash of the transaction. fn hash(&self) -> &TxHash; @@ -921,12 +930,11 @@ pub trait PoolTransaction: fmt::Debug + Send + Sync + Clone { fn chain_id(&self) -> Option; } -/// An extension trait that provides additional interfaces for the -/// [`EthTransactionValidator`](crate::EthTransactionValidator). +/// Super trait for transactions that can be converted to and from Eth transactions pub trait EthPoolTransaction: PoolTransaction< - Pooled = PooledTransactionsElementEcRecovered, - Consensus = TransactionSignedEcRecovered, + Consensus: From + Into, + Pooled: From + Into, > { /// Extracts the blob sidecar from the transaction. @@ -1069,18 +1077,6 @@ impl PoolTransaction for EthPooledTransaction { type Pooled = PooledTransactionsElementEcRecovered; - fn try_from_consensus(tx: Self::Consensus) -> Result { - tx.try_into() - } - - fn into_consensus(self) -> Self::Consensus { - self.into() - } - - fn from_pooled(pooled: Self::Pooled) -> Self { - pooled.into() - } - /// Returns hash of the transaction. fn hash(&self) -> &TxHash { self.transaction.hash_ref() diff --git a/crates/transaction-pool/src/validate/mod.rs b/crates/transaction-pool/src/validate/mod.rs index b8fe7cbb1de0..4395cc97908b 100644 --- a/crates/transaction-pool/src/validate/mod.rs +++ b/crates/transaction-pool/src/validate/mod.rs @@ -7,10 +7,7 @@ use crate::{ }; use alloy_primitives::{Address, TxHash, B256, U256}; use futures_util::future::Either; -use reth_primitives::{ - BlobTransactionSidecar, PooledTransactionsElementEcRecovered, SealedBlock, - TransactionSignedEcRecovered, -}; +use reth_primitives::{BlobTransactionSidecar, SealedBlock, TransactionSignedEcRecovered}; use std::{fmt, future::Future, time::Instant}; mod constants; @@ -154,10 +151,7 @@ impl ValidTransaction { /// Provides support for validating transaction at any given state of the chain pub trait TransactionValidator: Send + Sync { /// The transaction type to validate. - type Transaction: PoolTransaction< - Pooled = PooledTransactionsElementEcRecovered, - Consensus = TransactionSignedEcRecovered, - >; + type Transaction: PoolTransaction; /// Validates the transaction and returns a [`TransactionValidationOutcome`] describing the /// validity of the given transaction. @@ -380,12 +374,12 @@ impl ValidPoolTransaction { } } -impl> ValidPoolTransaction { +impl>> ValidPoolTransaction { /// Converts to this type into a [`TransactionSignedEcRecovered`]. /// /// Note: this takes `&self` since indented usage is via `Arc`. pub fn to_recovered_transaction(&self) -> TransactionSignedEcRecovered { - self.transaction.clone().into_consensus() + self.transaction.clone().into_consensus().into() } } diff --git a/crates/trie/db/src/hashed_cursor.rs b/crates/trie/db/src/hashed_cursor.rs index bf0341c8884d..6d0b79e5a02b 100644 --- a/crates/trie/db/src/hashed_cursor.rs +++ b/crates/trie/db/src/hashed_cursor.rs @@ -11,7 +11,7 @@ use reth_trie::hashed_cursor::{HashedCursor, HashedCursorFactory, HashedStorageC #[derive(Debug)] pub struct DatabaseHashedCursorFactory<'a, TX>(&'a TX); -impl<'a, TX> Clone for DatabaseHashedCursorFactory<'a, TX> { +impl Clone for DatabaseHashedCursorFactory<'_, TX> { fn clone(&self) -> Self { Self(self.0) } @@ -24,7 +24,7 @@ impl<'a, TX> DatabaseHashedCursorFactory<'a, TX> { } } -impl<'a, TX: DbTx> HashedCursorFactory for DatabaseHashedCursorFactory<'a, TX> { +impl HashedCursorFactory for DatabaseHashedCursorFactory<'_, TX> { type AccountCursor = DatabaseHashedAccountCursor<::Cursor>; type StorageCursor = DatabaseHashedStorageCursor<::DupCursor>; diff --git a/crates/trie/db/src/prefix_set.rs b/crates/trie/db/src/prefix_set.rs index 07b87016d2b4..079fe393764d 100644 --- a/crates/trie/db/src/prefix_set.rs +++ b/crates/trie/db/src/prefix_set.rs @@ -26,7 +26,7 @@ impl<'a, TX> PrefixSetLoader<'a, TX> { } } -impl<'a, TX: DbTx> PrefixSetLoader<'a, TX> { +impl PrefixSetLoader<'_, TX> { /// Load all account and storage changes for the given block range. pub fn load(self, range: RangeInclusive) -> Result { // Initialize prefix sets. diff --git a/crates/trie/db/src/trie_cursor.rs b/crates/trie/db/src/trie_cursor.rs index 601100b3faee..bfded342ba04 100644 --- a/crates/trie/db/src/trie_cursor.rs +++ b/crates/trie/db/src/trie_cursor.rs @@ -19,7 +19,7 @@ use reth_trie_common::StorageTrieEntry; #[derive(Debug)] pub struct DatabaseTrieCursorFactory<'a, TX>(&'a TX); -impl<'a, TX> Clone for DatabaseTrieCursorFactory<'a, TX> { +impl Clone for DatabaseTrieCursorFactory<'_, TX> { fn clone(&self) -> Self { Self(self.0) } @@ -33,7 +33,7 @@ impl<'a, TX> DatabaseTrieCursorFactory<'a, TX> { } /// Implementation of the trie cursor factory for a database transaction. -impl<'a, TX: DbTx> TrieCursorFactory for DatabaseTrieCursorFactory<'a, TX> { +impl TrieCursorFactory for DatabaseTrieCursorFactory<'_, TX> { type AccountTrieCursor = DatabaseAccountTrieCursor<::Cursor>; type StorageTrieCursor = DatabaseStorageTrieCursor<::DupCursor>; diff --git a/crates/trie/trie/src/forward_cursor.rs b/crates/trie/trie/src/forward_cursor.rs index da71326cea90..6db214bb51a3 100644 --- a/crates/trie/trie/src/forward_cursor.rs +++ b/crates/trie/trie/src/forward_cursor.rs @@ -21,7 +21,7 @@ impl<'a, K, V> ForwardInMemoryCursor<'a, K, V> { } } -impl<'a, K, V> ForwardInMemoryCursor<'a, K, V> +impl ForwardInMemoryCursor<'_, K, V> where K: PartialOrd + Clone, V: Clone, diff --git a/crates/trie/trie/src/hashed_cursor/post_state.rs b/crates/trie/trie/src/hashed_cursor/post_state.rs index 53a2cdb3bb64..678914191527 100644 --- a/crates/trie/trie/src/hashed_cursor/post_state.rs +++ b/crates/trie/trie/src/hashed_cursor/post_state.rs @@ -132,7 +132,7 @@ where } } -impl<'a, C> HashedCursor for HashedPostStateAccountCursor<'a, C> +impl HashedCursor for HashedPostStateAccountCursor<'_, C> where C: HashedCursor, { @@ -276,7 +276,7 @@ where } } -impl<'a, C> HashedCursor for HashedPostStateStorageCursor<'a, C> +impl HashedCursor for HashedPostStateStorageCursor<'_, C> where C: HashedStorageCursor, { @@ -304,7 +304,7 @@ where } } -impl<'a, C> HashedStorageCursor for HashedPostStateStorageCursor<'a, C> +impl HashedStorageCursor for HashedPostStateStorageCursor<'_, C> where C: HashedStorageCursor, { diff --git a/crates/trie/trie/src/trie_cursor/in_memory.rs b/crates/trie/trie/src/trie_cursor/in_memory.rs index 82ffcfb428f9..0f00191378ba 100644 --- a/crates/trie/trie/src/trie_cursor/in_memory.rs +++ b/crates/trie/trie/src/trie_cursor/in_memory.rs @@ -113,7 +113,7 @@ impl<'a, C: TrieCursor> InMemoryAccountTrieCursor<'a, C> { } } -impl<'a, C: TrieCursor> TrieCursor for InMemoryAccountTrieCursor<'a, C> { +impl TrieCursor for InMemoryAccountTrieCursor<'_, C> { fn seek_exact( &mut self, key: Nibbles, @@ -188,7 +188,7 @@ impl<'a, C> InMemoryStorageTrieCursor<'a, C> { } } -impl<'a, C: TrieCursor> InMemoryStorageTrieCursor<'a, C> { +impl InMemoryStorageTrieCursor<'_, C> { fn seek_inner( &mut self, key: Nibbles, @@ -237,7 +237,7 @@ impl<'a, C: TrieCursor> InMemoryStorageTrieCursor<'a, C> { } } -impl<'a, C: TrieCursor> TrieCursor for InMemoryStorageTrieCursor<'a, C> { +impl TrieCursor for InMemoryStorageTrieCursor<'_, C> { fn seek_exact( &mut self, key: Nibbles, diff --git a/crates/trie/trie/src/updates.rs b/crates/trie/trie/src/updates.rs index a2d3d67363ea..03e80cf52e53 100644 --- a/crates/trie/trie/src/updates.rs +++ b/crates/trie/trie/src/updates.rs @@ -455,7 +455,7 @@ pub mod serde_bincode_compat { } } - impl<'a> SerializeAs for TrieUpdates<'a> { + impl SerializeAs for TrieUpdates<'_> { fn serialize_as(source: &super::TrieUpdates, serializer: S) -> Result where S: Serializer, @@ -515,7 +515,7 @@ pub mod serde_bincode_compat { } } - impl<'a> SerializeAs for StorageTrieUpdates<'a> { + impl SerializeAs for StorageTrieUpdates<'_> { fn serialize_as( source: &super::StorageTrieUpdates, serializer: S, diff --git a/docs/repo/layout.md b/docs/repo/layout.md index 6ba14e652bf1..8eb0782b988c 100644 --- a/docs/repo/layout.md +++ b/docs/repo/layout.md @@ -132,7 +132,6 @@ The IPC transport lives in [`rpc/ipc`](../../crates/rpc/ipc). - [`rpc/rpc-api`](../../crates/rpc/rpc-api): RPC traits - Supported transports: HTTP, WS, IPC - Supported namespaces: `eth_`, `engine_`, `debug_` -- [`rpc/rpc-types`](../../crates/rpc/rpc-types): Types relevant for the RPC endpoints above, grouped by namespace - [`rpc/rpc-eth-api`](../../crates/rpc/rpc-eth-api/): Reth RPC 'eth' namespace API (including interface and implementation), this crate is re-exported by `rpc/rpc-api` - [`rpc/rpc-eth-types`](../../crates/rpc/rpc-eth-types/): Types `supporting implementation` of 'eth' namespace RPC server API - [`rpc/rpc-server-types`](../../crates/rpc/rpc-server-types/): RPC server types and constants diff --git a/etc/grafana/dashboards/overview.json b/etc/grafana/dashboards/overview.json index 39849f20f090..15786764f429 100644 --- a/etc/grafana/dashboards/overview.json +++ b/etc/grafana/dashboards/overview.json @@ -2,7 +2,7 @@ "__inputs": [ { "name": "DS_PROMETHEUS", - "label": "Prometheus", + "label": "prometheus", "description": "", "type": "datasource", "pluginId": "prometheus", @@ -5732,6 +5732,79 @@ "title": "Engine API getPayloadBodies Latency", "type": "timeseries" }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Counts the number of failed response deliveries due to client request termination.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "mode": "none" + } + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 198 + }, + "id": 213, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "expr": "consensus_engine_beacon_failed_new_payload_response_deliveries{instance=~\"$instance\"}", + "legendFormat": "Failed NewPayload Deliveries", + "refId": "A" + }, + { + "expr": "consensus_engine_beacon_failed_forkchoice_updated_response_deliveries{instance=~\"$instance\"}", + "legendFormat": "Failed ForkchoiceUpdated Deliveries", + "refId": "B" + } + ], + "title": "Failed Engine API Response Deliveries", + "type": "timeseries" + }, { "collapsed": false, "gridPos": { @@ -7787,7 +7860,7 @@ "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, - "description": "The total number of canonical state notifications sent to an ExEx.", + "description": "The total number of canonical state notifications sent to ExExes.", "fieldConfig": { "defaults": { "color": { @@ -7884,7 +7957,7 @@ "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, - "description": "The total number of events an ExEx has sent to the manager.", + "description": "The total number of events ExExes have sent to the manager.", "fieldConfig": { "defaults": { "color": { @@ -7981,7 +8054,7 @@ "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, - "description": "Current and Max capacity of the internal state notifications buffer.", + "description": "Current and Maximum capacity of the internal state notifications buffer.", "fieldConfig": { "defaults": { "color": { @@ -8187,7 +8260,7 @@ "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, - "description": "Number of ExExs on the node", + "description": "Total number of ExExes installed in the node", "fieldConfig": { "defaults": { "color": { @@ -8250,7 +8323,7 @@ "refId": "A" } ], - "title": "Number of ExExs", + "title": "Number of ExExes", "type": "stat" }, { @@ -8261,6 +8334,343 @@ "x": 0, "y": 308 }, + "id": 241, + "panels": [], + "title": "Execution Extensions Write-Ahead Log", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 309 + }, + "id": 243, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_exex_wal_lowest_committed_block_height{instance=~\"$instance\"}", + "hide": false, + "instant": false, + "legendFormat": "Lowest Block", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_exex_wal_highest_committed_block_height{instance=~\"$instance\"}", + "hide": false, + "instant": false, + "legendFormat": "Highest Block", + "range": true, + "refId": "C" + } + ], + "title": "Current Committed Block Heights", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 309 + }, + "id": 244, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_exex_wal_committed_blocks_count{instance=~\"$instance\"}", + "hide": false, + "instant": false, + "legendFormat": "Committed Blocks", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_exex_wal_notifications_count{instance=~\"$instance\"}", + "hide": false, + "instant": false, + "legendFormat": "Notifications", + "range": true, + "refId": "B" + } + ], + "title": "Number of entities", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 317 + }, + "id": 245, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_exex_wal_size_bytes{instance=~\"$instance\"}", + "hide": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "C" + } + ], + "title": "Total size of all notifications", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 325 + }, "id": 226, "panels": [], "title": "Eth Requests", @@ -8357,7 +8767,7 @@ "h": 8, "w": 12, "x": 0, - "y": 309 + "y": 326 }, "id": 225, "options": { @@ -8486,7 +8896,7 @@ "h": 8, "w": 12, "x": 12, - "y": 309 + "y": 326 }, "id": 227, "options": { @@ -8615,7 +9025,7 @@ "h": 8, "w": 12, "x": 0, - "y": 317 + "y": 334 }, "id": 235, "options": { @@ -8744,7 +9154,7 @@ "h": 8, "w": 12, "x": 12, - "y": 317 + "y": 334 }, "id": 234, "options": { @@ -8821,6 +9231,6 @@ "timezone": "", "title": "Reth", "uid": "2k8BXz24x", - "version": 3, + "version": 8, "weekStart": "" } diff --git a/etc/grafana/dashboards/reth-mempool.json b/etc/grafana/dashboards/reth-mempool.json index 41be1dc411eb..ebb693184a5a 100644 --- a/etc/grafana/dashboards/reth-mempool.json +++ b/etc/grafana/dashboards/reth-mempool.json @@ -15,7 +15,7 @@ "type": "grafana", "id": "grafana", "name": "Grafana", - "version": "11.1.3" + "version": "11.2.0" }, { "type": "panel", @@ -131,7 +131,7 @@ "textMode": "name", "wideLayout": true }, - "pluginVersion": "11.1.3", + "pluginVersion": "11.2.0", "targets": [ { "datasource": { @@ -201,7 +201,7 @@ "textMode": "name", "wideLayout": true }, - "pluginVersion": "11.1.3", + "pluginVersion": "11.2.0", "targets": [ { "datasource": { @@ -271,7 +271,7 @@ "textMode": "name", "wideLayout": true }, - "pluginVersion": "11.1.3", + "pluginVersion": "11.2.0", "targets": [ { "datasource": { @@ -341,7 +341,7 @@ "textMode": "name", "wideLayout": true }, - "pluginVersion": "11.1.3", + "pluginVersion": "11.2.0", "targets": [ { "datasource": { @@ -411,7 +411,7 @@ "textMode": "name", "wideLayout": true }, - "pluginVersion": "11.1.3", + "pluginVersion": "11.2.0", "targets": [ { "datasource": { @@ -481,7 +481,7 @@ "textMode": "name", "wideLayout": true }, - "pluginVersion": "11.1.3", + "pluginVersion": "11.2.0", "targets": [ { "datasource": { @@ -534,6 +534,7 @@ "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, + "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", @@ -686,6 +687,7 @@ "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, + "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", @@ -817,6 +819,7 @@ "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, + "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", @@ -969,6 +972,7 @@ "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, + "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", @@ -1099,6 +1103,7 @@ "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, + "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", @@ -1261,6 +1266,7 @@ "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, + "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", @@ -1357,6 +1363,7 @@ "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, + "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", @@ -1504,6 +1511,7 @@ "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, + "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", @@ -1605,6 +1613,7 @@ "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, + "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", @@ -1756,6 +1765,7 @@ "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, + "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", @@ -1852,6 +1862,7 @@ "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, + "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", @@ -1971,6 +1982,7 @@ "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, + "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", @@ -2101,6 +2113,7 @@ "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, + "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", @@ -2323,6 +2336,7 @@ "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, + "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", @@ -2442,6 +2456,7 @@ "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, + "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", @@ -2578,6 +2593,7 @@ "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, + "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", @@ -2679,6 +2695,177 @@ "title": "Fetch Hashes Pending Fetch Duration", "type": "timeseries" }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Number of all transactions of all sub-pools by type", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 69 + }, + "id": 218, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "reth_transaction_pool_total_legacy_transactions{instance=\"$instance\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "Legacy", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "reth_transaction_pool_total_eip2930_transactions{instance=\"$instance\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "EIP-2930", + "range": true, + "refId": "B", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "reth_transaction_pool_total_eip1559_transactions{instance=\"$instance\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "EIP-1559", + "range": true, + "refId": "C", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "reth_transaction_pool_total_eip4844_transactions{instance=\"$instance\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "EIP-4844", + "range": true, + "refId": "D", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "reth_transaction_pool_total_eip7702_transactions{instance=\"$instance\"}", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "EIP-7702", + "range": true, + "refId": "E", + "useBackend": false + } + ], + "title": "Transactions by Type in Pool", + "type": "timeseries" + }, { "datasource": { "type": "prometheus", @@ -2697,6 +2884,7 @@ "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, + "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", @@ -2865,6 +3053,7 @@ "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, + "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", @@ -2960,6 +3149,7 @@ "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, + "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", @@ -3102,6 +3292,7 @@ "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, + "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", @@ -3408,6 +3599,7 @@ "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, + "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", @@ -3524,6 +3716,6 @@ "timezone": "", "title": "Reth - Transaction Pool", "uid": "bee34f59-c79c-4669-a000-198057b3703d", - "version": 3, + "version": 2, "weekStart": "" } \ No newline at end of file diff --git a/examples/beacon-api-sidecar-fetcher/src/main.rs b/examples/beacon-api-sidecar-fetcher/src/main.rs index 7d8880ca185c..a0b9b6e01ec8 100644 --- a/examples/beacon-api-sidecar-fetcher/src/main.rs +++ b/examples/beacon-api-sidecar-fetcher/src/main.rs @@ -23,7 +23,7 @@ use clap::Parser; use futures_util::{stream::FuturesUnordered, StreamExt}; use mined_sidecar::MinedSidecarStream; use reth::{ - args::utils::EthereumChainSpecParser, builder::NodeHandle, cli::Cli, + builder::NodeHandle, chainspec::EthereumChainSpecParser, cli::Cli, providers::CanonStateSubscriptions, }; use reth_node_ethereum::EthereumNode; diff --git a/examples/beacon-api-sse/src/main.rs b/examples/beacon-api-sse/src/main.rs index 81535ef6140b..243511d4960e 100644 --- a/examples/beacon-api-sse/src/main.rs +++ b/examples/beacon-api-sse/src/main.rs @@ -21,7 +21,7 @@ use alloy_rpc_types_beacon::events::PayloadAttributesEvent; use clap::Parser; use futures_util::stream::StreamExt; use mev_share_sse::{client::EventStream, EventClient}; -use reth::{args::utils::EthereumChainSpecParser, cli::Cli}; +use reth::{chainspec::EthereumChainSpecParser, cli::Cli}; use reth_node_ethereum::EthereumNode; use std::net::{IpAddr, Ipv4Addr}; use tracing::{info, warn}; diff --git a/examples/custom-engine-types/src/main.rs b/examples/custom-engine-types/src/main.rs index 213a156af8fd..34f8186be7f8 100644 --- a/examples/custom-engine-types/src/main.rs +++ b/examples/custom-engine-types/src/main.rs @@ -253,6 +253,10 @@ where .consensus(EthereumConsensusBuilder::default()) .engine_validator(CustomEngineValidatorBuilder::default()) } + + fn add_ons(&self) -> Self::AddOns { + EthereumAddOns::default() + } } /// A custom payload service builder that supports the custom engine types diff --git a/examples/custom-evm/src/main.rs b/examples/custom-evm/src/main.rs index d931c3b275bf..9c421f9c6a59 100644 --- a/examples/custom-evm/src/main.rs +++ b/examples/custom-evm/src/main.rs @@ -226,7 +226,7 @@ async fn main() -> eyre::Result<()> { .executor(MyExecutorBuilder::default()) .payload(MyPayloadBuilder::default()), ) - .with_add_ons::() + .with_add_ons(EthereumAddOns::default()) .launch() .await .unwrap(); diff --git a/examples/custom-inspector/src/main.rs b/examples/custom-inspector/src/main.rs index 700de274e681..12b7620f4adc 100644 --- a/examples/custom-inspector/src/main.rs +++ b/examples/custom-inspector/src/main.rs @@ -15,8 +15,8 @@ use alloy_rpc_types::state::EvmOverrides; use clap::Parser; use futures_util::StreamExt; use reth::{ - args::utils::EthereumChainSpecParser, builder::NodeHandle, + chainspec::EthereumChainSpecParser, cli::Cli, primitives::BlockNumberOrTag, revm::{ diff --git a/examples/custom-node-components/src/main.rs b/examples/custom-node-components/src/main.rs index 1faca73d25b0..d00b8a70224a 100644 --- a/examples/custom-node-components/src/main.rs +++ b/examples/custom-node-components/src/main.rs @@ -25,7 +25,7 @@ fn main() { // Configure the components of the node // use default ethereum components but use our custom pool .with_components(EthereumNode::components().pool(CustomPoolBuilder::default())) - .with_add_ons::() + .with_add_ons(EthereumAddOns::default()) .launch() .await?; diff --git a/examples/custom-payload-builder/src/main.rs b/examples/custom-payload-builder/src/main.rs index 5ed414eb850b..e46b969adaa1 100644 --- a/examples/custom-payload-builder/src/main.rs +++ b/examples/custom-payload-builder/src/main.rs @@ -81,7 +81,7 @@ fn main() { .with_components( EthereumNode::components().payload(CustomPayloadBuilder::default()), ) - .with_add_ons::() + .with_add_ons(EthereumAddOns::default()) .launch() .await?; diff --git a/examples/node-custom-rpc/src/main.rs b/examples/node-custom-rpc/src/main.rs index 5aeecfd2915c..92e0bfea26e9 100644 --- a/examples/node-custom-rpc/src/main.rs +++ b/examples/node-custom-rpc/src/main.rs @@ -14,7 +14,7 @@ use clap::Parser; use jsonrpsee::{core::RpcResult, proc_macros::rpc}; -use reth::{args::utils::EthereumChainSpecParser, cli::Cli}; +use reth::{chainspec::EthereumChainSpecParser, cli::Cli}; use reth_node_ethereum::EthereumNode; use reth_transaction_pool::TransactionPool; diff --git a/examples/stateful-precompile/src/main.rs b/examples/stateful-precompile/src/main.rs index 05a6fd86c935..26ebdfe4124b 100644 --- a/examples/stateful-precompile/src/main.rs +++ b/examples/stateful-precompile/src/main.rs @@ -263,7 +263,7 @@ async fn main() -> eyre::Result<()> { .with_types::() // use default ethereum components but with our executor .with_components(EthereumNode::components().executor(MyExecutorBuilder::default())) - .with_add_ons::() + .with_add_ons(EthereumAddOns::default()) .launch() .await .unwrap(); diff --git a/examples/txpool-tracing/src/main.rs b/examples/txpool-tracing/src/main.rs index f8c2e19d203a..94f800987a96 100644 --- a/examples/txpool-tracing/src/main.rs +++ b/examples/txpool-tracing/src/main.rs @@ -15,7 +15,7 @@ use alloy_rpc_types_trace::{parity::TraceType, tracerequest::TraceCallRequest}; use clap::Parser; use futures_util::StreamExt; use reth::{ - args::utils::EthereumChainSpecParser, builder::NodeHandle, cli::Cli, + builder::NodeHandle, chainspec::EthereumChainSpecParser, cli::Cli, rpc::compat::transaction::transaction_to_call_request, transaction_pool::TransactionPool, }; use reth_node_ethereum::node::EthereumNode;