diff --git a/.config/zepter.yaml b/.config/zepter.yaml new file mode 100644 index 000000000000..8c6425f4ff0d --- /dev/null +++ b/.config/zepter.yaml @@ -0,0 +1,40 @@ +version: + format: 1 + # Minimum zepter version that is expected to work. This is just for printing a nice error + # message when someone tries to use an older version. + binary: 0.13.2 + +# The examples in the following comments assume crate `A` to have a dependency on crate `B`. +workflows: + check: + - [ + "lint", + # Check that `A` activates the features of `B`. + "propagate-feature", + # These are the features to check: + "--features=std,optimism,dev,asm-keccak,jemalloc,jemalloc-prof,tracy-allocator,serde-bincode-compat,serde,test-utils,arbitrary,bench", + # Do not try to add a new section into `[features]` of `A` only because `B` expose that feature. There are edge-cases where this is still needed, but we can add them manually. + "--left-side-feature-missing=ignore", + # Ignore the case that `A` it outside of the workspace. Otherwise it will report errors in external dependencies that we have no influence on. + "--left-side-outside-workspace=ignore", + # Auxillary flags: + "--offline", + "--locked", + "--show-path", + "--quiet", + ] + default: + # Running `zepter` with no subcommand will check & fix. + - [$check.0, "--fix"] + +# Will be displayed when any workflow fails: +help: + text: | + Reth uses the Zepter CLI to detect abnormalities in Cargo features, e.g. missing propagation. + + It looks like one more more checks failed; please check the console output. + + You can try to automatically address them by installing zepter (`cargo install zepter --locked`) and simply running `zepter` in the workspace root. + links: + - "https://github.com/paradigmxyz/reth/pull/11888" + - "https://github.com/ggwpez/zepter" diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index afb3d6776320..488e6c90cf7f 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -43,6 +43,6 @@ crates/tasks/ @mattsse crates/tokio-util/ @fgimenez @emhane crates/tracing/ @onbjerg crates/transaction-pool/ @mattsse -crates/trie/ @rkrasiuk @Rjected +crates/trie/ @rkrasiuk @Rjected @shekhirin etc/ @Rjected @onbjerg @shekhirin .github/ @onbjerg @gakonst @DaniPopes diff --git a/.github/assets/check_wasm.sh b/.github/assets/check_wasm.sh index 8d53f457af91..3b2f2d6b7e6c 100755 --- a/.github/assets/check_wasm.sh +++ b/.github/assets/check_wasm.sh @@ -3,7 +3,10 @@ set +e # Disable immediate exit on error # Array of crates to compile crates=($(cargo metadata --format-version=1 --no-deps | jq -r '.packages[].name' | grep '^reth' | sort)) + # Array of crates to exclude +# Used with the `contains` function. +# shellcheck disable=SC2034 exclude_crates=( # The following are not working yet, but known to be fixable reth-exex-types # https://github.com/paradigmxyz/reth/issues/9946 @@ -30,12 +33,9 @@ exclude_crates=( reth-engine-util reth-eth-wire reth-ethereum-cli - reth-ethereum-engine reth-ethereum-engine-primitives reth-ethereum-payload-builder reth-etl - reth-evm-ethereum - reth-execution-errors reth-exex reth-exex-test-utils reth-ipc @@ -49,7 +49,6 @@ exclude_crates=( reth-node-events reth-node-metrics reth-optimism-cli - reth-optimism-evm reth-optimism-node reth-optimism-payload-builder reth-optimism-rpc @@ -63,9 +62,7 @@ exclude_crates=( reth-rpc-eth-api reth-rpc-eth-types reth-rpc-layer - reth-rpc-types reth-stages - reth-storage-errors reth-engine-local # The following are not supposed to be working reth # all of the crates below diff --git a/.github/workflows/eth-sync.yml b/.github/workflows/eth-sync.yml deleted file mode 100644 index d2fcc8ee2b7c..000000000000 --- a/.github/workflows/eth-sync.yml +++ /dev/null @@ -1,52 +0,0 @@ -# Runs an ethereum mainnet sync test. - -name: eth-sync-test - -on: - pull_request: - merge_group: - push: - branches: [ main, scroll ] - -env: - CARGO_TERM_COLOR: always - -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - sync: - name: sync / 100k blocks - # Only run sync tests in merge groups - if: github.event_name == 'merge_group' - runs-on: ubuntu-latest - env: - RUST_LOG: info,sync=error - RUST_BACKTRACE: 1 - timeout-minutes: 60 - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 - with: - cache-on-failure: true - - name: Build reth - run: | - cargo install --features asm-keccak,jemalloc --path bin/reth - - name: Run sync - run: | - reth node \ - --debug.tip 0x91c90676cab257a59cd956d7cb0bceb9b1a71d79755c23c7277a0697ccfaf8c4 \ - --debug.max-block 100000 \ - --debug.terminate - - name: Verify the target block hash - run: | - reth db get static-file headers 100000 \ - | grep 0x91c90676cab257a59cd956d7cb0bceb9b1a71d79755c23c7277a0697ccfaf8c4 - - name: Run stage unwind for 100 blocks - run: | - reth stage unwind num-blocks 100 - - name: Run stage unwind to block hash - run: | - reth stage unwind to-block 0x52e0509d33a988ef807058e2980099ee3070187f7333aae12b64d4d675f34c5a diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 5f1e413d220d..85e13aa648be 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -103,7 +103,7 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: - toolchain: "1.81" # MSRV + toolchain: "1.82" # MSRV - uses: Swatinem/rust-cache@v2 with: cache-on-failure: true @@ -163,7 +163,7 @@ jobs: - uses: dtolnay/rust-toolchain@nightly - uses: dtolnay/rust-toolchain@master with: - toolchain: "1.81" # MSRV + toolchain: "1.82" # MSRV - uses: Swatinem/rust-cache@v2 with: cache-on-failure: true @@ -219,6 +219,22 @@ jobs: env: RUSTFLAGS: -D warnings + # Check crates correctly propagate features + feature-propagation: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@v4 + - name: fetch deps + run: | + # Eagerly pull dependencies + time cargo metadata --format-version=1 --locked > /dev/null + - name: run zepter + run: | + cargo install zepter -f --locked + zepter --version + time zepter run check + lint-success: name: lint success runs-on: ubuntu-latest @@ -236,6 +252,7 @@ jobs: - grafana - no-test-deps - features + - feature-propagation timeout-minutes: 30 steps: - name: Decide whether the needed jobs succeeded or failed diff --git a/.github/workflows/op-sync.yml b/.github/workflows/op-sync.yml deleted file mode 100644 index 37ef5a87c404..000000000000 --- a/.github/workflows/op-sync.yml +++ /dev/null @@ -1,54 +0,0 @@ -# Runs a base mainnet sync test. - -name: op-sync-test - -on: - pull_request: - merge_group: - push: - branches: [ main, scroll ] - -env: - CARGO_TERM_COLOR: always - -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - sync: - name: op sync / 10k blocks - # Only run sync tests in merge groups - if: github.event_name == 'merge_group' - runs-on: ubuntu-latest - env: - RUST_LOG: info,sync=error - RUST_BACKTRACE: 1 - timeout-minutes: 60 - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 - with: - cache-on-failure: true - - name: Build op-reth - run: make install-op - - name: Run sync - # https://basescan.org/block/10000 - run: | - op-reth node \ - --chain base \ - --debug.tip 0xbb9b85352c7ebca6ba8efc63bd66cecd038c92ec8ebd02e153a3e0b197e672b7 \ - --debug.max-block 10000 \ - --debug.terminate - - name: Verify the target block hash - run: | - op-reth db --chain base get static-file headers 10000 \ - | grep 0xbb9b85352c7ebca6ba8efc63bd66cecd038c92ec8ebd02e153a3e0b197e672b7 - - name: Run stage unwind for 100 blocks - run: | - op-reth stage --chain base unwind num-blocks 100 - - name: Run stage unwind to block hash - run: | - op-reth stage --chain base unwind to-block 0x118a6e922a8c6cab221fc5adfe5056d2b72d58c6580e9c5629de55299e2cf8de - diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml new file mode 100644 index 000000000000..1f7d592a0405 --- /dev/null +++ b/.github/workflows/sync.yml @@ -0,0 +1,62 @@ +# Runs sync tests. + +name: sync test + +on: + merge_group: + +env: + CARGO_TERM_COLOR: always + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + sync: + name: sync (${{ matrix.chain.bin }}) + runs-on: ubuntu-latest + env: + RUST_LOG: info,sync=error + RUST_BACKTRACE: 1 + timeout-minutes: 60 + strategy: + matrix: + chain: + - build: install + bin: reth + chain: mainnet + tip: "0x91c90676cab257a59cd956d7cb0bceb9b1a71d79755c23c7277a0697ccfaf8c4" + block: 100000 + unwind-target: "0x52e0509d33a988ef807058e2980099ee3070187f7333aae12b64d4d675f34c5a" + - build: install-op + bin: op-reth + chain: base + tip: "0xbb9b85352c7ebca6ba8efc63bd66cecd038c92ec8ebd02e153a3e0b197e672b7" + block: 10000 + unwind-target: "0x118a6e922a8c6cab221fc5adfe5056d2b72d58c6580e9c5629de55299e2cf8de" + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: true + - name: Build ${{ matrix.chain.bin }} + run: make ${{ matrix.chain.build }} + - name: Run sync + run: | + ${{ matrix.chain.bin }} node \ + --chain ${{ matrix.chain.chain }} \ + --debug.tip ${{ matrix.chain.tip }} \ + --debug.max-block ${{ matrix.chain.block }} \ + --debug.terminate + - name: Verify the target block hash + run: | + ${{ matrix.chain.bin }} db --chain ${{ matrix.chain.chain }} get static-file headers ${{ matrix.chain.block }} \ + | grep ${{ matrix.chain.tip }} + - name: Run stage unwind for 100 blocks + run: | + ${{ matrix.chain.bin }} stage --chain ${{ matrix.chain.chain }} unwind num-blocks 100 + - name: Run stage unwind to block hash + run: | + ${{ matrix.chain.bin }} stage --chain ${{ matrix.chain.chain }} unwind to-block ${{ matrix.chain.unwind-target }} diff --git a/Cargo.lock b/Cargo.lock index 828444080432..19287e6669a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -97,9 +97,9 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "alloy-chains" -version = "0.1.38" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "156bfc5dcd52ef9a5f33381701fa03310317e14c65093a9430d3e3557b08dcd3" +checksum = "d4932d790c723181807738cf1ac68198ab581cd699545b155601332541ee47bd" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -112,9 +112,9 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "705687d5bfd019fee57cf9e206b27b30a9a9617535d5590a02b171e813208f8e" +checksum = "42642aed67f938363d9c7543e5ca4163cfb4205d9ec15fe933dc4e865d2932dd" dependencies = [ "alloy-eips", "alloy-primitives", @@ -161,9 +161,9 @@ dependencies = [ [[package]] name = "alloy-eip7702" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea59dc42102bc9a1905dc57901edc6dd48b9f38115df86c7d252acba70d71d04" +checksum = "eeffd2590ce780ddfaa9d0ae340eb2b4e08627650c4676eef537cef0b4bf535d" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -176,9 +176,9 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ffb906284a1e1f63c4607da2068c8197458a352d0b3e9796e67353d72a9be85" +checksum = "9fbc52a30df46f9831ed74557dfad0d94b12420393662a8b9ef90e2d6c8cb4b0" dependencies = [ "alloy-eip2930", "alloy-eip7702", @@ -188,6 +188,8 @@ dependencies = [ "arbitrary", "c-kzg", "derive_more 1.0.0", + "ethereum_ssz", + "ethereum_ssz_derive", "once_cell", "serde", "sha2 0.10.8", @@ -195,9 +197,9 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8429cf4554eed9b40feec7f4451113e76596086447550275e3def933faf47ce3" +checksum = "0787d1688b9806290313cc335d416cc7ee39b11e3245f3d218544c62572d92ba" dependencies = [ "alloy-primitives", "alloy-serde", @@ -218,9 +220,9 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fa8a1a3c4cbd221f2b8e3693aeb328fca79a757fe556ed08e47bbbc2a70db7" +checksum = "d55a16a5f9ca498a217c060414bcd1c43e934235dc8058b31b87dcd69ff4f105" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -232,9 +234,9 @@ dependencies = [ [[package]] name = "alloy-network" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85fa23a6a9d612b52e402c995f2d582c25165ec03ac6edf64c861a76bc5b87cd" +checksum = "3d236a8c3e1d5adc09b1b63c81815fc9b757d9a4ba9482cc899f9679b55dd437" dependencies = [ "alloy-consensus", "alloy-eips", @@ -253,9 +255,9 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "801492711d4392b2ccf5fc0bc69e299fa1aab15167d74dcaa9aab96a54f684bd" +checksum = "cd15a0990fa8a56d85a42d6a689719aa4eebf5e2f1a5c5354658c0bfc52cac9a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -266,9 +268,9 @@ dependencies = [ [[package]] name = "alloy-node-bindings" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f1334a738aa1710cb8227441b3fcc319202ce78e967ef37406940242df4a454" +checksum = "2249f3c3ce446cf4063fe3d1aa7530823643c2706a1cc63045e0683ebc497a0a" dependencies = [ "alloy-genesis", "alloy-primitives", @@ -315,9 +317,9 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcfaa4ffec0af04e3555686b8aacbcdf7d13638133a0672749209069750f78a6" +checksum = "316f522bb6f9ac3805132112197957013b570e20cfdad058e8339dae6030c849" dependencies = [ "alloy-chains", "alloy-consensus", @@ -341,21 +343,24 @@ dependencies = [ "futures", "futures-utils-wasm", "lru", + "parking_lot 0.12.3", "pin-project", "reqwest", + "schnellru", "serde", "serde_json", "thiserror", "tokio", "tracing", "url", + "wasmtimer", ] [[package]] name = "alloy-pubsub" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f32cef487122ae75c91eb50154c70801d71fabdb976fec6c49e0af5e6486ab15" +checksum = "222cd9b17b1c5ad48de51a88ffbdb17f17145170288f22662f80ac88739125e6" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -389,14 +394,14 @@ checksum = "4d0f2d905ebd295e7effec65e5f6868d153936130ae718352771de3e7d03c75c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] name = "alloy-rpc-client" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "370143ed581aace6e663342d21d209c6b2e34ee6142f7d6675adb518deeaf0dc" +checksum = "5b2ab59712c594c9624aaa69e38e4d38f180cb569f1fa46cdaf8c21fd50793e5" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -414,13 +419,14 @@ dependencies = [ "tower 0.5.1", "tracing", "url", + "wasmtimer", ] [[package]] name = "alloy-rpc-types" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ffc534b7919e18f35e3aa1f507b6f3d9d92ec298463a9f6beaac112809d8d06" +checksum = "ba21284319e12d053baa204d438db6c1577aedd94c1298e4becefdac1f9cec87" dependencies = [ "alloy-primitives", "alloy-rpc-types-engine", @@ -431,9 +437,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-admin" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb520ed46cc5b7d8c014a73fdd77b6a310383a2a5c0a5ae3c9b8055881f062b7" +checksum = "416cc9f391d0b876c4c8da85f7131e771a88a55b917cc9a35e1724d9409e3b1c" dependencies = [ "alloy-genesis", "alloy-primitives", @@ -443,9 +449,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-anvil" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d780adaa5d95b07ad92006b2feb68ecfa7e2015f7d5976ceaac4c906c73ebd07" +checksum = "ba40bea86c3102b9ed9b3be579e32e0b3e54e766248d873de5fc0437238c8df2" dependencies = [ "alloy-primitives", "alloy-serde", @@ -454,9 +460,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-beacon" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a8dc5980fe30203d698627cddb5f0cedc57f900c8b5e1229c8b9448e37acb4a" +checksum = "b535781fe224c101c3d957b514cb9f438d165ff0280e5c0b2f87a0d9a2950593" dependencies = [ "alloy-eips", "alloy-primitives", @@ -468,9 +474,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-debug" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59d8f8c5bfb160081a772f1f68eb9a37e8929c4ef74e5d01f5b78c2b645a5c5e" +checksum = "4303deacf4cbf12ed4431a5a1bbc3284f0defb4b8b72d9aa2b888656cc5ae657" dependencies = [ "alloy-primitives", "serde", @@ -478,9 +484,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0285c4c09f838ab830048b780d7f4a4f460f309aa1194bb049843309524c64c" +checksum = "44848fced3b42260b9cb61f22102246636dfe5a2d0132f8d10a617df3cb1a74b" dependencies = [ "alloy-consensus", "alloy-eips", @@ -488,6 +494,8 @@ dependencies = [ "alloy-rlp", "alloy-serde", "derive_more 1.0.0", + "ethereum_ssz", + "ethereum_ssz_derive", "jsonrpsee-types", "jsonwebtoken", "rand 0.8.5", @@ -497,9 +505,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413f4aa3ccf2c3e4234a047c5fa4727916d7daf25a89f9b765df0ba09784fd87" +checksum = "35894711990019fafff0012b82b9176cbb744516eb2a9bbe6b8e5cae522163ee" dependencies = [ "alloy-consensus", "alloy-eips", @@ -508,6 +516,7 @@ dependencies = [ "alloy-rlp", "alloy-serde", "alloy-sol-types", + "arbitrary", "derive_more 1.0.0", "itertools 0.13.0", "jsonrpsee-types", @@ -517,9 +526,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-mev" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cec23ce56c869eec5f6b6fd6a8a92b5aa0cfaf8d7be3a96502e537554dc7430" +checksum = "cac6250cad380a005ecb5ffc6d2facf03df0e72628d819a63dd8c3ade7a766ff" dependencies = [ "alloy-eips", "alloy-primitives", @@ -530,9 +539,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "017cad3e5793c5613588c1f9732bcbad77e820ba7d0feaba3527749f856fdbc5" +checksum = "f568c5624881896d8a25e19acbdcbabadd8df339427ea2f10b2ee447d57c4509" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -544,9 +553,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-txpool" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b230e321c416be7f50530159392b4c41a45596d40d97e185575bcd0b545e521" +checksum = "d4a37d2e1ed9b7daf20ad0b3e0092613cbae46737e0e988b23caa556c7067ce6" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -556,9 +565,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dff0ab1cdd43ca001e324dc27ee0e8606bd2161d6623c63e0e0b8c4dfc13600" +checksum = "2843c195675f06b29c09a4315cccdc233ab5bdc7c0a3775909f9f0cab5e9ae0f" dependencies = [ "alloy-primitives", "arbitrary", @@ -568,9 +577,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd4e0ad79c81a27ca659be5d176ca12399141659fef2bcbfdc848da478f4504" +checksum = "88b2a00d9803dfef99963303ffe41a7bf2221f3342f0a503d6741a9f4a18e5e5" dependencies = [ "alloy-primitives", "async-trait", @@ -582,9 +591,9 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "494e0a256f3e99f2426f994bcd1be312c02cb8f88260088dacb33a8b8936475f" +checksum = "5a2505d4f8c98dcae86152d58d549cb4bcf953f8352fca903410e0a0ef535571" dependencies = [ "alloy-consensus", "alloy-network", @@ -609,7 +618,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -625,7 +634,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", "syn-solidity", "tiny-keccak", ] @@ -641,7 +650,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", "syn-solidity", ] @@ -670,9 +679,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ac3e97dad3d31770db0fc89bd6a63b789fbae78963086733f960cf32c483904" +checksum = "9dc2c8f6b8c227ef0398f702d954c4ab572c2ead3c1ed4a5157aa1cbaf959747" dependencies = [ "alloy-json-rpc", "base64 0.22.1", @@ -685,13 +694,14 @@ dependencies = [ "tower 0.5.1", "tracing", "url", + "wasmtimer", ] [[package]] name = "alloy-transport-http" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b367dcccada5b28987c2296717ee04b9a5637aacd78eacb1726ef211678b5212" +checksum = "dd328e990d57f4c4e63899fb2c26877597d6503f8e0022a3d71b2d753ecbfc0c" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -704,9 +714,9 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b90cf9cde7f2fce617da52768ee28f522264b282d148384a4ca0ea85af04fa3a" +checksum = "89aea26aaf1d67904a7ff95ec4a24ddd5e7d419a6945f641b885962d7c2803e2" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -723,9 +733,9 @@ dependencies = [ [[package]] name = "alloy-transport-ws" -version = "0.4.2" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7153b88690de6a50bba81c11e1d706bc41dbb90126d607404d60b763f6a3947f" +checksum = "e222e950ecc4ea12fbfb524b9a2275cac2cd5f57c8ce25bcaf1bd3ff80dd8fc8" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -741,9 +751,9 @@ dependencies = [ [[package]] name = "alloy-trie" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fa8acead43cb238a7b7f47238c71137f4677a0b8d90e7e3be6e6ca59a28194e" +checksum = "cdd7f8b3a7c65ca09b3c7bdd7c7d72d7423d026f5247eda96af53d24e58315c1" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -831,9 +841,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.89" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" +checksum = "37bf3594c4c988a53154954629820791dde498571819ae4ca50ca811e060cc95" [[package]] name = "aquamarine" @@ -846,7 +856,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -1022,9 +1032,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.15" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e26a9844c659a2a293d239c7910b752f8487fe122c6c8bd1659bf85a6507c302" +checksum = "103db485efc3e41214fe4fda9f3dbeae2eb9082f48fd236e6095627a9422066e" dependencies = [ "brotli", "flate2", @@ -1069,7 +1079,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -1080,7 +1090,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -1118,7 +1128,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -1224,7 +1234,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -1406,7 +1416,7 @@ checksum = "240f4126219a83519bad05c9a40bfc0303921eeb571fc2d7e44c17ffac99d3f1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", "synstructure", ] @@ -1488,6 +1498,17 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "bstr" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" +dependencies = [ + "memchr", + "regex-automata 0.4.8", + "serde", +] + [[package]] name = "bumpalo" version = "3.16.0" @@ -1517,7 +1538,7 @@ checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -1605,9 +1626,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.30" +version = "1.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" +checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" dependencies = [ "jobserver", "libc", @@ -1729,7 +1750,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -2017,9 +2038,9 @@ dependencies = [ [[package]] name = "critical-section" -version = "1.1.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f64009896348fc5af4222e9cf7d7d82a95a256c634ebcf61c53e4ea461422242" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" [[package]] name = "crossbeam-channel" @@ -2186,7 +2207,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -2210,7 +2231,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -2221,7 +2242,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -2249,6 +2270,7 @@ dependencies = [ "lock_api", "once_cell", "parking_lot_core 0.9.10", + "serde", ] [[package]] @@ -2341,7 +2363,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -2352,7 +2374,7 @@ checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -2373,7 +2395,7 @@ dependencies = [ "convert_case", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", "unicode-xid", ] @@ -2487,7 +2509,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -2635,7 +2657,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -2646,7 +2668,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -2665,6 +2687,47 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "ethereum_serde_utils" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70cbccfccf81d67bff0ab36e591fa536c8a935b078a7b0e58c1d00d418332fc9" +dependencies = [ + "alloy-primitives", + "hex", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "ethereum_ssz" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfbba28f4f3f32d92c06a64f5bf6c4537b5d4e21f28c689bd2bbaecfea4e0d3e" +dependencies = [ + "alloy-primitives", + "derivative", + "ethereum_serde_utils", + "itertools 0.13.0", + "serde", + "serde_derive", + "smallvec", + "typenum", +] + +[[package]] +name = "ethereum_ssz_derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d37845ba7c16bf4be8be4b5786f03a2ba5f2fda0d7f9e7cb2282f69cff420d7" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.80", +] + [[package]] name = "event-listener" version = "2.5.3" @@ -2831,8 +2894,6 @@ dependencies = [ "reth-network", "reth-network-api", "reth-node-ethereum", - "reth-primitives", - "reth-provider", "tokio", "tokio-stream", "tracing", @@ -2857,6 +2918,7 @@ dependencies = [ name = "example-manual-p2p" version = "0.0.0" dependencies = [ + "alloy-consensus", "eyre", "futures", "reth-chainspec", @@ -2921,7 +2983,6 @@ dependencies = [ "reth-discv4", "reth-network", "reth-primitives", - "reth-provider", "reth-tracing", "secp256k1", "serde_json", @@ -3202,7 +3263,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -3270,6 +3331,7 @@ version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ + "serde", "typenum", "version_check", "zeroize", @@ -3645,9 +3707,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" dependencies = [ "bytes", "futures-channel", @@ -3727,7 +3789,7 @@ dependencies = [ "quote", "serde", "serde_json", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -3877,7 +3939,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -4045,7 +4107,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b23a0c8dfe501baac4adf6ebbfa6eddf8f0c07f56b058cc1288017e32397846c" dependencies = [ "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -4190,9 +4252,9 @@ dependencies = [ [[package]] name = "jsonrpsee" -version = "0.24.6" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02f01f48e04e0d7da72280ab787c9943695699c9b32b99158ece105e8ad0afea" +checksum = "c5c71d8c1a731cc4227c2f698d377e7848ca12c8a48866fc5e6951c43a4db843" dependencies = [ "jsonrpsee-client-transport", "jsonrpsee-core", @@ -4208,9 +4270,9 @@ dependencies = [ [[package]] name = "jsonrpsee-client-transport" -version = "0.24.6" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d80eccbd47a7b9f1e67663fd846928e941cb49c65236e297dd11c9ea3c5e3387" +checksum = "548125b159ba1314104f5bb5f38519e03a41862786aa3925cf349aae9cdd546e" dependencies = [ "base64 0.22.1", "futures-channel", @@ -4233,9 +4295,9 @@ dependencies = [ [[package]] name = "jsonrpsee-core" -version = "0.24.6" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c2709a32915d816a6e8f625bf72cf74523ebe5d8829f895d6b041b1d3137818" +checksum = "f2882f6f8acb9fdaec7cefc4fd607119a9bd709831df7d7672a1d3b644628280" dependencies = [ "async-trait", "bytes", @@ -4260,9 +4322,9 @@ dependencies = [ [[package]] name = "jsonrpsee-http-client" -version = "0.24.6" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc54db939002b030e794fbfc9d5a925aa2854889c5a2f0352b0bffa54681707e" +checksum = "b3638bc4617f96675973253b3a45006933bde93c2fd8a6170b33c777cc389e5b" dependencies = [ "async-trait", "base64 0.22.1", @@ -4285,22 +4347,22 @@ dependencies = [ [[package]] name = "jsonrpsee-proc-macros" -version = "0.24.6" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a9a4b2eaba8cc928f49c4ccf4fcfa65b690a73997682da99ed08f3393b51f07" +checksum = "c06c01ae0007548e73412c08e2285ffe5d723195bf268bce67b1b77c3bb2a14d" dependencies = [ "heck", "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] name = "jsonrpsee-server" -version = "0.24.6" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e30110d0f2d7866c8cc6c86483bdab2eb9f4d2f0e20db55518b2bca84651ba8e" +checksum = "82ad8ddc14be1d4290cd68046e7d1d37acd408efed6d3ca08aefcc3ad6da069c" dependencies = [ "futures-util", "http", @@ -4325,9 +4387,9 @@ dependencies = [ [[package]] name = "jsonrpsee-types" -version = "0.24.6" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ca331cd7b3fe95b33432825c2d4c9f5a43963e207fdc01ae67f9fd80ab0930f" +checksum = "a178c60086f24cc35bb82f57c651d0d25d99c4742b4d335de04e97fa1f08a8a1" dependencies = [ "http", "serde", @@ -4337,9 +4399,9 @@ dependencies = [ [[package]] name = "jsonrpsee-wasm-client" -version = "0.24.6" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c603d97578071dc44d79d3cfaf0775437638fd5adc33c6b622dfe4fa2ec812d" +checksum = "1a01cd500915d24ab28ca17527e23901ef1be6d659a2322451e1045532516c25" dependencies = [ "jsonrpsee-client-transport", "jsonrpsee-core", @@ -4348,9 +4410,9 @@ dependencies = [ [[package]] name = "jsonrpsee-ws-client" -version = "0.24.6" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "755ca3da1c67671f1fae01cd1a47f41dfb2233a8f19a643e587ab0a663942044" +checksum = "0fe322e0896d0955a3ebdd5bf813571c53fea29edd713bc315b76620b327e86d" dependencies = [ "http", "jsonrpsee-client-transport", @@ -4453,9 +4515,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.159" +version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" [[package]] name = "libloading" @@ -4464,7 +4526,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -4575,6 +4637,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47186c6da4d81ca383c7c47c1bfc80f4b95f4720514d860a5407aaf4233f9588" dependencies = [ "linked-hash-map", + "serde", ] [[package]] @@ -4597,6 +4660,7 @@ checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", + "serde", ] [[package]] @@ -4709,7 +4773,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -4856,7 +4920,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -4961,6 +5025,7 @@ dependencies = [ "libc", "log", "mio 0.8.11", + "serde", "walkdir", "windows-sys 0.48.0", ] @@ -5103,7 +5168,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -5156,9 +5221,9 @@ checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" [[package]] name = "op-alloy-consensus" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ea7162170c6f3cad8f67f4dd7108e3f78349fd553da5b8bebff1e7ef8f38896" +checksum = "99d49163f952491820088dd0e66f3a35d63337c3066eceff0a931bf83a8e2101" dependencies = [ "alloy-consensus", "alloy-eips", @@ -5174,9 +5239,9 @@ dependencies = [ [[package]] name = "op-alloy-genesis" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3d31dfbbd8dd898c7512f8ce7d30103980485416f668566100b0ed0994b958" +checksum = "8e46c2ab105f679f0cbfbc3fb762f3456d4b8556c841e667fc8f3c2226eb6c1e" dependencies = [ "alloy-consensus", "alloy-eips", @@ -5188,9 +5253,9 @@ dependencies = [ [[package]] name = "op-alloy-network" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d113b325527ba7da271a8793f1c14bdf7f035ce9e0611e668c36fc6812568c7f" +checksum = "75ff1ea317441b9eb6317b24d13f9088e3b14ef48b15bfb6a125ca404df036d8" dependencies = [ "alloy-consensus", "alloy-network", @@ -5202,9 +5267,9 @@ dependencies = [ [[package]] name = "op-alloy-protocol" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310873e4fbfc41986716c4fb6000a8b49d025d932d2c261af58271c434b05288" +checksum = "6c439457b2a1791325603fc18a94cc175e0b4b1127f11ff8a45071f05d044dcb" dependencies = [ "alloy-consensus", "alloy-eips", @@ -5219,9 +5284,9 @@ dependencies = [ [[package]] name = "op-alloy-rpc-types" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "323c65880e2561aa87f74f8af260fd15b9cc930c448c88a60ae95af86c88c634" +checksum = "9c9556293835232b019ec9c6fd84e4265a3151111af60ea09b5b513e3dbed41c" dependencies = [ "alloy-consensus", "alloy-eips", @@ -5229,6 +5294,7 @@ dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", "alloy-serde", + "arbitrary", "op-alloy-consensus", "serde", "serde_json", @@ -5236,16 +5302,18 @@ dependencies = [ [[package]] name = "op-alloy-rpc-types-engine" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349e7b420f45d1a00216ec4c65fcf3f0057a841bc39732c405c85ae782b94121" +checksum = "8a42a5ac4e07ed226b6a2aeefaad9b2cc7ec160e372ba626a4214d681a355fc2" dependencies = [ "alloy-primitives", "alloy-rpc-types-engine", "alloy-serde", "derive_more 1.0.0", + "ethereum_ssz", "op-alloy-protocol", "serde", + "snap", ] [[package]] @@ -5320,6 +5388,7 @@ version = "3.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" dependencies = [ + "arbitrary", "arrayvec", "bitvec", "byte-slice-cast", @@ -5478,7 +5547,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -5507,7 +5576,7 @@ checksum = "a4502d8515ca9f32f1fb543d987f63d95a14934883db45bdb48060b6b69257f8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -5674,12 +5743,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.22" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" +checksum = "904afd36257cdb6ce0bee88b7981847bd7b955e5e216bb32f466b302923ad446" dependencies = [ "proc-macro2", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -5754,14 +5823,14 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] name = "proc-macro2" -version = "1.0.87" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" +checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" dependencies = [ "unicode-ident", ] @@ -5852,7 +5921,7 @@ checksum = "6ff7ff745a347b87471d859a377a9a404361e7efc2a971d73424a6d183c0fc77" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -6259,7 +6328,6 @@ dependencies = [ "reth-consensus-common", "reth-db", "reth-db-api", - "reth-discv4", "reth-downloaders", "reth-engine-util", "reth-errors", @@ -6309,6 +6377,7 @@ dependencies = [ name = "reth-auto-seal-consensus" version = "1.1.0" dependencies = [ + "alloy-eips", "alloy-primitives", "alloy-rpc-types-engine", "futures-util", @@ -6339,18 +6408,19 @@ dependencies = [ name = "reth-basic-payload-builder" version = "1.1.0" dependencies = [ + "alloy-consensus", "alloy-primitives", "alloy-rlp", "futures-core", "futures-util", "metrics", "reth-chainspec", + "reth-evm", "reth-metrics", "reth-payload-builder", "reth-payload-primitives", "reth-primitives", "reth-provider", - "reth-revm", "reth-tasks", "reth-transaction-pool", "revm", @@ -6362,6 +6432,7 @@ dependencies = [ name = "reth-beacon-consensus" version = "1.1.0" dependencies = [ + "alloy-eips", "alloy-genesis", "alloy-primitives", "alloy-rpc-types-engine", @@ -6528,6 +6599,7 @@ name = "reth-chainspec" version = "1.1.0" dependencies = [ "alloy-chains", + "alloy-consensus", "alloy-eips", "alloy-genesis", "alloy-primitives", @@ -6551,6 +6623,7 @@ dependencies = [ "clap", "eyre", "reth-cli-runner", + "reth-db", "serde_json", "shellexpand", ] @@ -6650,7 +6723,6 @@ dependencies = [ "alloy-eips", "alloy-genesis", "alloy-primitives", - "alloy-rlp", "alloy-trie", "arbitrary", "bytes", @@ -6658,7 +6730,6 @@ dependencies = [ "op-alloy-consensus", "proptest", "proptest-arbitrary-interop", - "rand 0.8.5", "reth-codecs-derive", "serde", "serde_json", @@ -6673,7 +6744,7 @@ dependencies = [ "proc-macro2", "quote", "similar-asserts", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -6696,6 +6767,7 @@ dependencies = [ name = "reth-consensus" version = "1.1.0" dependencies = [ + "alloy-eips", "alloy-primitives", "auto_impl", "derive_more 1.0.0", @@ -6758,7 +6830,6 @@ dependencies = [ "paste", "pprof", "proptest", - "rand 0.8.5", "reth-db-api", "reth-fs-util", "reth-libmdbx", @@ -6812,6 +6883,7 @@ dependencies = [ name = "reth-db-common" version = "1.1.0" dependencies = [ + "alloy-consensus", "alloy-genesis", "alloy-primitives", "boyer-moore-magiclen", @@ -6981,22 +7053,16 @@ dependencies = [ "eyre", "futures-util", "jsonrpsee", - "jsonrpsee-types", "op-alloy-rpc-types-engine", "reth", "reth-chainspec", "reth-db", - "reth-engine-local", "reth-network-peers", "reth-node-builder", - "reth-node-ethereum", "reth-payload-builder", "reth-payload-primitives", - "reth-primitives", "reth-provider", - "reth-rpc", "reth-rpc-layer", - "reth-rpc-types-compat", "reth-stages-types", "reth-tokio-util", "reth-tracing", @@ -7117,7 +7183,6 @@ dependencies = [ "assert_matches", "futures", "metrics", - "rand 0.8.5", "reth-beacon-consensus", "reth-blockchain-tree", "reth-blockchain-tree-api", @@ -7157,6 +7222,8 @@ dependencies = [ name = "reth-engine-util" version = "1.1.0" dependencies = [ + "alloy-consensus", + "alloy-eips", "alloy-primitives", "alloy-rpc-types-engine", "eyre", @@ -7268,6 +7335,8 @@ dependencies = [ name = "reth-ethereum-consensus" version = "1.1.0" dependencies = [ + "alloy-consensus", + "alloy-eips", "alloy-primitives", "reth-chainspec", "reth-consensus", @@ -7300,6 +7369,7 @@ name = "reth-ethereum-forks" version = "1.1.0" dependencies = [ "alloy-chains", + "alloy-consensus", "alloy-primitives", "alloy-rlp", "arbitrary", @@ -7318,6 +7388,8 @@ dependencies = [ name = "reth-ethereum-payload-builder" version = "1.1.0" dependencies = [ + "alloy-consensus", + "alloy-eips", "alloy-primitives", "reth-basic-payload-builder", "reth-chain-state", @@ -7359,12 +7431,16 @@ dependencies = [ "metrics", "parking_lot 0.12.3", "reth-chainspec", + "reth-consensus", + "reth-consensus-common", + "reth-ethereum-forks", "reth-execution-errors", "reth-execution-types", "reth-metrics", "reth-primitives", "reth-primitives-traits", "reth-prune-types", + "reth-revm", "reth-storage-errors", "revm", "revm-primitives", @@ -7380,12 +7456,12 @@ dependencies = [ "alloy-primitives", "alloy-sol-types", "reth-chainspec", + "reth-consensus", "reth-ethereum-consensus", "reth-ethereum-forks", "reth-evm", "reth-execution-types", "reth-primitives", - "reth-prune-types", "reth-revm", "reth-testing-utils", "revm-primitives", @@ -7443,7 +7519,6 @@ dependencies = [ "reth-chain-state", "reth-chainspec", "reth-config", - "reth-db-api", "reth-db-common", "reth-evm", "reth-evm-ethereum", @@ -7483,7 +7558,6 @@ dependencies = [ "reth-consensus", "reth-db", "reth-db-common", - "reth-ethereum-engine-primitives", "reth-evm", "reth-execution-types", "reth-exex", @@ -7787,9 +7861,14 @@ dependencies = [ name = "reth-node-api" version = "1.1.0" dependencies = [ + "alloy-rpc-types-engine", + "eyre", + "reth-beacon-consensus", + "reth-consensus", "reth-engine-primitives", "reth-evm", "reth-network-api", + "reth-node-core", "reth-node-types", "reth-payload-builder", "reth-payload-primitives", @@ -7869,6 +7948,7 @@ dependencies = [ name = "reth-node-core" version = "1.1.0" dependencies = [ + "alloy-consensus", "alloy-primitives", "alloy-rpc-types-engine", "clap", @@ -7907,7 +7987,6 @@ dependencies = [ "serde", "shellexpand", "strum", - "tempfile", "thiserror", "tokio", "toml", @@ -7919,11 +7998,11 @@ dependencies = [ name = "reth-node-ethereum" version = "1.1.0" dependencies = [ + "alloy-consensus", "alloy-genesis", "alloy-primitives", "eyre", "futures", - "futures-util", "reth", "reth-auto-seal-consensus", "reth-basic-payload-builder", @@ -7934,12 +8013,12 @@ dependencies = [ "reth-e2e-test-utils", "reth-ethereum-engine-primitives", "reth-ethereum-payload-builder", + "reth-evm", "reth-evm-ethereum", "reth-exex", "reth-network", "reth-node-api", "reth-node-builder", - "reth-node-core", "reth-payload-builder", "reth-primitives", "reth-provider", @@ -7988,7 +8067,6 @@ dependencies = [ "metrics-util", "procfs 0.16.0", "reqwest", - "reth-chainspec", "reth-db-api", "reth-metrics", "reth-provider", @@ -8008,6 +8086,8 @@ dependencies = [ "reth-chainspec", "reth-db-api", "reth-engine-primitives", + "reth-primitives", + "reth-primitives-traits", ] [[package]] @@ -8015,6 +8095,7 @@ name = "reth-optimism-chainspec" version = "1.1.0" dependencies = [ "alloy-chains", + "alloy-consensus", "alloy-genesis", "alloy-primitives", "derive_more 1.0.0", @@ -8076,6 +8157,7 @@ dependencies = [ name = "reth-optimism-consensus" version = "1.1.0" dependencies = [ + "alloy-consensus", "alloy-primitives", "reth-chainspec", "reth-consensus", @@ -8095,8 +8177,10 @@ dependencies = [ "alloy-eips", "alloy-genesis", "alloy-primitives", + "derive_more 1.0.0", "op-alloy-consensus", "reth-chainspec", + "reth-consensus", "reth-ethereum-forks", "reth-evm", "reth-execution-errors", @@ -8109,7 +8193,6 @@ dependencies = [ "reth-revm", "revm", "revm-primitives", - "thiserror", "tracing", ] @@ -8132,15 +8215,11 @@ dependencies = [ "alloy-genesis", "alloy-primitives", "alloy-rpc-types-engine", - "async-trait", "clap", "eyre", - "jsonrpsee", - "jsonrpsee-types", "op-alloy-consensus", "op-alloy-rpc-types-engine", "parking_lot 0.12.3", - "reqwest", "reth", "reth-auto-seal-consensus", "reth-basic-payload-builder", @@ -8148,7 +8227,6 @@ dependencies = [ "reth-chainspec", "reth-consensus", "reth-db", - "reth-discv5", "reth-e2e-test-utils", "reth-engine-local", "reth-evm", @@ -8165,24 +8243,19 @@ dependencies = [ "reth-primitives", "reth-provider", "reth-revm", - "reth-rpc", - "reth-rpc-eth-api", - "reth-rpc-eth-types", - "reth-rpc-types-compat", "reth-tracing", "reth-transaction-pool", "revm", "serde", "serde_json", - "thiserror", "tokio", - "tracing", ] [[package]] name = "reth-optimism-payload-builder" version = "1.1.0" dependencies = [ + "alloy-consensus", "alloy-eips", "alloy-primitives", "alloy-rlp", @@ -8216,15 +8289,16 @@ dependencies = [ name = "reth-optimism-primitives" version = "1.1.0" dependencies = [ + "alloy-consensus", "alloy-primitives", "reth-primitives", - "reth-primitives-traits", ] [[package]] name = "reth-optimism-rpc" version = "1.1.0" dependencies = [ + "alloy-consensus", "alloy-eips", "alloy-primitives", "alloy-rpc-types", @@ -8317,6 +8391,7 @@ dependencies = [ name = "reth-payload-validator" version = "1.1.0" dependencies = [ + "alloy-eips", "alloy-rpc-types", "reth-chainspec", "reth-primitives", @@ -8498,9 +8573,8 @@ dependencies = [ name = "reth-revm" version = "1.1.0" dependencies = [ + "alloy-eips", "alloy-primitives", - "reth-chainspec", - "reth-consensus-common", "reth-ethereum-forks", "reth-execution-errors", "reth-primitives", @@ -8602,7 +8676,6 @@ dependencies = [ "reth-network-peers", "reth-primitives", "reth-rpc-eth-api", - "serde_json", ] [[package]] @@ -8650,7 +8723,6 @@ dependencies = [ "reth-metrics", "reth-network-api", "reth-network-peers", - "reth-node-api", "reth-node-core", "reth-payload-builder", "reth-primitives", @@ -8664,7 +8736,6 @@ dependencies = [ "reth-rpc-server-types", "reth-rpc-types-compat", "reth-tasks", - "reth-tokio-util", "reth-tracing", "reth-transaction-pool", "serde", @@ -8716,6 +8787,7 @@ dependencies = [ name = "reth-rpc-eth-api" version = "1.1.0" dependencies = [ + "alloy-consensus", "alloy-dyn-abi", "alloy-eips", "alloy-json-rpc", @@ -8830,6 +8902,7 @@ dependencies = [ name = "reth-rpc-types-compat" version = "1.1.0" dependencies = [ + "alloy-consensus", "alloy-eips", "alloy-primitives", "alloy-rlp", @@ -8885,7 +8958,6 @@ dependencies = [ "reth-testing-utils", "reth-trie", "reth-trie-db", - "serde_json", "tempfile", "thiserror", "tokio", @@ -8945,11 +9017,8 @@ dependencies = [ "assert_matches", "parking_lot 0.12.3", "rayon", - "reth-chainspec", "reth-db", "reth-db-api", - "reth-nippy-jar", - "reth-node-types", "reth-provider", "reth-prune-types", "reth-stages", @@ -8977,6 +9046,7 @@ dependencies = [ name = "reth-storage-api" version = "1.1.0" dependencies = [ + "alloy-consensus", "alloy-eips", "alloy-primitives", "auto_impl", @@ -9105,18 +9175,17 @@ dependencies = [ name = "reth-trie" version = "1.1.0" dependencies = [ + "alloy-consensus", "alloy-primitives", "alloy-rlp", "auto_impl", "bincode", "criterion", - "derive_more 1.0.0", "itertools 0.13.0", "metrics", "proptest", "proptest-arbitrary-interop", "rayon", - "reth-chainspec", "reth-execution-errors", "reth-metrics", "reth-primitives", @@ -9127,7 +9196,6 @@ dependencies = [ "serde", "serde_json", "serde_with", - "tokio", "tracing", "triehash", ] @@ -9160,24 +9228,20 @@ dependencies = [ name = "reth-trie-db" version = "1.1.0" dependencies = [ + "alloy-consensus", "alloy-primitives", "alloy-rlp", - "auto_impl", "derive_more 1.0.0", - "itertools 0.13.0", "metrics", "proptest", "proptest-arbitrary-interop", - "rayon", "reth-chainspec", "reth-db", "reth-db-api", "reth-execution-errors", "reth-metrics", - "reth-node-types", "reth-primitives", "reth-provider", - "reth-stages-types", "reth-storage-errors", "reth-trie", "reth-trie-common", @@ -9185,8 +9249,6 @@ dependencies = [ "serde", "serde_json", "similar-asserts", - "tokio", - "tokio-stream", "tracing", "triehash", ] @@ -9206,7 +9268,6 @@ dependencies = [ "rand 0.8.5", "rayon", "reth-db", - "reth-db-api", "reth-execution-errors", "reth-metrics", "reth-primitives", @@ -9221,12 +9282,28 @@ dependencies = [ [[package]] name = "reth-trie-sparse" version = "1.1.0" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "assert_matches", + "criterion", + "itertools 0.13.0", + "pretty_assertions", + "proptest", + "rand 0.8.5", + "reth-testing-utils", + "reth-tracing", + "reth-trie", + "reth-trie-common", + "smallvec", + "thiserror", +] [[package]] name = "revm" -version = "14.0.3" +version = "16.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "641702b12847f9ed418d552f4fcabe536d867a2c980e96b6e7e25d7b992f929f" +checksum = "34e44692d5736cc44c697a372e507890f8797f06d1541c5f4b9bec594d90fd8a" dependencies = [ "auto_impl", "cfg-if", @@ -9239,9 +9316,9 @@ dependencies = [ [[package]] name = "revm-inspectors" -version = "0.8.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43c44af0bf801f48d25f7baf25cf72aff4c02d610f83b428175228162fef0246" +checksum = "a64e2246ad480167548724eb9c9c66945241b867c7d50894de3ca860c9823a45" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -9258,9 +9335,9 @@ dependencies = [ [[package]] name = "revm-interpreter" -version = "10.0.3" +version = "12.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5e14002afae20b5bf1566f22316122f42f57517000e559c55b25bf7a49cba2" +checksum = "6f89940d17d5d077570de1977f52f69049595322e237cb6c754c3d47f668f023" dependencies = [ "revm-primitives", "serde", @@ -9268,9 +9345,9 @@ dependencies = [ [[package]] name = "revm-precompile" -version = "11.0.3" +version = "13.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3198c06247e8d4ad0d1312591edf049b0de4ddffa9fecb625c318fd67db8639b" +checksum = "d8f816aaea3245cbdbe7fdd84955df33597f9322c7912c3e3ba7bc855e03211f" dependencies = [ "aurora-engine-modexp", "blst", @@ -9288,9 +9365,9 @@ dependencies = [ [[package]] name = "revm-primitives" -version = "10.0.0" +version = "12.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f1525851a03aff9a9d6a1d018b414d76252d6802ab54695b27093ecd7e7a101" +checksum = "532411bbde45a46707c1d434dcdc29866cf261c1b748fb01b303ce3b4310b361" dependencies = [ "alloy-eip2930", "alloy-eip7702", @@ -9512,9 +9589,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.14" +version = "0.23.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "415d9944693cb90382053259f89fbb077ea730ad7273047ec63b19bc9b160ba8" +checksum = "5fbb44d7acc4e873d613422379f69f237a1b141928c02f6bc6ccfddddc2d7993" dependencies = [ "log", "once_cell", @@ -9562,9 +9639,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55" +checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" [[package]] name = "rustls-platform-verifier" @@ -9606,9 +9683,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" [[package]] name = "rusty-fork" @@ -9645,9 +9722,9 @@ dependencies = [ [[package]] name = "scc" -version = "2.2.1" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "553f8299af7450cda9a52d3a370199904e7a46b5ffd1bef187c4a6af3bb6db69" +checksum = "f2c1f7fc6deb21665a9060dfc7d271be784669295a31babdcd4dd2c79ae8cbfb" dependencies = [ "sdd", ] @@ -9804,14 +9881,14 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" dependencies = [ "indexmap 2.6.0", "itoa", @@ -9839,7 +9916,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -9890,7 +9967,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -9913,7 +9990,7 @@ checksum = "82fe9db325bcef1fbcde82e078a5cc4efdf787e96b3b9cf45b50b529f2083d67" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -10040,6 +10117,10 @@ name = "similar" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" +dependencies = [ + "bstr", + "unicode-segmentation", +] [[package]] name = "similar-asserts" @@ -10048,6 +10129,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfe85670573cd6f0fa97940f26e7e6601213c3b0555246c24234131f88c5709e" dependencies = [ "console", + "serde", "similar", ] @@ -10204,7 +10286,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -10262,9 +10344,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.79" +version = "2.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +checksum = "e6e185e337f816bc8da115b8afcb3324006ccc82eeaddf35113888d3bd8e44ac" dependencies = [ "proc-macro2", "quote", @@ -10280,7 +10362,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -10306,7 +10388,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -10383,7 +10465,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -10422,7 +10504,7 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -10599,7 +10681,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -10800,7 +10882,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -10958,6 +11040,7 @@ dependencies = [ "parking_lot 0.12.3", "rand 0.8.5", "resolv-conf", + "serde", "smallvec", "thiserror", "tokio", @@ -11023,12 +11106,9 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicase" -version = "2.7.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] +checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" [[package]] name = "unicode-bidi" @@ -11146,9 +11226,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" dependencies = [ "getrandom 0.2.15", ] @@ -11247,7 +11327,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", "wasm-bindgen-shared", ] @@ -11281,7 +11361,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -11305,6 +11385,20 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wasmtimer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7ed9d8b15c7fb594d72bfb4b5a276f3d2029333cd93a932f376f5937f6f80ee" +dependencies = [ + "futures", + "js-sys", + "parking_lot 0.12.3", + "pin-utils", + "slab", + "wasm-bindgen", +] + [[package]] name = "web-sys" version = "0.3.72" @@ -11352,7 +11446,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -11423,7 +11517,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -11434,7 +11528,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -11445,7 +11539,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -11456,7 +11550,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -11731,7 +11825,7 @@ checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", "synstructure", ] @@ -11753,7 +11847,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -11773,7 +11867,7 @@ checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", "synstructure", ] @@ -11794,7 +11888,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] @@ -11816,7 +11910,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.80", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index e3ec1c1fb4a6..5b6912c33f27 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [workspace.package] version = "1.1.0" edition = "2021" -rust-version = "1.81" +rust-version = "1.82" license = "MIT OR Apache-2.0" homepage = "https://paradigmxyz.github.io/reth" repository = "https://github.com/paradigmxyz/reth" @@ -410,11 +410,9 @@ reth-trie-db = { path = "crates/trie/db" } reth-trie-parallel = { path = "crates/trie/parallel" } # revm -revm = { version = "14.0.3", features = [ - "std", -], default-features = false } -revm-inspectors = "0.8.1" -revm-primitives = { version = "10.0.0", features = [ +revm = { version = "16.0.0", features = ["std"], default-features = false } +revm-inspectors = "0.9.0" +revm-primitives = { version = "12.0.0", features = [ "std", ], default-features = false } @@ -426,45 +424,45 @@ alloy-rlp = "0.3.4" alloy-sol-types = "0.8.0" alloy-trie = { version = "0.7", default-features = false } -alloy-consensus = { version = "0.4.2", default-features = false } -alloy-eips = { version = "0.4.2", default-features = false } -alloy-genesis = { version = "0.4.2", default-features = false } -alloy-json-rpc = { version = "0.4.2", default-features = false } -alloy-network = { version = "0.4.2", default-features = false } -alloy-network-primitives = { version = "0.4.2", default-features = false } -alloy-node-bindings = { version = "0.4.2", default-features = false } -alloy-provider = { version = "0.4.2", features = [ +alloy-consensus = { version = "0.5.2", default-features = false } +alloy-eips = { version = "0.5.2", default-features = false } +alloy-genesis = { version = "0.5.2", default-features = false } +alloy-json-rpc = { version = "0.5.2", default-features = false } +alloy-network = { version = "0.5.2", default-features = false } +alloy-network-primitives = { version = "0.5.2", default-features = false } +alloy-node-bindings = { version = "0.5.2", default-features = false } +alloy-provider = { version = "0.5.2", features = [ "reqwest", ], default-features = false } -alloy-pubsub = { version = "0.4.2", default-features = false } -alloy-rpc-client = { version = "0.4.2", default-features = false } -alloy-rpc-types = { version = "0.4.2", features = [ +alloy-pubsub = { version = "0.5.2", default-features = false } +alloy-rpc-client = { version = "0.5.2", default-features = false } +alloy-rpc-types = { version = "0.5.2", features = [ "eth", ], default-features = false } -alloy-rpc-types-admin = { version = "0.4.2", default-features = false } -alloy-rpc-types-anvil = { version = "0.4.2", default-features = false } -alloy-rpc-types-beacon = { version = "0.4.2", default-features = false } -alloy-rpc-types-debug = { version = "0.4.2", default-features = false } -alloy-rpc-types-engine = { version = "0.4.2", default-features = false } -alloy-rpc-types-eth = { version = "0.4.2", default-features = false } -alloy-rpc-types-mev = { version = "0.4.2", default-features = false } -alloy-rpc-types-trace = { version = "0.4.2", default-features = false } -alloy-rpc-types-txpool = { version = "0.4.2", default-features = false } -alloy-serde = { version = "0.4.2", default-features = false } -alloy-signer = { version = "0.4.2", default-features = false } -alloy-signer-local = { version = "0.4.2", default-features = false } -alloy-transport = { version = "0.4.2" } -alloy-transport-http = { version = "0.4.2", features = [ +alloy-rpc-types-admin = { version = "0.5.2", default-features = false } +alloy-rpc-types-anvil = { version = "0.5.2", default-features = false } +alloy-rpc-types-beacon = { version = "0.5.2", default-features = false } +alloy-rpc-types-debug = { version = "0.5.2", default-features = false } +alloy-rpc-types-engine = { version = "0.5.2", default-features = false } +alloy-rpc-types-eth = { version = "0.5.2", default-features = false } +alloy-rpc-types-mev = { version = "0.5.2", default-features = false } +alloy-rpc-types-trace = { version = "0.5.2", default-features = false } +alloy-rpc-types-txpool = { version = "0.5.2", default-features = false } +alloy-serde = { version = "0.5.2", default-features = false } +alloy-signer = { version = "0.5.2", default-features = false } +alloy-signer-local = { version = "0.5.2", default-features = false } +alloy-transport = { version = "0.5.2" } +alloy-transport-http = { version = "0.5.2", features = [ "reqwest-rustls-tls", ], default-features = false } -alloy-transport-ipc = { version = "0.4.2", default-features = false } -alloy-transport-ws = { version = "0.4.2", default-features = false } +alloy-transport-ipc = { version = "0.5.2", default-features = false } +alloy-transport-ws = { version = "0.5.2", default-features = false } # op -op-alloy-rpc-types = "0.4" -op-alloy-rpc-types-engine = "0.4" -op-alloy-network = "0.4" -op-alloy-consensus = "0.4" +op-alloy-rpc-types = "0.5" +op-alloy-rpc-types-engine = "0.5" +op-alloy-network = "0.5" +op-alloy-consensus = "0.5" # misc aquamarine = "0.5" @@ -587,7 +585,7 @@ pprof = "0.13" proptest = "1.4" proptest-derive = "0.5" serial_test = { default-features = false, version = "3" } -similar-asserts = { default-features = false, version = "1.5.0" } +similar-asserts = { version = "1.5.0", features = ["serde"] } tempfile = "3.8" test-fuzz = "6" @@ -595,30 +593,35 @@ tikv-jemalloc-ctl = "0.6" tikv-jemallocator = "0.6" tracy-client = "0.17.3" -[patch.crates-io] -#alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -#alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -#alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -#alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -#alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -#alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -#alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -#alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -#alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -#alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -#alloy-rpc-types-admin = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -#alloy-rpc-types-anvil = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -#alloy-rpc-types-beacon = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -#alloy-rpc-types-debug = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -#alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -#alloy-rpc-types-eth = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -#alloy-rpc-types-mev = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -#alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -#alloy-rpc-types-txpool = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -#alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -#alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -#alloy-signer-local = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -#alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -#alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -#alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} -#alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} +#[patch.crates-io] +#alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "a971b3a" } +#alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "a971b3a" } +#alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "a971b3a" } +#alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy", rev = "a971b3a" } +#alloy-network = { git = "https://github.com/alloy-rs/alloy", rev = "a971b3a" } +#alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "a971b3a" } +#alloy-provider = { git = "https://github.com/alloy-rs/alloy", rev = "a971b3a" } +#alloy-pubsub = { git = "https://github.com/alloy-rs/alloy", rev = "a971b3a" } +#alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy", rev = "a971b3a" } +#alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "a971b3a" } +#alloy-rpc-types-admin = { git = "https://github.com/alloy-rs/alloy", rev = "a971b3a" } +#alloy-rpc-types-anvil = { git = "https://github.com/alloy-rs/alloy", rev = "a971b3a" } +#alloy-rpc-types-beacon = { git = "https://github.com/alloy-rs/alloy", rev = "a971b3a" } +#alloy-rpc-types-debug = { git = "https://github.com/alloy-rs/alloy", rev = "a971b3a" } +#alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/alloy", rev = "a971b3a" } +#alloy-rpc-types-eth = { git = "https://github.com/alloy-rs/alloy", rev = "a971b3a" } +#alloy-rpc-types-mev = { git = "https://github.com/alloy-rs/alloy", rev = "a971b3a" } +#alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "a971b3a" } +#alloy-rpc-types-txpool = { git = "https://github.com/alloy-rs/alloy", rev = "a971b3a" } +#alloy-serde = { git = "https://github.com/alloy-rs/alloy", rev = "a971b3a" } +#alloy-signer = { git = "https://github.com/alloy-rs/alloy", rev = "a971b3a" } +#alloy-signer-local = { git = "https://github.com/alloy-rs/alloy", rev = "a971b3a" } +#alloy-transport = { git = "https://github.com/alloy-rs/alloy", rev = "a971b3a" } +#alloy-transport-http = { git = "https://github.com/alloy-rs/alloy", rev = "a971b3a" } +#alloy-transport-ipc = { git = "https://github.com/alloy-rs/alloy", rev = "a971b3a" } +#alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy", rev = "a971b3a" } + +#op-alloy-rpc-types = { git = "https://github.com/alloy-rs/op-alloy", rev = "6a042e7681b1" } +#op-alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/op-alloy", rev = "6a042e7681b1" } +#op-alloy-network = { git = "https://github.com/alloy-rs/op-alloy", rev = "6a042e7681b1" } +#op-alloy-consensus = { git = "https://github.com/alloy-rs/op-alloy", rev = "6a042e7681b1" } diff --git a/HARDFORK-CHECKLIST.md b/HARDFORK-CHECKLIST.md new file mode 100644 index 000000000000..80ebfc20c98f --- /dev/null +++ b/HARDFORK-CHECKLIST.md @@ -0,0 +1,21 @@ +# Non-exhaustive checklist for integrating new changes for an upcoming hard fork/devnet + +## Introducing new EIP types or changes to primitive types + +- Make required changes to primitive data structures on [alloy](https://github.com/alloy-rs/alloy) +- All new EIP data structures/constants/helpers etc. go into the `alloy-eips` crate at first. +- New transaction types go into `alloy-consensus` +- If there are changes to existing data structures, such as `Header` or `Block`, apply them to the types in `alloy-consensus` (e.g. new `request_hashes` field in Prague) + +## Engine API + +- If there are changes to the engine API (e.g. a new `engine_newPayloadVx` and `engine_getPayloadVx` pair) add the new types to the `alloy-rpc-types-engine` crate. +- If there are new parameters to the `engine_newPayloadVx` endpoint, add them to the `ExecutionPayloadSidecar` container type. This types contains all additional parameters that are required to convert an `ExecutionPayload` to an EL block. + +## Reth changes + +### Updates to the engine API + +- Add new endpoints to the `EngineApi` trait and implement endpoints. +- Update the `ExceuctionPayload` + `ExecutionPayloadSidecar` to `Block` conversion if there are any additional parameters. +- Update version specific validation checks in the `EngineValidator` trait. \ No newline at end of file diff --git a/README.md b/README.md index 7fae4d0b62c0..8a6b8ddb42fb 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ When updating this, also update: - .github/workflows/lint.yml --> -The Minimum Supported Rust Version (MSRV) of this project is [1.81.0](https://blog.rust-lang.org/2024/09/05/Rust-1.81.0.html). +The Minimum Supported Rust Version (MSRV) of this project is [1.82.0](https://blog.rust-lang.org/2024/10/17/Rust-1.82.0.html). See the book for detailed instructions on how to [build from source](https://paradigmxyz.github.io/reth/installation/source.html). diff --git a/bin/reth-bench/Cargo.toml b/bin/reth-bench/Cargo.toml index e4e40daeca91..03844633a926 100644 --- a/bin/reth-bench/Cargo.toml +++ b/bin/reth-bench/Cargo.toml @@ -76,9 +76,16 @@ reth-tracing.workspace = true [features] default = ["jemalloc"] -asm-keccak = ["reth-primitives/asm-keccak"] - -jemalloc = ["reth-cli-util/jemalloc"] +asm-keccak = [ + "reth-primitives/asm-keccak", + "reth-node-core/asm-keccak", + "alloy-primitives/asm-keccak" +] + +jemalloc = [ + "reth-cli-util/jemalloc", + "reth-node-core/jemalloc" +] jemalloc-prof = ["reth-cli-util/jemalloc-prof"] tracy-allocator = ["reth-cli-util/tracy-allocator"] diff --git a/bin/reth-bench/src/authenticated_transport.rs b/bin/reth-bench/src/authenticated_transport.rs index c946d244de9e..72c4fd298898 100644 --- a/bin/reth-bench/src/authenticated_transport.rs +++ b/bin/reth-bench/src/authenticated_transport.rs @@ -84,7 +84,8 @@ impl InnerTransport { let (auth, claims) = build_auth(jwt).map_err(|e| AuthenticatedTransportError::InvalidJwt(e.to_string()))?; - let inner = WsConnect { url: url.to_string(), auth: Some(auth) } + let inner = WsConnect::new(url.clone()) + .with_auth(auth) .into_service() .await .map(Self::Ws) diff --git a/bin/reth-bench/src/bench/new_payload_fcu.rs b/bin/reth-bench/src/bench/new_payload_fcu.rs index a8c18b48a2b7..ca5359fb8c26 100644 --- a/bin/reth-bench/src/bench/new_payload_fcu.rs +++ b/bin/reth-bench/src/bench/new_payload_fcu.rs @@ -37,9 +37,8 @@ pub struct Command { impl Command { /// Execute `benchmark new-payload-fcu` command pub async fn execute(self, _ctx: CliContext) -> eyre::Result<()> { - let cloned_args = self.benchmark.clone(); let BenchContext { benchmark_mode, block_provider, auth_provider, mut next_block } = - BenchContext::new(&cloned_args, self.rpc_url).await?; + BenchContext::new(&self.benchmark, self.rpc_url).await?; let (sender, mut receiver) = tokio::sync::mpsc::channel(1000); tokio::task::spawn(async move { diff --git a/bin/reth-bench/src/bench/new_payload_only.rs b/bin/reth-bench/src/bench/new_payload_only.rs index e6392318a542..85342d1af76c 100644 --- a/bin/reth-bench/src/bench/new_payload_only.rs +++ b/bin/reth-bench/src/bench/new_payload_only.rs @@ -35,11 +35,10 @@ pub struct Command { impl Command { /// Execute `benchmark new-payload-only` command pub async fn execute(self, _ctx: CliContext) -> eyre::Result<()> { - let cloned_args = self.benchmark.clone(); // TODO: this could be just a function I guess, but destructuring makes the code slightly // more readable than a 4 element tuple. let BenchContext { benchmark_mode, block_provider, auth_provider, mut next_block } = - BenchContext::new(&cloned_args, self.rpc_url).await?; + BenchContext::new(&self.benchmark, self.rpc_url).await?; let (sender, mut receiver) = tokio::sync::mpsc::channel(1000); tokio::task::spawn(async move { diff --git a/bin/reth-bench/src/valid_payload.rs b/bin/reth-bench/src/valid_payload.rs index 6353aea71233..b00f4ddcd64b 100644 --- a/bin/reth-bench/src/valid_payload.rs +++ b/bin/reth-bench/src/valid_payload.rs @@ -215,14 +215,6 @@ pub(crate) async fn call_new_payload>( versioned_hashes: Vec, ) -> TransportResult { match payload { - ExecutionPayload::V4(_payload) => { - todo!("V4 payloads not supported yet"); - // auth_provider - // .new_payload_v4_wait(payload, versioned_hashes, parent_beacon_block_root, ...) - // .await?; - // - // Ok(EngineApiMessageVersion::V4) - } ExecutionPayload::V3(payload) => { // We expect the caller let parent_beacon_block_root = parent_beacon_block_root diff --git a/bin/reth/Cargo.toml b/bin/reth/Cargo.toml index 476f9cd5cec7..8380915d463e 100644 --- a/bin/reth/Cargo.toml +++ b/bin/reth/Cargo.toml @@ -96,7 +96,6 @@ backon.workspace = true similar-asserts.workspace = true [dev-dependencies] -reth-discv4.workspace = true tempfile.workspace = true [features] @@ -104,14 +103,21 @@ default = ["jemalloc"] dev = ["reth-cli-commands/dev"] -asm-keccak = ["reth-node-core/asm-keccak", "reth-primitives/asm-keccak"] +asm-keccak = [ + "reth-node-core/asm-keccak", + "reth-primitives/asm-keccak", + "alloy-primitives/asm-keccak" +] jemalloc = [ "reth-cli-util/jemalloc", "reth-node-core/jemalloc", "reth-node-metrics/jemalloc", ] -jemalloc-prof = ["reth-cli-util/jemalloc"] +jemalloc-prof = [ + "reth-cli-util/jemalloc", + "reth-cli-util/jemalloc-prof" +] tracy-allocator = ["reth-cli-util/tracy-allocator"] min-error-logs = ["tracing/release_max_level_error"] diff --git a/bin/reth/src/cli/mod.rs b/bin/reth/src/cli/mod.rs index cca801da36b2..01f8f73e7b1b 100644 --- a/bin/reth/src/cli/mod.rs +++ b/bin/reth/src/cli/mod.rs @@ -18,6 +18,7 @@ use reth_db::DatabaseEnv; use reth_ethereum_cli::chainspec::EthereumChainSpecParser; use reth_node_builder::{NodeBuilder, WithLaunchContext}; use reth_node_ethereum::{EthExecutorProvider, EthereumNode}; +use reth_node_metrics::recorder::install_prometheus_recorder; use reth_tracing::FileWorkerGuard; use std::{ffi::OsString, fmt, future::Future, sync::Arc}; use tracing::info; @@ -145,6 +146,10 @@ impl, Ext: clap::Args + fmt::Debug> Cl let _guard = self.init_tracing()?; info!(target: "reth::cli", "Initialized tracing, debug log directory: {}", self.logs.log_file_directory); + // Install the prometheus recorder to be sure to record task + // executor's metrics + let _ = install_prometheus_recorder(); + let runner = CliRunner::default(); match self.command { Commands::Node(command) => { diff --git a/bin/reth/src/commands/debug_cmd/replay_engine.rs b/bin/reth/src/commands/debug_cmd/replay_engine.rs index cbffa1f0e076..de497cbe0070 100644 --- a/bin/reth/src/commands/debug_cmd/replay_engine.rs +++ b/bin/reth/src/commands/debug_cmd/replay_engine.rs @@ -171,7 +171,9 @@ impl> Command { debug!(target: "reth::cli", ?response, "Received for forkchoice updated"); } StoredEngineApiMessage::NewPayload { payload, cancun_fields } => { - let response = beacon_engine_handle.new_payload(payload, cancun_fields).await?; + // todo: prague (last arg) + let response = + beacon_engine_handle.new_payload(payload, cancun_fields, None).await?; debug!(target: "reth::cli", ?response, "Received for new payload"); } }; diff --git a/book/cli/help.rs b/book/cli/help.rs index e347e1ea5db6..963f53deb0a9 100755 --- a/book/cli/help.rs +++ b/book/cli/help.rs @@ -320,7 +320,7 @@ fn preprocess_help(s: &str) -> Cow<'_, str> { (r"default: reth/.*/\w+", "default: reth//"), // Remove rpc.max-tracing-requests default value ( - r"(rpc.max-tracing-requests \n.*\n.*\n.*)\[default: \d+\]", + r"(rpc.max-tracing-requests \n.*\n.*\n.*\n.*\n.*)\[default: \d+\]", r"$1[default: ]", ), ]; diff --git a/book/cli/reth/node.md b/book/cli/reth/node.md index 73a8063a852b..34d32209ada9 100644 --- a/book/cli/reth/node.md +++ b/book/cli/reth/node.md @@ -331,7 +331,9 @@ RPC: [default: 500] --rpc.max-tracing-requests - Maximum number of concurrent tracing requests + Maximum number of concurrent tracing requests. + + By default this chooses a sensible value based on the number of available cores. Tracing requests are generally CPU bound. Choosing a value that is higher than the available CPU cores can have a negative impact on the performance of the node and affect the node's ability to maintain sync. [default: ] @@ -614,8 +616,6 @@ Pruning: --block-interval Minimum pruning interval measured in blocks - [default: 0] - --prune.senderrecovery.full Prunes all sender recovery data diff --git a/book/sources/exex/remote/Cargo.toml b/book/sources/exex/remote/Cargo.toml index 6eeb848cacfe..6cca3a841f07 100644 --- a/book/sources/exex/remote/Cargo.toml +++ b/book/sources/exex/remote/Cargo.toml @@ -6,9 +6,11 @@ edition = "2021" [dependencies] # reth reth = { git = "https://github.com/paradigmxyz/reth.git" } -reth-exex = { git = "https://github.com/paradigmxyz/reth.git", features = ["serde"] } -reth-node-ethereum = { git = "https://github.com/paradigmxyz/reth.git"} -reth-node-api = { git = "https://github.com/paradigmxyz/reth.git"} +reth-exex = { git = "https://github.com/paradigmxyz/reth.git", features = [ + "serde", +] } +reth-node-ethereum = { git = "https://github.com/paradigmxyz/reth.git" } +reth-node-api = { git = "https://github.com/paradigmxyz/reth.git" } reth-tracing = { git = "https://github.com/paradigmxyz/reth.git" } # async @@ -49,4 +51,4 @@ path = "src/exex.rs" [[bin]] name = "consumer" -path = "src/consumer.rs" \ No newline at end of file +path = "src/consumer.rs" diff --git a/clippy.toml b/clippy.toml index cdfa4bc93a21..862c568634e5 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1,3 +1,17 @@ -msrv = "1.81" +msrv = "1.82" too-large-for-stack = 128 -doc-valid-idents = ["P2P", "ExEx", "ExExes", "IPv4", "IPv6", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "WAL", "MessagePack"] +doc-valid-idents = [ + "P2P", + "ExEx", + "ExExes", + "IPv4", + "IPv6", + "KiB", + "MiB", + "GiB", + "TiB", + "PiB", + "EiB", + "WAL", + "MessagePack", +] diff --git a/crates/blockchain-tree/Cargo.toml b/crates/blockchain-tree/Cargo.toml index cff117c92b05..aa8fab16fa54 100644 --- a/crates/blockchain-tree/Cargo.toml +++ b/crates/blockchain-tree/Cargo.toml @@ -63,5 +63,24 @@ alloy-genesis.workspace = true alloy-consensus.workspace = true [features] -test-utils = [] -optimism = ["reth-primitives/optimism", "reth-provider/optimism"] +test-utils = [ + "reth-chainspec/test-utils", + "reth-consensus/test-utils", + "reth-evm/test-utils", + "reth-network/test-utils", + "reth-primitives/test-utils", + "reth-revm/test-utils", + "reth-stages-api/test-utils", + "reth-db/test-utils", + "reth-db-api/test-utils", + "reth-provider/test-utils", + "reth-trie-db/test-utils", + "reth-trie/test-utils" +] +optimism = [ + "reth-primitives/optimism", + "reth-provider/optimism", + "reth-execution-types/optimism", + "reth-db/optimism", + "reth-db-api/optimism" +] diff --git a/crates/blockchain-tree/src/blockchain_tree.rs b/crates/blockchain-tree/src/blockchain_tree.rs index 4bed718aa0a6..95c0361f31f5 100644 --- a/crates/blockchain-tree/src/blockchain_tree.rs +++ b/crates/blockchain-tree/src/blockchain_tree.rs @@ -113,9 +113,6 @@ where /// is crucial for the correct execution of transactions. /// - `tree_config`: Configuration for the blockchain tree, including any parameters that affect /// its structure or performance. - /// - `prune_modes`: Configuration for pruning old blockchain data. This helps in managing the - /// storage space efficiently. It's important to validate this configuration to ensure it does - /// not lead to unintended data loss. pub fn new( externals: TreeExternals, config: BlockchainTreeConfig, @@ -902,6 +899,7 @@ where // check unconnected block buffer for children of the chains let mut all_chain_blocks = Vec::new(); for chain in self.state.chains.values() { + all_chain_blocks.reserve_exact(chain.blocks().len()); for (&number, block) in chain.blocks() { all_chain_blocks.push(BlockNumHash { number, hash: block.hash() }) } @@ -1376,7 +1374,7 @@ where #[cfg(test)] mod tests { use super::*; - use alloy_consensus::TxEip1559; + use alloy_consensus::{TxEip1559, EMPTY_ROOT_HASH}; use alloy_genesis::{Genesis, GenesisAccount}; use alloy_primitives::{keccak256, Address, Sealable, B256}; use assert_matches::assert_matches; @@ -1388,7 +1386,7 @@ mod tests { use reth_evm::test_utils::MockExecutorProvider; use reth_evm_ethereum::execute::EthExecutorProvider; use reth_primitives::{ - constants::{EIP1559_INITIAL_BASE_FEE, EMPTY_ROOT_HASH}, + constants::EIP1559_INITIAL_BASE_FEE, proofs::{calculate_receipt_root, calculate_transaction_root}, revm_primitives::AccountInfo, Account, BlockBody, Header, Signature, Transaction, TransactionSigned, @@ -1634,7 +1632,6 @@ mod tests { transactions: body.clone().into_iter().map(|tx| tx.into_signed()).collect(), ommers: Vec::new(), withdrawals: Some(Withdrawals::default()), - requests: None, }, }, body.iter().map(|tx| tx.signer()).collect(), diff --git a/crates/blockchain-tree/src/externals.rs b/crates/blockchain-tree/src/externals.rs index 719852c12ac0..4e22fcb78b6b 100644 --- a/crates/blockchain-tree/src/externals.rs +++ b/crates/blockchain-tree/src/externals.rs @@ -21,7 +21,6 @@ use std::{collections::BTreeMap, sync::Arc}; /// - A handle to the database /// - A handle to the consensus engine /// - The executor factory to execute blocks with -/// - The chain spec #[derive(Debug)] pub struct TreeExternals { /// The provider factory, used to commit the canonical chain, or unwind it. diff --git a/crates/chain-state/Cargo.toml b/crates/chain-state/Cargo.toml index c9691bec411c..9a88a3c54bc6 100644 --- a/crates/chain-state/Cargo.toml +++ b/crates/chain-state/Cargo.toml @@ -56,9 +56,13 @@ revm.workspace = true [features] test-utils = [ - "alloy-signer", - "alloy-signer-local", - "alloy-consensus", - "rand", - "revm" + "alloy-signer", + "alloy-signer-local", + "alloy-consensus", + "rand", + "revm", + "reth-chainspec/test-utils", + "reth-primitives/test-utils", + "reth-trie/test-utils", + "revm?/test-utils" ] diff --git a/crates/chain-state/src/in_memory.rs b/crates/chain-state/src/in_memory.rs index fb67608ebda7..be33e1fd79a8 100644 --- a/crates/chain-state/src/in_memory.rs +++ b/crates/chain-state/src/in_memory.rs @@ -504,20 +504,6 @@ impl CanonicalInMemoryState { self.inner.canon_state_notification_sender.send(event).ok(); } - /// Return state provider with reference to in-memory blocks that overlay database state. - /// - /// This merges the state of all blocks that are part of the chain that the requested block is - /// the head of. This includes all blocks that connect back to the canonical block on disk. - pub fn state_provider_from_state( - &self, - state: &BlockState, - historical: StateProviderBox, - ) -> MemoryOverlayStateProvider { - let in_memory = state.chain().into_iter().map(|block_state| block_state.block()).collect(); - - MemoryOverlayStateProvider::new(historical, in_memory) - } - /// Return state provider with reference to in-memory blocks that overlay database state. /// /// This merges the state of all blocks that are part of the chain that the requested block is @@ -723,6 +709,16 @@ impl BlockState { pub fn iter(self: Arc) -> impl Iterator> { std::iter::successors(Some(self), |state| state.parent.clone()) } + + /// Return state provider with reference to in-memory blocks that overlay database state. + /// + /// This merges the state of all blocks that are part of the chain that the this block is + /// the head of. This includes all blocks that connect back to the canonical block on disk. + pub fn state_provider(&self, historical: StateProviderBox) -> MemoryOverlayStateProvider { + let in_memory = self.chain().into_iter().map(|block_state| block_state.block()).collect(); + + MemoryOverlayStateProvider::new(historical, in_memory) + } } /// Represents an executed block stored in-memory. @@ -869,10 +865,11 @@ impl NewCanonicalChain { mod tests { use super::*; use crate::test_utils::TestBlockBuilder; + use alloy_eips::eip7685::Requests; use alloy_primitives::{map::HashSet, BlockNumber, Bytes, StorageKey, StorageValue}; use rand::Rng; use reth_errors::ProviderResult; - use reth_primitives::{Account, Bytecode, Receipt, Requests}; + use reth_primitives::{Account, Bytecode, Receipt}; use reth_storage_api::{ AccountReader, BlockHashReader, StateProofProvider, StateProvider, StateRootProvider, StorageRootProvider, diff --git a/crates/chain-state/src/test_utils.rs b/crates/chain-state/src/test_utils.rs index 4b0bfcdd996a..f1648ab6bff4 100644 --- a/crates/chain-state/src/test_utils.rs +++ b/crates/chain-state/src/test_utils.rs @@ -2,7 +2,8 @@ use crate::{ in_memory::ExecutedBlock, CanonStateNotification, CanonStateNotifications, CanonStateSubscriptions, }; -use alloy_consensus::TxEip1559; +use alloy_consensus::{Transaction as _, TxEip1559, EMPTY_ROOT_HASH}; +use alloy_eips::eip7685::Requests; use alloy_primitives::{Address, BlockNumber, Sealable, B256, U256}; use alloy_signer::SignerSync; use alloy_signer_local::PrivateKeySigner; @@ -10,10 +11,10 @@ use rand::{thread_rng, Rng}; use reth_chainspec::{ChainSpec, EthereumHardfork, MIN_TRANSACTION_GAS}; use reth_execution_types::{Chain, ExecutionOutcome}; use reth_primitives::{ - constants::{EIP1559_INITIAL_BASE_FEE, EMPTY_ROOT_HASH}, + constants::EIP1559_INITIAL_BASE_FEE, proofs::{calculate_receipt_root, calculate_transaction_root, calculate_withdrawals_root}, - BlockBody, Header, Receipt, Receipts, Requests, SealedBlock, SealedBlockWithSenders, - SealedHeader, Transaction, TransactionSigned, TransactionSignedEcRecovered, + BlockBody, Header, Receipt, Receipts, SealedBlock, SealedBlockWithSenders, SealedHeader, + Transaction, TransactionSigned, TransactionSignedEcRecovered, }; use reth_trie::{root::state_root_unhashed, updates::TrieUpdates, HashedPostState}; use revm::{db::BundleState, primitives::AccountInfo}; @@ -169,7 +170,6 @@ impl TestBlockBuilder { transactions: transactions.into_iter().map(|tx| tx.into_signed()).collect(), ommers: Vec::new(), withdrawals: Some(vec![].into()), - requests: None, }, }; diff --git a/crates/chainspec/Cargo.toml b/crates/chainspec/Cargo.toml index b44a606b65b7..5bac582cd8b6 100644 --- a/crates/chainspec/Cargo.toml +++ b/crates/chainspec/Cargo.toml @@ -22,7 +22,7 @@ alloy-chains = { workspace = true, features = ["serde", "rlp"] } alloy-eips = { workspace = true, features = ["serde"] } alloy-genesis.workspace = true alloy-primitives = { workspace = true, features = ["rand", "rlp"] } -alloy-trie.workspace = true +alloy-consensus.workspace = true # misc auto_impl.workspace = true @@ -40,11 +40,26 @@ alloy-genesis.workspace = true [features] default = ["std"] std = [ - "alloy-chains/std", - "alloy-eips/std", - "alloy-genesis/std", - "alloy-primitives/std", - "alloy-trie/std", + "alloy-chains/std", + "alloy-eips/std", + "alloy-genesis/std", + "alloy-primitives/std", + "alloy-trie/std", + "reth-primitives-traits/std", + "alloy-consensus/std", + "once_cell/std" +] +arbitrary = [ + "alloy-chains/arbitrary", + "reth-ethereum-forks/arbitrary", + "reth-primitives-traits/arbitrary", + "reth-trie-common/arbitrary", + "alloy-consensus/arbitrary", + "alloy-eips/arbitrary", + "alloy-primitives/arbitrary", + "alloy-trie/arbitrary" +] +test-utils = [ + "reth-primitives-traits/test-utils", + "reth-trie-common/test-utils" ] -arbitrary = ["alloy-chains/arbitrary"] -test-utils = [] diff --git a/crates/chainspec/src/api.rs b/crates/chainspec/src/api.rs index 39a6716eedde..f7061ff18fe7 100644 --- a/crates/chainspec/src/api.rs +++ b/crates/chainspec/src/api.rs @@ -14,9 +14,14 @@ pub trait EthChainSpec: Send + Sync + Unpin + Debug { // todo: make chain spec type generic over hardfork //type Hardfork: Clone + Copy + 'static; - /// Chain id. + /// Returns the [`Chain`] object this spec targets. fn chain(&self) -> Chain; + /// Returns the chain id number + fn chain_id(&self) -> u64 { + self.chain().id() + } + /// Get the [`BaseFeeParams`] for the chain at the given block. fn base_fee_params_at_block(&self, block_number: u64) -> BaseFeeParams; diff --git a/crates/chainspec/src/spec.rs b/crates/chainspec/src/spec.rs index e6e8a67d75d3..a7f45727dd8e 100644 --- a/crates/chainspec/src/spec.rs +++ b/crates/chainspec/src/spec.rs @@ -2,11 +2,13 @@ pub use alloy_eips::eip1559::BaseFeeParams; use alloc::{boxed::Box, sync::Arc, vec::Vec}; use alloy_chains::{Chain, NamedChain}; +use alloy_consensus::constants::EMPTY_WITHDRAWALS; +use alloy_eips::eip7685::EMPTY_REQUESTS_HASH; use alloy_genesis::Genesis; use alloy_primitives::{address, b256, Address, BlockNumber, B256, U256}; -use alloy_trie::EMPTY_ROOT_HASH; use derive_more::From; +use alloy_consensus::constants::{DEV_GENESIS_HASH, MAINNET_GENESIS_HASH}; use reth_ethereum_forks::{ ChainHardforks, DisplayHardforks, EthereumHardfork, EthereumHardforks, ForkCondition, ForkFilter, ForkFilterKey, ForkHash, ForkId, Hardfork, Hardforks, Head, DEV_HARDFORKS, @@ -17,8 +19,8 @@ use reth_network_peers::{ }; use reth_primitives_traits::{ constants::{ - DEV_GENESIS_HASH, EIP1559_INITIAL_BASE_FEE, EMPTY_WITHDRAWALS, ETHEREUM_BLOCK_GAS_LIMIT, - HOLESKY_GENESIS_HASH, MAINNET_GENESIS_HASH, SEPOLIA_GENESIS_HASH, + EIP1559_INITIAL_BASE_FEE, ETHEREUM_BLOCK_GAS_LIMIT, HOLESKY_GENESIS_HASH, + SEPOLIA_GENESIS_HASH, }, Header, SealedHeader, }; @@ -282,8 +284,9 @@ impl ChainSpec { }; // If Prague is activated at genesis we set requests root to an empty trie root. - let requests_root = - self.is_prague_active_at_timestamp(self.genesis.timestamp).then_some(EMPTY_ROOT_HASH); + let requests_hash = self + .is_prague_active_at_timestamp(self.genesis.timestamp) + .then_some(EMPTY_REQUESTS_HASH); Header { gas_limit: self.genesis.gas_limit, @@ -299,7 +302,7 @@ impl ChainSpec { parent_beacon_block_root, blob_gas_used: blob_gas_used.map(Into::into), excess_blob_gas: excess_blob_gas.map(Into::into), - requests_root, + requests_hash, ..Default::default() } } @@ -938,6 +941,7 @@ mod tests { use alloy_chains::Chain; use alloy_genesis::{ChainConfig, GenesisAccount}; use alloy_primitives::{b256, hex}; + use alloy_trie::EMPTY_ROOT_HASH; use reth_ethereum_forks::{ForkCondition, ForkHash, ForkId, Head}; use reth_trie_common::TrieAccount; @@ -1814,9 +1818,7 @@ Post-merge hard forks (timestamp based): hex!("078dc6061b1d8eaa8493384b59c9c65ceb917201221d08b80c4de6770b6ec7e7").into(); assert_eq!(chainspec.genesis_header().state_root, expected_state_root); - let expected_withdrawals_hash: B256 = - hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").into(); - assert_eq!(chainspec.genesis_header().withdrawals_root, Some(expected_withdrawals_hash)); + assert_eq!(chainspec.genesis_header().withdrawals_root, Some(EMPTY_ROOT_HASH)); let expected_hash: B256 = hex!("1fc027d65f820d3eef441ebeec139ebe09e471cf98516dce7b5643ccb27f418c").into(); diff --git a/crates/cli/cli/Cargo.toml b/crates/cli/cli/Cargo.toml index 7eb1f43b1e58..5da51a1b2f80 100644 --- a/crates/cli/cli/Cargo.toml +++ b/crates/cli/cli/Cargo.toml @@ -14,7 +14,7 @@ workspace = true [dependencies] # reth reth-cli-runner.workspace = true - +reth-db.workspace = true alloy-genesis.workspace = true # misc diff --git a/crates/cli/cli/src/lib.rs b/crates/cli/cli/src/lib.rs index 1db5ebf86b13..e2c55057a48b 100644 --- a/crates/cli/cli/src/lib.rs +++ b/crates/cli/cli/src/lib.rs @@ -10,10 +10,12 @@ use clap::{Error, Parser}; use reth_cli_runner::CliRunner; +use reth_db::ClientVersion; use std::{borrow::Cow, ffi::OsString}; /// The chainspec module defines the different chainspecs that can be used by the node. pub mod chainspec; +use crate::chainspec::ChainSpecParser; /// Reth based node cli. /// @@ -22,6 +24,9 @@ pub mod chainspec; /// It provides commonly used functionality for running commands and information about the CL, such /// as the name and version. pub trait RethCli: Sized { + /// The associated `ChainSpecParser` type + type ChainSpecParser: ChainSpecParser; + /// The name of the implementation, eg. `reth`, `op-reth`, etc. fn name(&self) -> Cow<'static, str>; @@ -66,4 +71,7 @@ pub trait RethCli: Sized { Ok(cli.with_runner(f)) } + + /// The client version of the node. + fn client_version() -> ClientVersion; } diff --git a/crates/cli/commands/src/db/get.rs b/crates/cli/commands/src/db/get.rs index 5b794feeada2..4006d1660aa3 100644 --- a/crates/cli/commands/src/db/get.rs +++ b/crates/cli/commands/src/db/get.rs @@ -132,9 +132,8 @@ pub(crate) fn table_key(key: &str) -> Result { } /// Get an instance of subkey for given dupsort table -fn table_subkey(subkey: &Option) -> Result { - serde_json::from_str::(&subkey.clone().unwrap_or_default()) - .map_err(|e| eyre::eyre!(e)) +fn table_subkey(subkey: Option<&str>) -> Result { + serde_json::from_str::(subkey.unwrap_or_default()).map_err(|e| eyre::eyre!(e)) } struct GetValueViewer<'a, N: NodeTypesWithDB> { @@ -175,7 +174,7 @@ impl TableViewer<()> for GetValueViewer<'_, N> { let key = table_key::(&self.key)?; // process dupsort table - let subkey = table_subkey::(&self.subkey)?; + let subkey = table_subkey::(self.subkey.as_deref())?; match self.tool.get_dup::(key, subkey)? { Some(content) => { diff --git a/crates/cli/commands/src/node.rs b/crates/cli/commands/src/node.rs index 5b1a87e068b3..b099a2c05222 100644 --- a/crates/cli/commands/src/node.rs +++ b/crates/cli/commands/src/node.rs @@ -16,7 +16,6 @@ use reth_node_core::{ node_config::NodeConfig, version, }; -use reth_node_metrics::recorder::install_prometheus_recorder; use std::{ffi::OsString, fmt, future::Future, net::SocketAddr, path::PathBuf, sync::Arc}; /// Start the node @@ -180,10 +179,6 @@ impl< pruning, }; - // Register the prometheus recorder before creating the database, - // because database init needs it to register metrics. - let _ = install_prometheus_recorder(); - let data_dir = node_config.datadir(); let db_path = data_dir.db(); diff --git a/crates/cli/commands/src/stage/drop.rs b/crates/cli/commands/src/stage/drop.rs index 9e0396404b37..3a277cabd185 100644 --- a/crates/cli/commands/src/stage/drop.rs +++ b/crates/cli/commands/src/stage/drop.rs @@ -81,7 +81,6 @@ impl> Command tx.clear::()?; tx.clear::()?; tx.clear::()?; - tx.clear::()?; reset_stage_checkpoint(tx, StageId::Bodies)?; insert_genesis_header(&provider_rw.0, &static_file_provider, &self.env.chain)?; diff --git a/crates/config/src/config.rs b/crates/config/src/config.rs index e4a7fc9677aa..4c4b6066a3e9 100644 --- a/crates/config/src/config.rs +++ b/crates/config/src/config.rs @@ -14,6 +14,9 @@ use std::{ const EXTENSION: &str = "toml"; +/// The default prune block interval +pub const DEFAULT_BLOCK_INTERVAL: usize = 5; + /// Configuration for the reth node. #[derive(Debug, Clone, Default, Deserialize, PartialEq, Eq, Serialize)] #[serde(default)] @@ -383,7 +386,7 @@ pub struct PruneConfig { impl Default for PruneConfig { fn default() -> Self { - Self { block_interval: 5, segments: PruneModes::none() } + Self { block_interval: DEFAULT_BLOCK_INTERVAL, segments: PruneModes::none() } } } @@ -397,27 +400,33 @@ impl PruneConfig { /// if the corresponding value in this config is not set. pub fn merge(&mut self, other: Option) { let Some(other) = other else { return }; - - // Merge block_interval - if self.block_interval == 0 { - self.block_interval = other.block_interval; + let Self { + block_interval, + segments: + PruneModes { + sender_recovery, + transaction_lookup, + receipts, + account_history, + storage_history, + receipts_log_filter, + }, + } = other; + + // Merge block_interval, only update if it's the default interval + if self.block_interval == DEFAULT_BLOCK_INTERVAL { + self.block_interval = block_interval; } // Merge the various segment prune modes - self.segments.sender_recovery = - self.segments.sender_recovery.or(other.segments.sender_recovery); - self.segments.transaction_lookup = - self.segments.transaction_lookup.or(other.segments.transaction_lookup); - self.segments.receipts = self.segments.receipts.or(other.segments.receipts); - self.segments.account_history = - self.segments.account_history.or(other.segments.account_history); - self.segments.storage_history = - self.segments.storage_history.or(other.segments.storage_history); - - if self.segments.receipts_log_filter.0.is_empty() && - !other.segments.receipts_log_filter.0.is_empty() - { - self.segments.receipts_log_filter = other.segments.receipts_log_filter; + self.segments.sender_recovery = self.segments.sender_recovery.or(sender_recovery); + self.segments.transaction_lookup = self.segments.transaction_lookup.or(transaction_lookup); + self.segments.receipts = self.segments.receipts.or(receipts); + self.segments.account_history = self.segments.account_history.or(account_history); + self.segments.storage_history = self.segments.storage_history.or(storage_history); + + if self.segments.receipts_log_filter.0.is_empty() && !receipts_log_filter.0.is_empty() { + self.segments.receipts_log_filter = receipts_log_filter; } } } @@ -961,7 +970,7 @@ receipts = 'full' // Check that the configuration has been merged. Any configuration present in config1 // should not be overwritten by config2 - assert_eq!(config1.block_interval, 5); + assert_eq!(config1.block_interval, 10); assert_eq!(config1.segments.sender_recovery, Some(PruneMode::Full)); assert_eq!(config1.segments.transaction_lookup, Some(PruneMode::Full)); assert_eq!(config1.segments.receipts, Some(PruneMode::Distance(1000))); diff --git a/crates/consensus/auto-seal/Cargo.toml b/crates/consensus/auto-seal/Cargo.toml index b4b281230336..f2bfb43bcced 100644 --- a/crates/consensus/auto-seal/Cargo.toml +++ b/crates/consensus/auto-seal/Cargo.toml @@ -31,6 +31,7 @@ reth-tokio-util.workspace = true reth-trie.workspace = true # ethereum +alloy-eips.workspace = true alloy-primitives.workspace = true revm-primitives.workspace = true alloy-rpc-types-engine.workspace = true @@ -45,4 +46,12 @@ tokio-stream.workspace = true tracing.workspace = true [features] -optimism = ["reth-provider/optimism", "reth-optimism-consensus"] +optimism = [ + "reth-provider/optimism", + "reth-optimism-consensus", + "reth-beacon-consensus/optimism", + "reth-execution-types/optimism", + "reth-optimism-consensus?/optimism", + "reth-primitives/optimism", + "revm-primitives/optimism" +] diff --git a/crates/consensus/auto-seal/src/lib.rs b/crates/consensus/auto-seal/src/lib.rs index 261227f1074e..16299e19ba45 100644 --- a/crates/consensus/auto-seal/src/lib.rs +++ b/crates/consensus/auto-seal/src/lib.rs @@ -15,6 +15,7 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +use alloy_eips::eip7685::Requests; use alloy_primitives::{BlockHash, BlockNumber, Bloom, B256, U256}; use reth_beacon_consensus::BeaconEngineMessage; use reth_chainspec::{EthChainSpec, EthereumHardforks}; @@ -25,7 +26,7 @@ use reth_execution_errors::{ }; use reth_execution_types::ExecutionOutcome; use reth_primitives::{ - proofs, Block, BlockBody, BlockHashOrNumber, BlockWithSenders, Header, Requests, SealedBlock, + proofs, Block, BlockBody, BlockHashOrNumber, BlockWithSenders, Header, SealedBlock, SealedHeader, TransactionSigned, Withdrawals, }; use reth_provider::{BlockReaderIdExt, StateProviderFactory, StateRootProvider}; @@ -301,7 +302,7 @@ impl StorageInner { timestamp, base_fee_per_gas, blob_gas_used, - requests_root: requests.map(|r| proofs::calculate_requests_root(&r.0)), + requests_hash: requests.map(|r| r.requests_hash()), ..Default::default() }; @@ -366,7 +367,6 @@ impl StorageInner { transactions, ommers: ommers.clone(), withdrawals: withdrawals.clone(), - requests: requests.clone(), }, } .with_recovered_senders() @@ -390,7 +390,7 @@ impl StorageInner { // root here let Block { mut header, body, .. } = block.block; - let body = BlockBody { transactions: body.transactions, ommers, withdrawals, requests }; + let body = BlockBody { transactions: body.transactions, ommers, withdrawals }; trace!(target: "consensus::auto", ?execution_outcome, ?header, ?body, "executed block, calculating state root and completing header"); @@ -682,7 +682,7 @@ mod tests { timestamp, base_fee_per_gas: None, blob_gas_used: Some(0), - requests_root: None, + requests_hash: None, excess_blob_gas: Some(0), ..Default::default() } diff --git a/crates/consensus/beacon/Cargo.toml b/crates/consensus/beacon/Cargo.toml index f1366812608c..dd1e339319b6 100644 --- a/crates/consensus/beacon/Cargo.toml +++ b/crates/consensus/beacon/Cargo.toml @@ -31,6 +31,7 @@ reth-node-types.workspace = true reth-chainspec = { workspace = true, optional = true } # ethereum +alloy-eips.workspace = true alloy-primitives.workspace = true alloy-rpc-types-engine.workspace = true @@ -77,8 +78,10 @@ assert_matches.workspace = true [features] optimism = [ - "reth-chainspec", - "reth-primitives/optimism", - "reth-provider/optimism", - "reth-blockchain-tree/optimism", + "reth-chainspec", + "reth-primitives/optimism", + "reth-provider/optimism", + "reth-blockchain-tree/optimism", + "reth-db/optimism", + "reth-db-api/optimism" ] diff --git a/crates/consensus/beacon/src/engine/handle.rs b/crates/consensus/beacon/src/engine/handle.rs index 65b7c38df918..bb5c4dee174c 100644 --- a/crates/consensus/beacon/src/engine/handle.rs +++ b/crates/consensus/beacon/src/engine/handle.rs @@ -4,6 +4,7 @@ use crate::{ engine::message::OnForkChoiceUpdated, BeaconConsensusEngineEvent, BeaconEngineMessage, BeaconForkChoiceUpdateError, BeaconOnNewPayloadError, }; +use alloy_eips::eip7685::Requests; use alloy_rpc_types_engine::{ CancunPayloadFields, ExecutionPayload, ForkchoiceState, ForkchoiceUpdated, PayloadStatus, }; @@ -47,9 +48,17 @@ where &self, payload: ExecutionPayload, cancun_fields: Option, + execution_requests: Option, ) -> Result { let (tx, rx) = oneshot::channel(); - let _ = self.to_engine.send(BeaconEngineMessage::NewPayload { payload, cancun_fields, tx }); + // HACK(onbjerg): We should have a pectra payload fields struct, this is just a temporary + // workaround. + let _ = self.to_engine.send(BeaconEngineMessage::NewPayload { + payload, + cancun_fields, + execution_requests, + tx, + }); rx.await.map_err(|_| BeaconOnNewPayloadError::EngineUnavailable)? } diff --git a/crates/consensus/beacon/src/engine/hooks/static_file.rs b/crates/consensus/beacon/src/engine/hooks/static_file.rs index 8a5a28f95741..89231ed55825 100644 --- a/crates/consensus/beacon/src/engine/hooks/static_file.rs +++ b/crates/consensus/beacon/src/engine/hooks/static_file.rs @@ -9,7 +9,8 @@ use futures::FutureExt; use reth_errors::RethResult; use reth_primitives::static_file::HighestStaticFiles; use reth_provider::{ - BlockReader, DatabaseProviderFactory, StageCheckpointReader, StaticFileProviderFactory, + BlockReader, ChainStateBlockReader, DatabaseProviderFactory, StageCheckpointReader, + StaticFileProviderFactory, }; use reth_static_file::{StaticFileProducer, StaticFileProducerWithResult}; use reth_tasks::TaskSpawner; @@ -31,8 +32,9 @@ pub struct StaticFileHook { impl StaticFileHook where Provider: StaticFileProviderFactory - + DatabaseProviderFactory - + 'static, + + DatabaseProviderFactory< + Provider: StageCheckpointReader + BlockReader + ChainStateBlockReader, + > + 'static, { /// Create a new instance pub fn new( @@ -104,6 +106,11 @@ where return Ok(None) }; + let finalized_block_number = locked_static_file_producer + .last_finalized_block()? + .map(|on_disk| finalized_block_number.min(on_disk)) + .unwrap_or(finalized_block_number); + let targets = locked_static_file_producer.get_static_file_targets(HighestStaticFiles { headers: Some(finalized_block_number), @@ -137,8 +144,9 @@ where impl EngineHook for StaticFileHook where Provider: StaticFileProviderFactory - + DatabaseProviderFactory - + 'static, + + DatabaseProviderFactory< + Provider: StageCheckpointReader + BlockReader + ChainStateBlockReader, + > + 'static, { fn name(&self) -> &'static str { "StaticFile" diff --git a/crates/consensus/beacon/src/engine/message.rs b/crates/consensus/beacon/src/engine/message.rs index fdaad0cc4b0d..56328f03db0b 100644 --- a/crates/consensus/beacon/src/engine/message.rs +++ b/crates/consensus/beacon/src/engine/message.rs @@ -1,4 +1,5 @@ use crate::engine::{error::BeaconOnNewPayloadError, forkchoice::ForkchoiceStatus}; +use alloy_eips::eip7685::Requests; use alloy_rpc_types_engine::{ CancunPayloadFields, ExecutionPayload, ForkChoiceUpdateResult, ForkchoiceState, ForkchoiceUpdateError, ForkchoiceUpdated, PayloadId, PayloadStatus, PayloadStatusEnum, @@ -146,6 +147,10 @@ pub enum BeaconEngineMessage { payload: ExecutionPayload, /// The cancun-related newPayload fields, if any. cancun_fields: Option, + // HACK(onbjerg): We should have a pectra payload fields struct, this is just a temporary + // workaround. + /// The pectra EIP-7685 execution requests. + execution_requests: Option, /// The sender for returning payload status result. tx: oneshot::Sender>, }, diff --git a/crates/consensus/beacon/src/engine/mod.rs b/crates/consensus/beacon/src/engine/mod.rs index ccea982bfbd9..cff648b2843b 100644 --- a/crates/consensus/beacon/src/engine/mod.rs +++ b/crates/consensus/beacon/src/engine/mod.rs @@ -1,3 +1,4 @@ +use alloy_eips::eip7685::Requests; use alloy_primitives::{BlockNumber, B256}; use alloy_rpc_types_engine::{ CancunPayloadFields, ExecutionPayload, ForkchoiceState, PayloadStatus, PayloadStatusEnum, @@ -1085,6 +1086,9 @@ where &mut self, payload: ExecutionPayload, cancun_fields: Option, + // HACK(onbjerg): We should have a pectra payload fields struct, this is just a temporary + // workaround. + execution_requests: Option, ) -> Result, BeaconOnNewPayloadError> { self.metrics.new_payload_messages.increment(1); @@ -1114,10 +1118,11 @@ where // // This validation **MUST** be instantly run in all cases even during active sync process. let parent_hash = payload.parent_hash(); - let block = match self - .payload_validator - .ensure_well_formed_payload(payload, cancun_fields.into()) - { + let block = match self.payload_validator.ensure_well_formed_payload( + payload, + cancun_fields.into(), + execution_requests, + ) { Ok(block) => block, Err(error) => { error!(target: "consensus::engine", %error, "Invalid payload"); @@ -1862,8 +1867,13 @@ where BeaconEngineMessage::ForkchoiceUpdated { state, payload_attrs, tx } => { this.on_forkchoice_updated(state, payload_attrs, tx); } - BeaconEngineMessage::NewPayload { payload, cancun_fields, tx } => { - match this.on_new_payload(payload, cancun_fields) { + BeaconEngineMessage::NewPayload { + payload, + cancun_fields, + execution_requests, + tx, + } => { + match this.on_new_payload(payload, cancun_fields, execution_requests) { Ok(Either::Right(block)) => { this.set_blockchain_tree_action( BlockchainTreeAction::InsertNewPayload { block, tx }, diff --git a/crates/consensus/beacon/src/engine/test_utils.rs b/crates/consensus/beacon/src/engine/test_utils.rs index 633ae03d8ad9..7e9e1ec6b26d 100644 --- a/crates/consensus/beacon/src/engine/test_utils.rs +++ b/crates/consensus/beacon/src/engine/test_utils.rs @@ -70,7 +70,7 @@ impl TestEnv { payload: T, cancun_fields: Option, ) -> Result { - self.engine_handle.new_payload(payload.into(), cancun_fields).await + self.engine_handle.new_payload(payload.into(), cancun_fields, None).await } /// Sends the `ExecutionPayload` message to the consensus engine and retries if the engine diff --git a/crates/consensus/common/Cargo.toml b/crates/consensus/common/Cargo.toml index 66a92270dba8..eaae1301b46a 100644 --- a/crates/consensus/common/Cargo.toml +++ b/crates/consensus/common/Cargo.toml @@ -19,6 +19,7 @@ reth-consensus.workspace = true # ethereum alloy-primitives.workspace = true revm-primitives.workspace = true +alloy-consensus.workspace = true [dev-dependencies] reth-storage-api.workspace = true diff --git a/crates/consensus/common/src/validation.rs b/crates/consensus/common/src/validation.rs index 88a4cabe96c5..dabb8c3c34d2 100644 --- a/crates/consensus/common/src/validation.rs +++ b/crates/consensus/common/src/validation.rs @@ -1,12 +1,10 @@ //! Collection of methods for block validation. +use alloy_consensus::constants::MAXIMUM_EXTRA_DATA_SIZE; use reth_chainspec::{EthChainSpec, EthereumHardforks}; use reth_consensus::ConsensusError; use reth_primitives::{ - constants::{ - eip4844::{DATA_GAS_PER_BLOB, MAX_DATA_GAS_PER_BLOCK}, - MAXIMUM_EXTRA_DATA_SIZE, - }, + constants::eip4844::{DATA_GAS_PER_BLOB, MAX_DATA_GAS_PER_BLOCK}, EthereumHardfork, GotExpected, Header, SealedBlock, SealedHeader, }; use revm_primitives::calc_excess_blob_gas; @@ -77,24 +75,6 @@ pub fn validate_cancun_gas(block: &SealedBlock) -> Result<(), ConsensusError> { Ok(()) } -/// Validate that requests root is present if Prague is active. -/// -/// See [EIP-7685]: General purpose execution layer requests -/// -/// [EIP-7685]: https://eips.ethereum.org/EIPS/eip-7685 -#[inline] -pub fn validate_prague_request(block: &SealedBlock) -> Result<(), ConsensusError> { - let requests_root = - block.body.calculate_requests_root().ok_or(ConsensusError::BodyRequestsMissing)?; - let header_requests_root = block.requests_root.ok_or(ConsensusError::RequestsRootMissing)?; - if requests_root != *header_requests_root { - return Err(ConsensusError::BodyRequestsRootDiff( - GotExpected { got: requests_root, expected: header_requests_root }.into(), - )); - } - Ok(()) -} - /// Validate a block without regard for state: /// /// - Compares the ommer hash in the block header to the block body @@ -127,10 +107,6 @@ pub fn validate_block_pre_execution( validate_cancun_gas(block)?; } - if chain_spec.is_prague_active_at_timestamp(block.timestamp) { - validate_prague_request(block)?; - } - Ok(()) } @@ -300,7 +276,7 @@ pub fn validate_against_parent_4844( #[cfg(test)] mod tests { use super::*; - use alloy_consensus::TxEip4844; + use alloy_consensus::{TxEip4844, EMPTY_OMMER_ROOT_HASH, EMPTY_ROOT_HASH}; use alloy_primitives::{ hex_literal::hex, Address, BlockHash, BlockNumber, Bytes, Parity, Sealable, U256, }; @@ -441,11 +417,11 @@ mod tests { let header = Header { parent_hash: hex!("859fad46e75d9be177c2584843501f2270c7e5231711e90848290d12d7c6dcdd").into(), - ommers_hash: hex!("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347").into(), + ommers_hash: EMPTY_OMMER_ROOT_HASH, beneficiary: hex!("4675c7e5baafbffbca748158becba61ef3b0a263").into(), state_root: hex!("8337403406e368b3e40411138f4868f79f6d835825d55fd0c2f6e17b1a3948e9").into(), - transactions_root: hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").into(), - receipts_root: hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").into(), + transactions_root: EMPTY_ROOT_HASH, + receipts_root: EMPTY_ROOT_HASH, logs_bloom: hex!("002400000000004000220000800002000000000000000000000000000000100000000000000000100000000000000021020000000800000006000000002100040000000c0004000000000008000008200000000000000000000000008000000001040000020000020000002000000800000002000020000000022010000000000000010002001000000000020200000000000001000200880000004000000900020000000000020000000040000000000000000000000000000080000000000001000002000000000000012000200020000000000000001000000000000020000010321400000000100000000000000000000000000000400000000000000000").into(), difficulty: U256::ZERO, // total difficulty: 0xc70d815d562d3cfa955).into(), number: 0xf21d20, @@ -460,7 +436,7 @@ mod tests { blob_gas_used: None, excess_blob_gas: None, parent_beacon_block_root: None, - requests_root: None + requests_hash: None }; // size: 0x9b5 @@ -480,7 +456,7 @@ mod tests { ( SealedBlock { header: SealedHeader::new(header, seal), - body: BlockBody { transactions, ommers, withdrawals: None, requests: None }, + body: BlockBody { transactions, ommers, withdrawals: None }, }, parent, ) @@ -552,7 +528,6 @@ mod tests { transactions: vec![transaction], ommers: vec![], withdrawals: Some(Withdrawals::default()), - requests: None, }; let block = SealedBlock::new(header, body); diff --git a/crates/consensus/consensus/Cargo.toml b/crates/consensus/consensus/Cargo.toml index 660b43865ea7..2faf3f2ac719 100644 --- a/crates/consensus/consensus/Cargo.toml +++ b/crates/consensus/consensus/Cargo.toml @@ -15,6 +15,7 @@ workspace = true reth-primitives.workspace = true # ethereum +alloy-eips.workspace = true alloy-primitives.workspace = true # misc @@ -23,5 +24,11 @@ derive_more.workspace = true [features] default = ["std"] -std = [] -test-utils = [] +std = [ + "reth-primitives/std", + "alloy-primitives/std", + "alloy-eips/std" +] +test-utils = [ + "reth-primitives/test-utils" +] diff --git a/crates/consensus/consensus/src/lib.rs b/crates/consensus/consensus/src/lib.rs index 91b93c8a75e1..4bf5da3b152f 100644 --- a/crates/consensus/consensus/src/lib.rs +++ b/crates/consensus/consensus/src/lib.rs @@ -12,10 +12,11 @@ extern crate alloc; use alloc::{fmt::Debug, vec::Vec}; +use alloy_eips::eip7685::Requests; use alloy_primitives::{BlockHash, BlockNumber, Bloom, B256, U256}; use reth_primitives::{ constants::MINIMUM_GAS_LIMIT, BlockWithSenders, GotExpected, GotExpectedBoxed, Header, - InvalidTransactionError, Receipt, Request, SealedBlock, SealedHeader, + InvalidTransactionError, Receipt, SealedBlock, SealedHeader, }; /// A consensus implementation that does nothing. @@ -31,12 +32,12 @@ pub struct PostExecutionInput<'a> { /// Receipts of the block. pub receipts: &'a [Receipt], /// EIP-7685 requests of the block. - pub requests: &'a [Request], + pub requests: &'a Requests, } impl<'a> PostExecutionInput<'a> { /// Creates a new instance of `PostExecutionInput`. - pub const fn new(receipts: &'a [Receipt], requests: &'a [Request]) -> Self { + pub const fn new(receipts: &'a [Receipt], requests: &'a Requests) -> Self { Self { receipts, requests } } } @@ -170,10 +171,10 @@ pub enum ConsensusError { #[display("mismatched block withdrawals root: {_0}")] BodyWithdrawalsRootDiff(GotExpectedBoxed), - /// Error when the requests root in the block is different from the expected requests - /// root. - #[display("mismatched block requests root: {_0}")] - BodyRequestsRootDiff(GotExpectedBoxed), + /// Error when the requests hash in the block is different from the expected requests + /// hash. + #[display("mismatched block requests hash: {_0}")] + BodyRequestsHashDiff(GotExpectedBoxed), /// Error when a block with a specific hash and number is already known. #[display("block with [hash={hash}, number={number}] is already known")] @@ -248,17 +249,17 @@ pub enum ConsensusError { #[display("missing withdrawals root")] WithdrawalsRootMissing, - /// Error when the requests root is missing. - #[display("missing requests root")] - RequestsRootMissing, + /// Error when the requests hash is missing. + #[display("missing requests hash")] + RequestsHashMissing, /// Error when an unexpected withdrawals root is encountered. #[display("unexpected withdrawals root")] WithdrawalsRootUnexpected, - /// Error when an unexpected requests root is encountered. - #[display("unexpected requests root")] - RequestsRootUnexpected, + /// Error when an unexpected requests hash is encountered. + #[display("unexpected requests hash")] + RequestsHashUnexpected, /// Error when withdrawals are missing. #[display("missing withdrawals")] diff --git a/crates/e2e-test-utils/Cargo.toml b/crates/e2e-test-utils/Cargo.toml index 2742d704054e..9fa3e2b60ab1 100644 --- a/crates/e2e-test-utils/Cargo.toml +++ b/crates/e2e-test-utils/Cargo.toml @@ -13,11 +13,8 @@ workspace = true [dependencies] reth.workspace = true reth-chainspec.workspace = true -reth-engine-local.workspace = true -reth-primitives.workspace = true reth-tracing.workspace = true reth-db = { workspace = true, features = ["test-utils"] } -reth-rpc.workspace = true reth-rpc-layer.workspace = true reth-payload-builder = { workspace = true, features = ["test-utils"] } reth-payload-primitives.workspace = true @@ -26,11 +23,8 @@ reth-node-builder = { workspace = true, features = ["test-utils"] } reth-tokio-util.workspace = true reth-stages-types.workspace = true reth-network-peers.workspace = true -reth-node-ethereum.workspace = true -reth-rpc-types-compat.workspace = true # rpc -jsonrpsee-types.workspace = true jsonrpsee.workspace = true # ethereum @@ -48,4 +42,4 @@ alloy-signer-local = { workspace = true, features = ["mnemonic"] } alloy-rpc-types.workspace = true alloy-network.workspace = true alloy-consensus = { workspace = true, features = ["kzg"] } -tracing.workspace = true \ No newline at end of file +tracing.workspace = true diff --git a/crates/e2e-test-utils/src/engine_api.rs b/crates/e2e-test-utils/src/engine_api.rs index 1b0ff9b54e71..f4aa8fdf5ff0 100644 --- a/crates/e2e-test-utils/src/engine_api.rs +++ b/crates/e2e-test-utils/src/engine_api.rs @@ -29,7 +29,7 @@ impl EngineApiTestContext { pub async fn get_payload_v3( &self, payload_id: PayloadId, - ) -> eyre::Result { + ) -> eyre::Result { Ok(EngineApiClient::::get_payload_v3(&self.engine_api_client, payload_id).await?) } @@ -50,10 +50,10 @@ impl EngineApiTestContext { versioned_hashes: Vec, ) -> eyre::Result where - E::ExecutionPayloadV3: From + PayloadEnvelopeExt, + E::ExecutionPayloadEnvelopeV3: From + PayloadEnvelopeExt, { // setup payload for submission - let envelope_v3: ::ExecutionPayloadV3 = payload.into(); + let envelope_v3: ::ExecutionPayloadEnvelopeV3 = payload.into(); // submit payload to engine api let submission = EngineApiClient::::new_payload_v3( diff --git a/crates/e2e-test-utils/src/lib.rs b/crates/e2e-test-utils/src/lib.rs index 998b48e70431..48e56910e6c0 100644 --- a/crates/e2e-test-utils/src/lib.rs +++ b/crates/e2e-test-utils/src/lib.rs @@ -7,15 +7,13 @@ use reth::{ args::{DiscoveryArgs, NetworkArgs, RpcServerArgs}, builder::{NodeBuilder, NodeConfig, NodeHandle}, network::PeersHandleProvider, - rpc::api::eth::{helpers::AddDevSigners, FullEthApiServer}, tasks::TaskManager, }; use reth_chainspec::{EthChainSpec, EthereumHardforks}; use reth_db::{test_utils::TempDatabase, DatabaseEnv}; use reth_node_builder::{ - components::NodeComponentsBuilder, rpc::EthApiBuilderProvider, FullNodeTypesAdapter, Node, - NodeAdapter, NodeAddOns, NodeComponents, NodeTypesWithDBAdapter, NodeTypesWithEngine, - RethFullAdapter, + components::NodeComponentsBuilder, rpc::RethRpcAddOns, FullNodeTypesAdapter, Node, NodeAdapter, + NodeComponents, NodeTypesWithDBAdapter, NodeTypesWithEngine, RethFullAdapter, }; use reth_provider::providers::BlockchainProvider; use tracing::{span, Level}; @@ -56,10 +54,7 @@ where TmpNodeAdapter, Components: NodeComponents, Network: PeersHandleProvider>, >, - N::AddOns: NodeAddOns< - Adapter, - EthApi: FullEthApiServer + AddDevSigners + EthApiBuilderProvider>, - >, + N::AddOns: RethRpcAddOns>, { let tasks = TaskManager::current(); let exec = tasks.executor(); @@ -115,7 +110,8 @@ type TmpNodeAdapter = FullNodeTypesAdapter< BlockchainProvider>, >; -type Adapter = NodeAdapter< +/// Type alias for a `NodeAdapter` +pub type Adapter = NodeAdapter< RethFullAdapter, <>>::ComponentsBuilder as NodeComponentsBuilder< RethFullAdapter, diff --git a/crates/e2e-test-utils/src/node.rs b/crates/e2e-test-utils/src/node.rs index 2ea39348f5de..776a437a58e9 100644 --- a/crates/e2e-test-utils/src/node.rs +++ b/crates/e2e-test-utils/src/node.rs @@ -18,7 +18,7 @@ use reth::{ }, }; use reth_chainspec::EthereumHardforks; -use reth_node_builder::{NodeAddOns, NodeTypesWithEngine}; +use reth_node_builder::{rpc::RethRpcAddOns, NodeTypesWithEngine}; use reth_stages_types::StageId; use tokio_stream::StreamExt; @@ -32,7 +32,7 @@ use crate::{ pub struct NodeTestContext where Node: FullNodeComponents, - AddOns: NodeAddOns, + AddOns: RethRpcAddOns, { /// The core structure representing the full node. pub inner: FullNode, @@ -52,7 +52,7 @@ where Node: FullNodeComponents, Node::Types: NodeTypesWithEngine, Node::Network: PeersHandleProvider, - AddOns: NodeAddOns, + AddOns: RethRpcAddOns, { /// Creates a new test node pub async fn new(node: FullNode) -> eyre::Result { @@ -67,7 +67,7 @@ where canonical_stream: node.provider.canonical_state_stream(), _marker: PhantomData::, }, - rpc: RpcTestContext { inner: node.rpc_registry }, + rpc: RpcTestContext { inner: node.add_ons_handle.rpc_registry }, }) } @@ -88,7 +88,7 @@ where attributes_generator: impl Fn(u64) -> Engine::PayloadBuilderAttributes + Copy, ) -> eyre::Result> where - Engine::ExecutionPayloadV3: From + PayloadEnvelopeExt, + Engine::ExecutionPayloadEnvelopeV3: From + PayloadEnvelopeExt, AddOns::EthApi: EthApiSpec + EthTransactions + TraceExt + FullEthApiTypes, { let mut chain = Vec::with_capacity(length as usize); @@ -113,7 +113,7 @@ where attributes_generator: impl Fn(u64) -> Engine::PayloadBuilderAttributes, ) -> eyre::Result<(Engine::BuiltPayload, Engine::PayloadBuilderAttributes)> where - ::ExecutionPayloadV3: + ::ExecutionPayloadEnvelopeV3: From + PayloadEnvelopeExt, { // trigger new payload building draining the pool @@ -135,7 +135,7 @@ where attributes_generator: impl Fn(u64) -> Engine::PayloadBuilderAttributes, ) -> eyre::Result<(Engine::BuiltPayload, Engine::PayloadBuilderAttributes)> where - ::ExecutionPayloadV3: + ::ExecutionPayloadEnvelopeV3: From + PayloadEnvelopeExt, { let (payload, eth_attr) = self.new_payload(attributes_generator).await?; diff --git a/crates/e2e-test-utils/src/payload.rs b/crates/e2e-test-utils/src/payload.rs index 1f9a89307b66..946d9af57536 100644 --- a/crates/e2e-test-utils/src/payload.rs +++ b/crates/e2e-test-utils/src/payload.rs @@ -28,7 +28,7 @@ impl PayloadTestContext { ) -> eyre::Result { self.timestamp += 1; let attributes: E::PayloadBuilderAttributes = attributes_generator(self.timestamp); - self.payload_builder.new_payload(attributes.clone()).await.unwrap(); + self.payload_builder.send_new_payload(attributes.clone()).await.unwrap()?; Ok(attributes) } diff --git a/crates/e2e-test-utils/src/transaction.rs b/crates/e2e-test-utils/src/transaction.rs index 04960304442d..58a25dc12579 100644 --- a/crates/e2e-test-utils/src/transaction.rs +++ b/crates/e2e-test-utils/src/transaction.rs @@ -56,8 +56,7 @@ impl TransactionTestContext { delegate_to: Address, wallet: PrivateKeySigner, ) -> TxEnvelope { - let authorization = - Authorization { chain_id: U256::from(chain_id), address: delegate_to, nonce: 0 }; + let authorization = Authorization { chain_id, address: delegate_to, nonce: 0 }; let signature = wallet .sign_hash_sync(&authorization.signature_hash()) .expect("could not sign authorization"); diff --git a/crates/engine/invalid-block-hooks/src/witness.rs b/crates/engine/invalid-block-hooks/src/witness.rs index bb227e304198..416c4adb40f8 100644 --- a/crates/engine/invalid-block-hooks/src/witness.rs +++ b/crates/engine/invalid-block-hooks/src/witness.rs @@ -6,14 +6,15 @@ use eyre::OptionExt; use pretty_assertions::Comparison; use reth_chainspec::{EthChainSpec, EthereumHardforks}; use reth_engine_primitives::InvalidBlockHook; -use reth_evm::{system_calls::SystemCaller, ConfigureEvm}; +use reth_evm::{ + state_change::post_block_balance_increments, system_calls::SystemCaller, ConfigureEvm, +}; use reth_primitives::{Header, Receipt, SealedBlockWithSenders, SealedHeader}; use reth_provider::{BlockExecutionOutput, ChainSpecProvider, StateProviderFactory}; use reth_revm::{ database::StateProviderDatabase, db::states::bundle_state::BundleRetention, primitives::{BlockEnv, CfgEnvWithHandlerCfg, EnvWithHandlerCfg}, - state_change::post_block_balance_increments, DatabaseCommit, StateBuilder, }; use reth_rpc_api::DebugApiClient; @@ -162,7 +163,7 @@ where let response = ExecutionWitness { state: HashMap::from_iter(state), codes: Default::default(), - keys: Some(state_preimages), + keys: state_preimages, }; let re_executed_witness_path = self.save_file( format!("{}_{}.witness.re_executed.json", block.number, block.hash()), diff --git a/crates/engine/local/Cargo.toml b/crates/engine/local/Cargo.toml index f22ab1f8d560..d9dc63253399 100644 --- a/crates/engine/local/Cargo.toml +++ b/crates/engine/local/Cargo.toml @@ -46,4 +46,8 @@ op-alloy-rpc-types-engine = { workspace = true, optional = true } workspace = true [features] -optimism = ["op-alloy-rpc-types-engine"] +optimism = [ + "op-alloy-rpc-types-engine", + "reth-beacon-consensus/optimism", + "reth-provider/optimism" +] diff --git a/crates/engine/local/src/miner.rs b/crates/engine/local/src/miner.rs index f20d70b14893..552cbd047764 100644 --- a/crates/engine/local/src/miner.rs +++ b/crates/engine/local/src/miner.rs @@ -9,7 +9,7 @@ use reth_chainspec::EthereumHardforks; use reth_engine_primitives::EngineTypes; use reth_payload_builder::PayloadBuilderHandle; use reth_payload_primitives::{ - BuiltPayload, PayloadAttributesBuilder, PayloadBuilder, PayloadTypes, + BuiltPayload, PayloadAttributesBuilder, PayloadBuilder, PayloadKind, PayloadTypes, }; use reth_provider::{BlockReader, ChainSpecProvider}; use reth_rpc_types_compat::engine::payload::block_to_payload; @@ -202,10 +202,9 @@ where let payload_id = res.payload_id.ok_or_eyre("No payload id")?; - // wait for some time to let the payload be built - tokio::time::sleep(Duration::from_millis(200)).await; - - let Some(Ok(payload)) = self.payload_builder.best_payload(payload_id).await else { + let Some(Ok(payload)) = + self.payload_builder.resolve_kind(payload_id, PayloadKind::WaitForPending).await + else { eyre::bail!("No payload") }; @@ -223,6 +222,8 @@ where self.to_engine.send(BeaconEngineMessage::NewPayload { payload: block_to_payload(payload.block().clone()), cancun_fields, + // todo: prague + execution_requests: None, tx, })?; diff --git a/crates/engine/primitives/src/lib.rs b/crates/engine/primitives/src/lib.rs index 2cf1366eb014..fab96b0d17e9 100644 --- a/crates/engine/primitives/src/lib.rs +++ b/crates/engine/primitives/src/lib.rs @@ -24,9 +24,9 @@ use serde::{de::DeserializeOwned, ser::Serialize}; pub trait EngineTypes: PayloadTypes< BuiltPayload: TryInto - + TryInto - + TryInto - + TryInto, + + TryInto + + TryInto + + TryInto, > + DeserializeOwned + Serialize + 'static @@ -34,11 +34,29 @@ pub trait EngineTypes: /// Execution Payload V1 type. type ExecutionPayloadV1: DeserializeOwned + Serialize + Clone + Unpin + Send + Sync + 'static; /// Execution Payload V2 type. - type ExecutionPayloadV2: DeserializeOwned + Serialize + Clone + Unpin + Send + Sync + 'static; + type ExecutionPayloadEnvelopeV2: DeserializeOwned + + Serialize + + Clone + + Unpin + + Send + + Sync + + 'static; /// Execution Payload V3 type. - type ExecutionPayloadV3: DeserializeOwned + Serialize + Clone + Unpin + Send + Sync + 'static; + type ExecutionPayloadEnvelopeV3: DeserializeOwned + + Serialize + + Clone + + Unpin + + Send + + Sync + + 'static; /// Execution Payload V4 type. - type ExecutionPayloadV4: DeserializeOwned + Serialize + Clone + Unpin + Send + Sync + 'static; + type ExecutionPayloadEnvelopeV4: DeserializeOwned + + Serialize + + Clone + + Unpin + + Send + + Sync + + 'static; } /// Type that validates the payloads sent to the engine. diff --git a/crates/engine/tree/Cargo.toml b/crates/engine/tree/Cargo.toml index 91c9cd5422d0..6fe741db8833 100644 --- a/crates/engine/tree/Cargo.toml +++ b/crates/engine/tree/Cargo.toml @@ -76,15 +76,24 @@ reth-chainspec.workspace = true alloy-rlp.workspace = true assert_matches.workspace = true -rand.workspace = true [features] test-utils = [ - "reth-db/test-utils", - "reth-chain-state/test-utils", - "reth-network-p2p/test-utils", - "reth-prune-types", - "reth-stages/test-utils", - "reth-static-file", - "reth-tracing" + "reth-db/test-utils", + "reth-chain-state/test-utils", + "reth-network-p2p/test-utils", + "reth-prune-types", + "reth-stages/test-utils", + "reth-static-file", + "reth-tracing", + "reth-blockchain-tree/test-utils", + "reth-chainspec/test-utils", + "reth-consensus/test-utils", + "reth-evm/test-utils", + "reth-payload-builder/test-utils", + "reth-primitives/test-utils", + "reth-revm/test-utils", + "reth-stages-api/test-utils", + "reth-provider/test-utils", + "reth-trie/test-utils" ] diff --git a/crates/engine/tree/src/tree/mod.rs b/crates/engine/tree/src/tree/mod.rs index 3eadbbd522db..a2abd3f531df 100644 --- a/crates/engine/tree/src/tree/mod.rs +++ b/crates/engine/tree/src/tree/mod.rs @@ -70,6 +70,7 @@ use crate::{ engine::{EngineApiKind, EngineApiRequest}, tree::metrics::EngineApiMetrics, }; +use alloy_eips::eip7685::Requests; pub use config::TreeConfig; pub use invalid_block_hook::{InvalidBlockHooks, NoopInvalidBlockHook}; pub use persistence_state::PersistenceState; @@ -721,6 +722,7 @@ where &mut self, payload: ExecutionPayload, cancun_fields: Option, + execution_requests: Option, ) -> Result, InsertBlockFatalError> { trace!(target: "engine::tree", "invoked new payload"); self.metrics.engine.new_payload_messages.increment(1); @@ -751,10 +753,11 @@ where // // This validation **MUST** be instantly run in all cases even during active sync process. let parent_hash = payload.parent_hash(); - let block = match self - .payload_validator - .ensure_well_formed_payload(payload, cancun_fields.into()) - { + let block = match self.payload_validator.ensure_well_formed_payload( + payload, + cancun_fields.into(), + execution_requests, + ) { Ok(block) => block, Err(error) => { error!(target: "engine::tree", %error, "Invalid payload"); @@ -1143,6 +1146,7 @@ where if blocks_to_persist.is_empty() { debug!(target: "engine::tree", "Returned empty set of blocks to persist"); } else { + debug!(target: "engine::tree", blocks = ?blocks_to_persist.iter().map(|block| block.block.num_hash()).collect::>(), "Persisting blocks"); let (tx, rx) = oneshot::channel(); let _ = self.persistence.save_blocks(blocks_to_persist, tx); self.persistence_state.start(rx); @@ -1171,7 +1175,7 @@ where return Ok(()) }; - trace!(target: "engine::tree", ?last_persisted_block_hash, ?last_persisted_block_number, "Finished persisting, calling finish"); + debug!(target: "engine::tree", ?last_persisted_block_hash, ?last_persisted_block_number, "Finished persisting, calling finish"); self.persistence_state .finish(last_persisted_block_hash, last_persisted_block_number); self.on_new_persisted_block()?; @@ -1236,8 +1240,14 @@ where error!(target: "engine::tree", "Failed to send event: {err:?}"); } } - BeaconEngineMessage::NewPayload { payload, cancun_fields, tx } => { - let output = self.on_new_payload(payload, cancun_fields); + BeaconEngineMessage::NewPayload { + payload, + cancun_fields, + execution_requests, + tx, + } => { + let output = + self.on_new_payload(payload, cancun_fields, execution_requests); if let Err(err) = tx.send(output.map(|o| o.outcome).map_err(|e| { reth_beacon_consensus::BeaconOnNewPayloadError::Internal( Box::new(e), @@ -2852,6 +2862,7 @@ mod tests { parent_beacon_block_root: block.parent_beacon_block_root.unwrap(), versioned_hashes: vec![], }), + None, ) .unwrap(); } @@ -3114,7 +3125,7 @@ mod tests { let mut test_harness = TestHarness::new(HOLESKY.clone()); - let outcome = test_harness.tree.on_new_payload(payload.into(), None).unwrap(); + let outcome = test_harness.tree.on_new_payload(payload.into(), None, None).unwrap(); assert!(outcome.outcome.is_syncing()); // ensure block is buffered @@ -3159,6 +3170,7 @@ mod tests { BeaconEngineMessage::NewPayload { payload: payload.clone().into(), cancun_fields: None, + execution_requests: None, tx, } .into(), diff --git a/crates/engine/util/Cargo.toml b/crates/engine/util/Cargo.toml index 20a0acb8d428..07aa40165e2b 100644 --- a/crates/engine/util/Cargo.toml +++ b/crates/engine/util/Cargo.toml @@ -27,8 +27,10 @@ revm-primitives.workspace = true reth-trie.workspace = true # alloy +alloy-eips.workspace = true alloy-primitives.workspace = true alloy-rpc-types-engine.workspace = true +alloy-consensus.workspace = true # async tokio = { workspace = true, default-features = false } @@ -49,5 +51,8 @@ tracing.workspace = true [features] optimism = [ - "reth-beacon-consensus/optimism", + "reth-beacon-consensus/optimism", + "reth-primitives/optimism", + "reth-provider/optimism", + "revm-primitives/optimism" ] diff --git a/crates/engine/util/src/engine_store.rs b/crates/engine/util/src/engine_store.rs index 1f3445199611..de193bf3bbe0 100644 --- a/crates/engine/util/src/engine_store.rs +++ b/crates/engine/util/src/engine_store.rs @@ -73,7 +73,13 @@ impl EngineMessageStore { })?, )?; } - BeaconEngineMessage::NewPayload { payload, cancun_fields, tx: _tx } => { + // todo(onbjerg): execution requests + BeaconEngineMessage::NewPayload { + payload, + cancun_fields, + execution_requests: _, + tx: _tx, + } => { let filename = format!("{}-new_payload-{}.json", timestamp, payload.block_hash()); fs::write( self.path.join(filename), diff --git a/crates/engine/util/src/reorg.rs b/crates/engine/util/src/reorg.rs index 611095101ff3..85216e32fad0 100644 --- a/crates/engine/util/src/reorg.rs +++ b/crates/engine/util/src/reorg.rs @@ -1,5 +1,7 @@ //! Stream wrapper that simulates reorgs. +use alloy_consensus::Transaction; +use alloy_eips::eip7685::Requests; use alloy_primitives::U256; use alloy_rpc_types_engine::{ CancunPayloadFields, ExecutionPayload, ForkchoiceState, PayloadStatus, @@ -10,14 +12,16 @@ use reth_beacon_consensus::{BeaconEngineMessage, BeaconOnNewPayloadError, OnFork use reth_engine_primitives::EngineTypes; use reth_errors::{BlockExecutionError, BlockValidationError, RethError, RethResult}; use reth_ethereum_forks::EthereumHardforks; -use reth_evm::{system_calls::SystemCaller, ConfigureEvm}; +use reth_evm::{ + state_change::post_block_withdrawals_balance_increments, system_calls::SystemCaller, + ConfigureEvm, +}; use reth_payload_validator::ExecutionPayloadValidator; use reth_primitives::{proofs, Block, BlockBody, Header, Receipt, Receipts}; use reth_provider::{BlockReader, ExecutionOutcome, ProviderError, StateProviderFactory}; use reth_revm::{ database::StateProviderDatabase, db::{states::bundle_state::BundleRetention, State}, - state_change::post_block_withdrawals_balance_increments, DatabaseCommit, }; use reth_rpc_types_compat::engine::payload::block_to_payload; @@ -146,7 +150,12 @@ where let next = ready!(this.stream.poll_next_unpin(cx)); let item = match (next, &this.last_forkchoice_state) { ( - Some(BeaconEngineMessage::NewPayload { payload, cancun_fields, tx }), + Some(BeaconEngineMessage::NewPayload { + payload, + cancun_fields, + execution_requests, + tx, + }), Some(last_forkchoice_state), ) if this.forkchoice_states_forwarded > this.frequency && // Only enter reorg state if new payload attaches to current head. @@ -161,26 +170,29 @@ where // forkchoice state. We will rely on CL to reorg us back to canonical chain. // TODO: This is an expensive blocking operation, ideally it's spawned as a task // so that the stream could yield the control back. - let (reorg_payload, reorg_cancun_fields) = match create_reorg_head( - this.provider, - this.evm_config, - this.payload_validator, - *this.depth, - payload.clone(), - cancun_fields.clone(), - ) { - Ok(result) => result, - Err(error) => { - error!(target: "engine::stream::reorg", %error, "Error attempting to create reorg head"); - // Forward the payload and attempt to create reorg on top of - // the next one - return Poll::Ready(Some(BeaconEngineMessage::NewPayload { - payload, - cancun_fields, - tx, - })) - } - }; + let (reorg_payload, reorg_cancun_fields, reorg_execution_requests) = + match create_reorg_head( + this.provider, + this.evm_config, + this.payload_validator, + *this.depth, + payload.clone(), + cancun_fields.clone(), + execution_requests.clone(), + ) { + Ok(result) => result, + Err(error) => { + error!(target: "engine::stream::reorg", %error, "Error attempting to create reorg head"); + // Forward the payload and attempt to create reorg on top of + // the next one + return Poll::Ready(Some(BeaconEngineMessage::NewPayload { + payload, + cancun_fields, + execution_requests, + tx, + })) + } + }; let reorg_forkchoice_state = ForkchoiceState { finalized_block_hash: last_forkchoice_state.finalized_block_hash, safe_block_hash: last_forkchoice_state.safe_block_hash, @@ -196,11 +208,17 @@ where let queue = VecDeque::from([ // Current payload - BeaconEngineMessage::NewPayload { payload, cancun_fields, tx }, + BeaconEngineMessage::NewPayload { + payload, + cancun_fields, + execution_requests, + tx, + }, // Reorg payload BeaconEngineMessage::NewPayload { payload: reorg_payload, cancun_fields: reorg_cancun_fields, + execution_requests: reorg_execution_requests, tx: reorg_payload_tx, }, // Reorg forkchoice state @@ -235,7 +253,8 @@ fn create_reorg_head( mut depth: usize, next_payload: ExecutionPayload, next_cancun_fields: Option, -) -> RethResult<(ExecutionPayload, Option)> + next_execution_requests: Option, +) -> RethResult<(ExecutionPayload, Option, Option)> where Provider: BlockReader + StateProviderFactory, Evm: ConfigureEvm
, @@ -245,7 +264,11 @@ where // Ensure next payload is valid. let next_block = payload_validator - .ensure_well_formed_payload(next_payload, next_cancun_fields.into()) + .ensure_well_formed_payload( + next_payload, + next_cancun_fields.into(), + next_execution_requests, + ) .map_err(RethError::msg)?; // Fetch reorg target block depending on its depth and its parent. @@ -400,7 +423,7 @@ where transactions_root: proofs::calculate_transaction_root(&transactions), receipts_root: outcome.receipts_root_slow(reorg_target.header.number).unwrap(), logs_bloom: outcome.block_logs_bloom(reorg_target.header.number).unwrap(), - requests_root: None, // TODO(prague) + requests_hash: None, // TODO(prague) gas_used: cumulative_gas_used, blob_gas_used: blob_gas_used.map(Into::into), excess_blob_gas: excess_blob_gas.map(Into::into), @@ -410,7 +433,6 @@ where transactions, ommers: reorg_target.body.ommers, withdrawals: reorg_target.body.withdrawals, - requests: None, // TODO(prague) }, } .seal_slow(); @@ -421,5 +443,7 @@ where .header .parent_beacon_block_root .map(|root| CancunPayloadFields { parent_beacon_block_root: root, versioned_hashes }), + // todo(prague) + None, )) } diff --git a/crates/engine/util/src/skip_new_payload.rs b/crates/engine/util/src/skip_new_payload.rs index d2450711ecfc..47c48282eef6 100644 --- a/crates/engine/util/src/skip_new_payload.rs +++ b/crates/engine/util/src/skip_new_payload.rs @@ -41,7 +41,12 @@ where loop { let next = ready!(this.stream.poll_next_unpin(cx)); let item = match next { - Some(BeaconEngineMessage::NewPayload { payload, cancun_fields, tx }) => { + Some(BeaconEngineMessage::NewPayload { + payload, + cancun_fields, + execution_requests, + tx, + }) => { if this.skipped < this.threshold { *this.skipped += 1; tracing::warn!( @@ -56,7 +61,12 @@ where continue } *this.skipped = 0; - Some(BeaconEngineMessage::NewPayload { payload, cancun_fields, tx }) + Some(BeaconEngineMessage::NewPayload { + payload, + cancun_fields, + execution_requests, + tx, + }) } next => next, }; diff --git a/crates/ethereum-forks/Cargo.toml b/crates/ethereum-forks/Cargo.toml index 08a0bc98dbcf..9f7ce7ee8f39 100644 --- a/crates/ethereum-forks/Cargo.toml +++ b/crates/ethereum-forks/Cargo.toml @@ -35,16 +35,31 @@ auto_impl.workspace = true [dev-dependencies] arbitrary = { workspace = true, features = ["derive"] } -proptest.workspace = true +alloy-consensus.workspace = true [features] default = ["std", "serde", "rustc-hash"] -arbitrary = ["dep:arbitrary", "dep:proptest", "dep:proptest-derive"] -serde = ["dep:serde"] +arbitrary = [ + "dep:arbitrary", + "dep:proptest", + "dep:proptest-derive", + "alloy-chains/arbitrary", + "alloy-consensus/arbitrary", + "alloy-primitives/arbitrary" +] +serde = [ + "dep:serde", + "alloy-chains/serde", + "alloy-consensus/serde", + "alloy-primitives/serde" +] std = [ - "alloy-chains/std", - "alloy-primitives/std", - "thiserror-no-std/std", - "rustc-hash/std", + "alloy-chains/std", + "alloy-primitives/std", + "thiserror-no-std/std", + "rustc-hash/std", + "alloy-consensus/std", + "once_cell/std", + "serde?/std" ] rustc-hash = ["dep:rustc-hash"] diff --git a/crates/ethereum-forks/src/forkid.rs b/crates/ethereum-forks/src/forkid.rs index 6876d0eb926d..b612f3b0b1ae 100644 --- a/crates/ethereum-forks/src/forkid.rs +++ b/crates/ethereum-forks/src/forkid.rs @@ -446,15 +446,12 @@ impl Cache { #[cfg(test)] mod tests { use super::*; - use alloy_primitives::b256; - - const GENESIS_HASH: B256 = - b256!("d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"); + use alloy_consensus::constants::MAINNET_GENESIS_HASH; // EIP test vectors. #[test] fn forkhash() { - let mut fork_hash = ForkHash::from(GENESIS_HASH); + let mut fork_hash = ForkHash::from(MAINNET_GENESIS_HASH); assert_eq!(fork_hash.0, hex!("fc64ec04")); fork_hash += 1_150_000u64; @@ -468,7 +465,7 @@ mod tests { fn compatibility_check() { let mut filter = ForkFilter::new( Head { number: 0, ..Default::default() }, - GENESIS_HASH, + MAINNET_GENESIS_HASH, 0, vec![ ForkFilterKey::Block(1_150_000), @@ -727,7 +724,7 @@ mod tests { let mut fork_filter = ForkFilter::new( Head { number: 0, ..Default::default() }, - GENESIS_HASH, + MAINNET_GENESIS_HASH, 0, vec![ForkFilterKey::Block(b1), ForkFilterKey::Block(b2)], ); diff --git a/crates/ethereum/consensus/Cargo.toml b/crates/ethereum/consensus/Cargo.toml index 02d217b63b27..bace4195ca63 100644 --- a/crates/ethereum/consensus/Cargo.toml +++ b/crates/ethereum/consensus/Cargo.toml @@ -18,6 +18,8 @@ reth-primitives.workspace = true reth-consensus.workspace = true # alloy +alloy-eips.workspace = true alloy-primitives.workspace = true +alloy-consensus.workspace = true tracing.workspace = true diff --git a/crates/ethereum/consensus/src/lib.rs b/crates/ethereum/consensus/src/lib.rs index e74f3498fa5f..dd286584a598 100644 --- a/crates/ethereum/consensus/src/lib.rs +++ b/crates/ethereum/consensus/src/lib.rs @@ -8,6 +8,7 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +use alloy_consensus::EMPTY_OMMER_ROOT_HASH; use alloy_primitives::U256; use reth_chainspec::{EthChainSpec, EthereumHardfork, EthereumHardforks}; use reth_consensus::{Consensus, ConsensusError, PostExecutionInput}; @@ -19,7 +20,6 @@ use reth_consensus_common::validation::{ }; use reth_primitives::{ constants::MINIMUM_GAS_LIMIT, BlockWithSenders, Header, SealedBlock, SealedHeader, - EMPTY_OMMER_ROOT_HASH, }; use std::{fmt::Debug, sync::Arc, time::SystemTime}; @@ -121,11 +121,11 @@ impl Consensu } if self.chain_spec.is_prague_active_at_timestamp(header.timestamp) { - if header.requests_root.is_none() { - return Err(ConsensusError::RequestsRootMissing) + if header.requests_hash.is_none() { + return Err(ConsensusError::RequestsHashMissing) } - } else if header.requests_root.is_some() { - return Err(ConsensusError::RequestsRootUnexpected) + } else if header.requests_hash.is_some() { + return Err(ConsensusError::RequestsHashUnexpected) } Ok(()) diff --git a/crates/ethereum/consensus/src/validation.rs b/crates/ethereum/consensus/src/validation.rs index e510a91ab964..f990ecc57d82 100644 --- a/crates/ethereum/consensus/src/validation.rs +++ b/crates/ethereum/consensus/src/validation.rs @@ -1,7 +1,8 @@ +use alloy_eips::eip7685::Requests; use alloy_primitives::{Bloom, B256}; use reth_chainspec::EthereumHardforks; use reth_consensus::ConsensusError; -use reth_primitives::{gas_spent_by_transactions, BlockWithSenders, GotExpected, Receipt, Request}; +use reth_primitives::{gas_spent_by_transactions, BlockWithSenders, GotExpected, Receipt}; /// Validate a block with regard to execution results: /// @@ -11,7 +12,7 @@ pub fn validate_block_post_execution( block: &BlockWithSenders, chain_spec: &ChainSpec, receipts: &[Receipt], - requests: &[Request], + requests: &Requests, ) -> Result<(), ConsensusError> { // Check if gas used matches the value set in header. let cumulative_gas_used = @@ -36,15 +37,15 @@ pub fn validate_block_post_execution( } } - // Validate that the header requests root matches the calculated requests root + // Validate that the header requests hash matches the calculated requests hash if chain_spec.is_prague_active_at_timestamp(block.timestamp) { - let Some(header_requests_root) = block.header.requests_root else { - return Err(ConsensusError::RequestsRootMissing) + let Some(header_requests_hash) = block.header.requests_hash else { + return Err(ConsensusError::RequestsHashMissing) }; - let requests_root = reth_primitives::proofs::calculate_requests_root(requests); - if requests_root != header_requests_root { - return Err(ConsensusError::BodyRequestsRootDiff( - GotExpected::new(requests_root, header_requests_root).into(), + let requests_hash = requests.requests_hash(); + if requests_hash != header_requests_hash { + return Err(ConsensusError::BodyRequestsHashDiff( + GotExpected::new(requests_hash, header_requests_hash).into(), )) } } diff --git a/crates/ethereum/engine-primitives/src/lib.rs b/crates/ethereum/engine-primitives/src/lib.rs index 034a8c6bffbb..20a55883680b 100644 --- a/crates/ethereum/engine-primitives/src/lib.rs +++ b/crates/ethereum/engine-primitives/src/lib.rs @@ -44,9 +44,9 @@ where + TryInto, { type ExecutionPayloadV1 = ExecutionPayloadV1; - type ExecutionPayloadV2 = ExecutionPayloadEnvelopeV2; - type ExecutionPayloadV3 = ExecutionPayloadEnvelopeV3; - type ExecutionPayloadV4 = ExecutionPayloadEnvelopeV4; + type ExecutionPayloadEnvelopeV2 = ExecutionPayloadEnvelopeV2; + type ExecutionPayloadEnvelopeV3 = ExecutionPayloadEnvelopeV3; + type ExecutionPayloadEnvelopeV4 = ExecutionPayloadEnvelopeV4; } /// A default payload type for [`EthEngineTypes`] diff --git a/crates/ethereum/engine-primitives/src/payload.rs b/crates/ethereum/engine-primitives/src/payload.rs index ae370fdb9d7b..1ad9c5450ee7 100644 --- a/crates/ethereum/engine-primitives/src/payload.rs +++ b/crates/ethereum/engine-primitives/src/payload.rs @@ -1,6 +1,6 @@ //! Contains types required for building a payload. -use alloy_eips::eip4844::BlobTransactionSidecar; +use alloy_eips::{eip4844::BlobTransactionSidecar, eip7685::Requests}; use alloy_primitives::{Address, B256, U256}; use alloy_rlp::Encodable; use alloy_rpc_types_engine::{ @@ -11,8 +11,7 @@ use reth_chain_state::ExecutedBlock; use reth_payload_primitives::{BuiltPayload, PayloadBuilderAttributes}; use reth_primitives::{SealedBlock, Withdrawals}; use reth_rpc_types_compat::engine::payload::{ - block_to_payload_v1, block_to_payload_v3, block_to_payload_v4, - convert_block_to_payload_field_v2, + block_to_payload_v1, block_to_payload_v3, convert_block_to_payload_field_v2, }; use std::convert::Infallible; @@ -142,10 +141,17 @@ impl From for ExecutionPayloadEnvelopeV3 { impl From for ExecutionPayloadEnvelopeV4 { fn from(value: EthBuiltPayload) -> Self { - let EthBuiltPayload { block, fees, sidecars, .. } = value; - + let EthBuiltPayload { block, fees, sidecars, executed_block, .. } = value; + + // if we have an executed block, we pop off the first set of requests from the execution + // outcome. the assumption here is that there will always only be one block in the execution + // outcome. + let execution_requests = executed_block + .and_then(|block| block.execution_outcome().requests.first().cloned()) + .map(Requests::take) + .unwrap_or_default(); Self { - execution_payload: block_to_payload_v4(block), + execution_payload: block_to_payload_v3(block), block_value: fees, // From the engine API spec: // @@ -157,6 +163,7 @@ impl From for ExecutionPayloadEnvelopeV4 { // should_override_builder: false, blobs_bundle: sidecars.into_iter().map(Into::into).collect::>().into(), + execution_requests, } } } diff --git a/crates/ethereum/evm/Cargo.toml b/crates/ethereum/evm/Cargo.toml index 61ce0a23b904..17e870e61113 100644 --- a/crates/ethereum/evm/Cargo.toml +++ b/crates/ethereum/evm/Cargo.toml @@ -18,8 +18,7 @@ reth-evm.workspace = true reth-primitives = { workspace = true, features = ["reth-codec"] } reth-revm.workspace = true reth-ethereum-consensus.workspace = true -reth-prune-types.workspace = true -reth-execution-types.workspace = true +reth-consensus.workspace = true # Ethereum revm-primitives.workspace = true @@ -28,16 +27,28 @@ revm-primitives.workspace = true alloy-primitives.workspace = true alloy-eips.workspace = true alloy-sol-types.workspace = true +alloy-consensus.workspace = true [dev-dependencies] reth-testing-utils.workspace = true +reth-evm = { workspace = true, features = ["test-utils"] } reth-revm = { workspace = true, features = ["test-utils"] } reth-primitives = { workspace = true, features = ["secp256k1"] } +reth-execution-types.workspace = true secp256k1.workspace = true serde_json.workspace = true alloy-genesis.workspace = true -alloy-consensus.workspace = true [features] default = ["std"] -std = [] +std = [ + "reth-consensus/std", + "reth-primitives/std", + "reth-revm/std", + "alloy-consensus/std", + "alloy-eips/std", + "alloy-genesis/std", + "alloy-primitives/std", + "revm-primitives/std", + "secp256k1/std" +] diff --git a/crates/ethereum/evm/src/eip6110.rs b/crates/ethereum/evm/src/eip6110.rs index e78becd960cf..4cf1c6ae9da5 100644 --- a/crates/ethereum/evm/src/eip6110.rs +++ b/crates/ethereum/evm/src/eip6110.rs @@ -1,11 +1,11 @@ //! EIP-6110 deposit requests parsing use alloc::{string::ToString, vec::Vec}; -use alloy_eips::eip6110::{DepositRequest, MAINNET_DEPOSIT_CONTRACT_ADDRESS}; -use alloy_primitives::Log; +use alloy_eips::eip6110::MAINNET_DEPOSIT_CONTRACT_ADDRESS; +use alloy_primitives::{Bytes, Log}; use alloy_sol_types::{sol, SolEvent}; use reth_chainspec::ChainSpec; use reth_evm::execute::BlockValidationError; -use reth_primitives::{Receipt, Request}; +use reth_primitives::Receipt; sol! { #[allow(missing_docs)] @@ -20,73 +20,57 @@ sol! { /// Parse [deposit contract](https://etherscan.io/address/0x00000000219ab540356cbb839cbe05303d7705fa) /// (address is from the passed [`ChainSpec`]) deposits from receipts, and return them as a -/// [vector](Vec) of (requests)[Request]. +/// [vector](Vec) of (requests)[`alloy_eips::eip7685::Requests`]. pub fn parse_deposits_from_receipts<'a, I>( chain_spec: &ChainSpec, receipts: I, -) -> Result, BlockValidationError> +) -> Result where I: IntoIterator, { + let mut requests = Vec::new(); let deposit_contract_address = chain_spec .deposit_contract .as_ref() .map_or(MAINNET_DEPOSIT_CONTRACT_ADDRESS, |contract| contract.address); - receipts + let logs: Vec<_> = receipts .into_iter() - .flat_map(|receipt| receipt.logs.iter()) - // No need to filter for topic because there's only one event and that's the Deposit event - // in the deposit contract. + .flat_map(|receipt| &receipt.logs) + // No need to filter for topic because there's only one event and that's the Deposit + // event in the deposit contract. .filter(|log| log.address == deposit_contract_address) - .map(|log| { - let decoded_log = DepositEvent::decode_log(log, false)?; - let deposit = parse_deposit_from_log(&decoded_log); - Ok(Request::DepositRequest(deposit)) - }) - .collect::, _>>() - .map_err(|err: alloy_sol_types::Error| { - BlockValidationError::DepositRequestDecode(err.to_string()) - }) + .collect(); + + for log in &logs { + let decoded_log = + DepositEvent::decode_log(log, false).map_err(|err: alloy_sol_types::Error| { + BlockValidationError::DepositRequestDecode(err.to_string()) + })?; + requests.extend(parse_deposit_from_log(&decoded_log).as_ref()) + } + + Ok(requests.into()) } -fn parse_deposit_from_log(log: &Log) -> DepositRequest { +fn parse_deposit_from_log(log: &Log) -> Bytes { // SAFETY: These `expect` https://github.com/ethereum/consensus-specs/blob/5f48840f4d768bf0e0a8156a3ed06ec333589007/solidity_deposit_contract/deposit_contract.sol#L107-L110 // are safe because the `DepositEvent` is the only event in the deposit contract and the length // checks are done there. - DepositRequest { - pubkey: log - .pubkey - .as_ref() - .try_into() - .expect("pubkey length should be enforced in deposit contract"), - withdrawal_credentials: log - .withdrawal_credentials - .as_ref() - .try_into() - .expect("withdrawal_credentials length should be enforced in deposit contract"), - amount: u64::from_le_bytes( - log.amount - .as_ref() - .try_into() - .expect("amount length should be enforced in deposit contract"), - ), - signature: log - .signature - .as_ref() - .try_into() - .expect("signature length should be enforced in deposit contract"), - index: u64::from_le_bytes( - log.index - .as_ref() - .try_into() - .expect("deposit index length should be enforced in deposit contract"), - ), - } + [ + log.pubkey.as_ref(), + log.withdrawal_credentials.as_ref(), + log.amount.as_ref(), + log.signature.as_ref(), + log.index.as_ref(), + ] + .concat() + .into() } #[cfg(test)] mod tests { use super::*; + use alloy_primitives::bytes; use reth_chainspec::MAINNET; use reth_primitives::TxType; @@ -119,9 +103,12 @@ mod tests { }, ]; - let requests = parse_deposits_from_receipts(&MAINNET, &receipts).unwrap(); - assert_eq!(requests.len(), 2); - assert_eq!(requests[0].as_deposit_request().unwrap().amount, 32e9 as u64); - assert_eq!(requests[1].as_deposit_request().unwrap().amount, 32e9 as u64); + let request_data = parse_deposits_from_receipts(&MAINNET, &receipts).unwrap(); + assert_eq!( + request_data, + bytes!( + "998c8086669bf65e24581cda47d8537966e9f5066fc6ffdcba910a1bfb91eae7a4873fcce166a1c4ea217e6b1afd396201000000000000000000000001c340fb72ed14d4eaa71f7633ee9e33b88d4f39004059730700000098ddbffd700c1aac324cfdf0492ff289223661eb26718ce3651ba2469b22f480d56efab432ed91af05a006bde0c1ea68134e0acd8cacca0c13ad1f716db874b44abfcc966368019753174753bca3af2ea84bc569c46f76592a91e97f311eddece474160000000000a1a2ba870a90e889aa594a0cc1c6feffb94c2d8f65646c937f1f456a315ef649533e25a4614d8f4f66ebdb06481b90af0100000000000000000000000a0f04a231efbc29e1db7d086300ff550211c2f60040597307000000ad416d590e1a7f52baff770a12835b68904efad22cc9f8ba531e50cbbd26f32b9c7373cf6538a0577f501e4d3e3e63e208767bcccaae94e1e3720bfb734a286f9c017d17af46536545ccb7ca94d71f295e71f6d25bf978c09ada6f8d3f7ba039e374160000000000" + ) + ); } } diff --git a/crates/ethereum/evm/src/execute.rs b/crates/ethereum/evm/src/execute.rs index 108e1f87c455..e8b238c6a7cb 100644 --- a/crates/ethereum/evm/src/execute.rs +++ b/crates/ethereum/evm/src/execute.rs @@ -1,156 +1,161 @@ -//! Ethereum block executor. +//! Ethereum block execution strategy. use crate::{ dao_fork::{DAO_HARDFORK_BENEFICIARY, DAO_HARDKFORK_ACCOUNTS}, EthEvmConfig, }; use alloc::{boxed::Box, sync::Arc, vec, vec::Vec}; -use alloy_primitives::{BlockNumber, U256}; +use alloy_consensus::Transaction as _; +use alloy_eips::eip7685::Requests; use core::fmt::Display; -use reth_chainspec::{ChainSpec, EthereumHardforks, MAINNET}; +use reth_chainspec::{ChainSpec, EthereumHardfork, EthereumHardforks, MAINNET}; +use reth_consensus::ConsensusError; use reth_ethereum_consensus::validate_block_post_execution; use reth_evm::{ execute::{ - BatchExecutor, BlockExecutionError, BlockExecutionInput, BlockExecutionOutput, - BlockExecutorProvider, BlockValidationError, Executor, ProviderError, + BasicBlockExecutorProvider, BlockExecutionError, BlockExecutionStrategy, + BlockExecutionStrategyFactory, BlockValidationError, ExecuteOutput, ProviderError, }, - system_calls::{NoopHook, OnStateHook, SystemCaller}, - ConfigureEvm, -}; -use reth_execution_types::ExecutionOutcome; -use reth_primitives::{BlockWithSenders, EthereumHardfork, Header, Receipt, Request}; -use reth_prune_types::PruneModes; -use reth_revm::{ - batch::BlockBatchRecord, - db::{states::bundle_state::BundleRetention, State}, state_change::post_block_balance_increments, - Evm, + system_calls::{OnStateHook, SystemCaller}, + ConfigureEvm, }; +use reth_primitives::{BlockWithSenders, Receipt}; +use reth_revm::db::{states::bundle_state::BundleRetention, BundleState, State}; use revm_primitives::{ db::{Database, DatabaseCommit}, - BlockEnv, CfgEnvWithHandlerCfg, EnvWithHandlerCfg, ResultAndState, + BlockEnv, CfgEnvWithHandlerCfg, EnvWithHandlerCfg, ResultAndState, U256, }; -/// Provides executors to execute regular ethereum blocks +/// Factory for [`EthExecutionStrategy`]. #[derive(Debug, Clone)] -pub struct EthExecutorProvider { +pub struct EthExecutionStrategyFactory { + /// The chainspec chain_spec: Arc, + /// How to create an EVM. evm_config: EvmConfig, } -impl EthExecutorProvider { - /// Creates a new default ethereum executor provider. +impl EthExecutionStrategyFactory { + /// Creates a new default ethereum executor strategy factory. pub fn ethereum(chain_spec: Arc) -> Self { Self::new(chain_spec.clone(), EthEvmConfig::new(chain_spec)) } - /// Returns a new provider for the mainnet. + /// Returns a new factory for the mainnet. pub fn mainnet() -> Self { Self::ethereum(MAINNET.clone()) } } -impl EthExecutorProvider { - /// Creates a new executor provider. +impl EthExecutionStrategyFactory { + /// Creates a new executor strategy factory. pub const fn new(chain_spec: Arc, evm_config: EvmConfig) -> Self { Self { chain_spec, evm_config } } } -impl EthExecutorProvider -where - EvmConfig: ConfigureEvm
, -{ - fn eth_executor(&self, db: DB) -> EthBlockExecutor - where - DB: Database>, - { - EthBlockExecutor::new( - self.chain_spec.clone(), - self.evm_config.clone(), - State::builder().with_database(db).with_bundle_update().without_state_clear().build(), - ) - } -} - -impl BlockExecutorProvider for EthExecutorProvider +impl BlockExecutionStrategyFactory for EthExecutionStrategyFactory where - EvmConfig: ConfigureEvm
, + EvmConfig: + Clone + Unpin + Sync + Send + 'static + ConfigureEvm
, { - type Executor + Display>> = - EthBlockExecutor; - - type BatchExecutor + Display>> = - EthBatchExecutor; - - fn executor(&self, db: DB) -> Self::Executor - where - DB: Database + Display>, - { - self.eth_executor(db) - } + type Strategy + Display>> = + EthExecutionStrategy; - fn batch_executor(&self, db: DB) -> Self::BatchExecutor + fn create_strategy(&self, db: DB) -> Self::Strategy where DB: Database + Display>, { - let executor = self.eth_executor(db); - EthBatchExecutor { executor, batch_record: BlockBatchRecord::default() } + let state = + State::builder().with_database(db).with_bundle_update().without_state_clear().build(); + EthExecutionStrategy::new(state, self.chain_spec.clone(), self.evm_config.clone()) } } -/// Helper type for the output of executing a block. -#[derive(Debug, Clone)] -struct EthExecuteOutput { - receipts: Vec, - requests: Vec, - gas_used: u64, -} - -/// Helper container type for EVM with chain spec. -#[derive(Debug, Clone)] -struct EthEvmExecutor { +/// Block execution strategy for Ethereum. +#[allow(missing_debug_implementations)] +pub struct EthExecutionStrategy +where + EvmConfig: Clone, +{ /// The chainspec chain_spec: Arc, /// How to create an EVM. evm_config: EvmConfig, + /// Current state for block execution. + state: State, + /// Utility to call system smart contracts. + system_caller: SystemCaller, } -impl EthEvmExecutor +impl EthExecutionStrategy where - EvmConfig: ConfigureEvm
, + EvmConfig: Clone, { - /// Executes the transactions in the block and returns the receipts of the transactions in the - /// block, the total gas used and the list of EIP-7685 [requests](Request). - /// - /// 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. + /// Creates a new [`EthExecutionStrategy`] + pub fn new(state: State, chain_spec: Arc, evm_config: EvmConfig) -> Self { + let system_caller = SystemCaller::new(evm_config.clone(), (*chain_spec).clone()); + Self { state, chain_spec, evm_config, system_caller } + } +} + +impl EthExecutionStrategy +where + DB: Database + Display>, + EvmConfig: ConfigureEvm
, +{ + /// Configures a new evm configuration and block environment for the given block. /// - /// # Note + /// # Caution /// - /// It does __not__ apply post-execution changes that do not require an [EVM](Evm), for that see - /// [`EthBlockExecutor::post_execution`]. - fn execute_state_transitions( + /// This does not initialize the tx environment. + fn evm_env_for_block( &self, + header: &alloy_consensus::Header, + total_difficulty: U256, + ) -> EnvWithHandlerCfg { + let mut cfg = CfgEnvWithHandlerCfg::new(Default::default(), Default::default()); + let mut block_env = BlockEnv::default(); + self.evm_config.fill_cfg_and_block_env(&mut cfg, &mut block_env, header, total_difficulty); + + EnvWithHandlerCfg::new_with_cfg_env(cfg, block_env, Default::default()) + } +} + +impl BlockExecutionStrategy for EthExecutionStrategy +where + DB: Database + Display>, + EvmConfig: ConfigureEvm
, +{ + type Error = BlockExecutionError; + + fn apply_pre_execution_changes( + &mut self, block: &BlockWithSenders, - mut evm: Evm<'_, Ext, &mut State>, - state_hook: Option, - ) -> Result - where - DB: Database, - DB::Error: Into + Display, - F: OnStateHook + 'static, - { - let mut system_caller = SystemCaller::new(self.evm_config.clone(), &self.chain_spec); - if let Some(hook) = state_hook { - system_caller.with_state_hook(Some(Box::new(hook) as Box)); - } + total_difficulty: U256, + ) -> Result<(), Self::Error> { + // Set state clear flag if the block is after the Spurious Dragon hardfork. + let state_clear_flag = + (*self.chain_spec).is_spurious_dragon_active_at_block(block.header.number); + self.state.set_state_clear_flag(state_clear_flag); + + let env = self.evm_env_for_block(&block.header, total_difficulty); + let mut evm = self.evm_config.evm_with_env(&mut self.state, env); + + self.system_caller.apply_pre_execution_changes(block, &mut evm)?; - system_caller.apply_pre_execution_changes(block, &mut evm)?; + Ok(()) + } + + fn execute_transactions( + &mut self, + block: &BlockWithSenders, + total_difficulty: U256, + ) -> Result { + let env = self.evm_env_for_block(&block.header, total_difficulty); + let mut evm = self.evm_config.evm_with_env(&mut self.state, env); - // execute transactions let mut cumulative_gas_used = 0; let mut receipts = Vec::with_capacity(block.body.transactions.len()); for (sender, transaction) in block.transactions_with_sender() { @@ -176,7 +181,7 @@ where error: Box::new(new_err), } })?; - system_caller.on_state(&result_and_state); + self.system_caller.on_state(&result_and_state); let ResultAndState { result, state } = result_and_state; evm.db_mut().commit(state); @@ -198,137 +203,36 @@ where }, ); } - - let requests = if self.chain_spec.is_prague_active_at_timestamp(block.timestamp) { - // Collect all EIP-6110 deposits - let deposit_requests = - crate::eip6110::parse_deposits_from_receipts(&self.chain_spec, &receipts)?; - - let post_execution_requests = system_caller.apply_post_execution_changes(&mut evm)?; - - [deposit_requests, post_execution_requests].concat() - } else { - vec![] - }; - - Ok(EthExecuteOutput { receipts, requests, gas_used: cumulative_gas_used }) - } -} - -/// A basic Ethereum block executor. -/// -/// Expected usage: -/// - Create a new instance of the executor. -/// - Execute the block. -#[derive(Debug)] -pub struct EthBlockExecutor { - /// Chain specific evm config that's used to execute a block. - executor: EthEvmExecutor, - /// The state to use for execution - state: State, -} - -impl EthBlockExecutor { - /// Creates a new Ethereum block executor. - pub const fn new(chain_spec: Arc, evm_config: EvmConfig, state: State) -> Self { - Self { executor: EthEvmExecutor { chain_spec, evm_config }, state } - } - - #[inline] - fn chain_spec(&self) -> &ChainSpec { - &self.executor.chain_spec - } - - /// Returns mutable reference to the state that wraps the underlying database. - #[allow(unused)] - fn state_mut(&mut self) -> &mut State { - &mut self.state - } -} - -impl EthBlockExecutor -where - EvmConfig: ConfigureEvm
, - DB: Database + Display>, -{ - /// Configures a new evm configuration and block environment for the given block. - /// - /// # Caution - /// - /// This does not initialize the tx environment. - fn evm_env_for_block(&self, header: &Header, total_difficulty: U256) -> EnvWithHandlerCfg { - let mut cfg = CfgEnvWithHandlerCfg::new(Default::default(), Default::default()); - let mut block_env = BlockEnv::default(); - self.executor.evm_config.fill_cfg_and_block_env( - &mut cfg, - &mut block_env, - header, - total_difficulty, - ); - - 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::) + Ok(ExecuteOutput { receipts, gas_used: cumulative_gas_used }) } - /// 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_with_state_hook( + fn apply_post_execution_changes( &mut self, block: &BlockWithSenders, total_difficulty: U256, - state_hook: Option, - ) -> Result - where - F: OnStateHook + 'static, - { - // 1. prepare state on new block - self.on_new_block(&block.header); - - // 2. configure the evm and execute + receipts: &[Receipt], + ) -> Result { 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, state_hook) - }?; + let mut evm = self.evm_config.evm_with_env(&mut self.state, env); - // 3. apply post execution changes - self.post_execution(block, total_difficulty)?; - - Ok(output) - } + let requests = if self.chain_spec.is_prague_active_at_timestamp(block.timestamp) { + // Collect all EIP-6110 deposits + let deposit_requests = + crate::eip6110::parse_deposits_from_receipts(&self.chain_spec, receipts)?; - /// Apply settings before a new block is executed. - pub(crate) fn on_new_block(&mut self, header: &Header) { - // Set state clear flag if the block is after the Spurious Dragon hardfork. - let state_clear_flag = self.chain_spec().is_spurious_dragon_active_at_block(header.number); - self.state.set_state_clear_flag(state_clear_flag); - } + let mut requests = Requests::new(vec![deposit_requests]); + requests.extend(self.system_caller.apply_post_execution_changes(&mut evm)?); + requests + } else { + Requests::default() + }; + drop(evm); - /// Apply post execution state changes that do not require an [EVM](Evm), such as: block - /// rewards, withdrawals, and irregular DAO hardfork state change - pub fn post_execution( - &mut self, - block: &BlockWithSenders, - total_difficulty: U256, - ) -> Result<(), BlockExecutionError> { let mut balance_increments = - post_block_balance_increments(self.chain_spec(), block, total_difficulty); + post_block_balance_increments(&self.chain_spec, block, total_difficulty); // Irregular state change at Ethereum DAO hardfork - if self.chain_spec().fork(EthereumHardfork::Dao).transitions_at_block(block.number) { + if self.chain_spec.fork(EthereumHardfork::Dao).transitions_at_block(block.number) { // drain balances from hardcoded addresses. let drained_balance: u128 = self .state @@ -345,165 +249,73 @@ where .increment_balances(balance_increments) .map_err(|_| BlockValidationError::IncrementBalanceFailed)?; - Ok(()) + Ok(requests) } -} -impl Executor for EthBlockExecutor -where - EvmConfig: ConfigureEvm
, - DB: Database + Display>, -{ - type Input<'a> = BlockExecutionInput<'a, BlockWithSenders>; - type Output = BlockExecutionOutput; - type Error = BlockExecutionError; - - /// Executes the block and commits the changes to the internal state. - /// - /// Returns the receipts of the transactions in the block. - /// - /// Returns an error if the block could not be executed or failed verification. - fn execute(mut self, input: Self::Input<'_>) -> Result { - let BlockExecutionInput { block, total_difficulty } = input; - let EthExecuteOutput { receipts, requests, gas_used } = - self.execute_without_verification(block, total_difficulty)?; - - // 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 }) + fn state_ref(&self) -> &State { + &self.state } - fn execute_with_state_closure( - mut self, - input: Self::Input<'_>, - mut witness: F, - ) -> Result - where - F: FnMut(&State), - { - let BlockExecutionInput { block, total_difficulty } = input; - let EthExecuteOutput { receipts, requests, gas_used } = - self.execute_without_verification(block, total_difficulty)?; + fn state_mut(&mut self) -> &mut State { + &mut self.state + } - // NOTE: we need to merge keep the reverts for the bundle retention - self.state.merge_transitions(BundleRetention::Reverts); - witness(&self.state); - Ok(BlockExecutionOutput { state: self.state.take_bundle(), receipts, requests, gas_used }) + fn with_state_hook(&mut self, hook: Option>) { + self.system_caller.with_state_hook(hook); } - fn execute_with_state_hook( - mut self, - input: Self::Input<'_>, - state_hook: F, - ) -> Result - where - F: OnStateHook + 'static, - { - 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 + fn finish(&mut self) -> BundleState { self.state.merge_transitions(BundleRetention::Reverts); - Ok(BlockExecutionOutput { state: self.state.take_bundle(), receipts, requests, gas_used }) + self.state.take_bundle() } -} -/// An executor for a batch of blocks. -/// -/// State changes are tracked until the executor is finalized. -#[derive(Debug)] -pub struct EthBatchExecutor { - /// The executor used to execute single blocks - /// - /// All state changes are committed to the [State]. - executor: EthBlockExecutor, - /// Keeps track of the batch and records receipts based on the configured prune mode - batch_record: BlockBatchRecord, -} -impl EthBatchExecutor { - /// Returns mutable reference to the state that wraps the underlying database. - #[allow(unused)] - fn state_mut(&mut self) -> &mut State { - self.executor.state_mut() + fn validate_block_post_execution( + &self, + block: &BlockWithSenders, + receipts: &[Receipt], + requests: &Requests, + ) -> Result<(), ConsensusError> { + validate_block_post_execution(block, &self.chain_spec.clone(), receipts, requests) } } -impl BatchExecutor for EthBatchExecutor -where - EvmConfig: ConfigureEvm
, - DB: Database + Display>, -{ - type Input<'a> = BlockExecutionInput<'a, BlockWithSenders>; - type Output = ExecutionOutcome; - type Error = BlockExecutionError; - - fn execute_and_verify_one(&mut self, input: Self::Input<'_>) -> Result<(), Self::Error> { - let BlockExecutionInput { block, total_difficulty } = input; - - if self.batch_record.first_block().is_none() { - self.batch_record.set_first_block(block.number); - } - - let EthExecuteOutput { receipts, requests, gas_used: _ } = - self.executor.execute_without_verification(block, total_difficulty)?; - - validate_block_post_execution(block, self.executor.chain_spec(), &receipts, &requests)?; - - // prepare the state according to the prune mode - let retention = self.batch_record.bundle_retention(block.number); - self.executor.state.merge_transitions(retention); - - // store receipts in the set - self.batch_record.save_receipts(receipts)?; - - // store requests in the set - self.batch_record.save_requests(requests); - - Ok(()) - } - - fn finalize(mut self) -> Self::Output { - ExecutionOutcome::new( - self.executor.state.take_bundle(), - self.batch_record.take_receipts(), - self.batch_record.first_block().unwrap_or_default(), - self.batch_record.take_requests(), - ) - } - - fn set_tip(&mut self, tip: BlockNumber) { - self.batch_record.set_tip(tip); - } +/// Helper type with backwards compatible methods to obtain Ethereum executor +/// providers. +#[derive(Debug)] +pub struct EthExecutorProvider; - fn set_prune_modes(&mut self, prune_modes: PruneModes) { - self.batch_record.set_prune_modes(prune_modes); +impl EthExecutorProvider { + /// Creates a new default ethereum executor provider. + pub fn ethereum( + chain_spec: Arc, + ) -> BasicBlockExecutorProvider { + BasicBlockExecutorProvider::new(EthExecutionStrategyFactory::ethereum(chain_spec)) } - fn size_hint(&self) -> Option { - Some(self.executor.state.bundle_state.size_hint()) + /// Returns a new provider for the mainnet. + pub fn mainnet() -> BasicBlockExecutorProvider { + BasicBlockExecutorProvider::new(EthExecutionStrategyFactory::mainnet()) } } #[cfg(test)] mod tests { use super::*; - use alloy_consensus::TxLegacy; + use alloy_consensus::{Header, TxLegacy}; use alloy_eips::{ eip2935::{HISTORY_STORAGE_ADDRESS, HISTORY_STORAGE_CODE}, eip4788::{BEACON_ROOTS_ADDRESS, BEACON_ROOTS_CODE, SYSTEM_ADDRESS}, eip7002::{WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS, WITHDRAWAL_REQUEST_PREDEPLOY_CODE}, + eip7685::EMPTY_REQUESTS_HASH, }; use alloy_primitives::{b256, fixed_bytes, keccak256, Bytes, TxKind, B256}; use reth_chainspec::{ChainSpecBuilder, ForkCondition}; + use reth_evm::execute::{ + BasicBlockExecutorProvider, BatchExecutor, BlockExecutorProvider, Executor, + }; + use reth_execution_types::BlockExecutionOutput; use reth_primitives::{ - constants::{EMPTY_ROOT_HASH, ETH_TO_WEI}, - public_key_to_address, Account, Block, BlockBody, Transaction, + constants::ETH_TO_WEI, public_key_to_address, Account, Block, BlockBody, Transaction, }; use reth_revm::{ database::StateProviderDatabase, test_utils::StateProviderTest, TransitionState, @@ -551,8 +363,13 @@ mod tests { db } - fn executor_provider(chain_spec: Arc) -> EthExecutorProvider { - EthExecutorProvider { evm_config: EthEvmConfig::new(chain_spec.clone()), chain_spec } + fn executor_provider( + chain_spec: Arc, + ) -> BasicBlockExecutorProvider { + let strategy_factory = + EthExecutionStrategyFactory::new(chain_spec.clone(), EthEvmConfig::new(chain_spec)); + + BasicBlockExecutorProvider::new(strategy_factory) } #[test] @@ -571,10 +388,11 @@ mod tests { let provider = executor_provider(chain_spec); + let mut executor = provider.batch_executor(StateProviderDatabase::new(&db)); + // attempt to execute a block without parent beacon block root, expect err - let err = provider - .executor(StateProviderDatabase::new(&db)) - .execute( + let err = executor + .execute_and_verify_one( ( &BlockWithSenders { block: Block { @@ -583,7 +401,6 @@ mod tests { transactions: vec![], ommers: vec![], withdrawals: None, - requests: None, }, }, senders: vec![], @@ -604,24 +421,24 @@ mod tests { // fix header, set a gas limit header.parent_beacon_block_root = Some(B256::with_last_byte(0x69)); - let mut executor = provider.executor(StateProviderDatabase::new(&db)); - // Now execute a block with the fixed header, ensure that it does not fail executor - .execute_without_verification( - &BlockWithSenders { - block: Block { - header: header.clone(), - body: BlockBody { - transactions: vec![], - ommers: vec![], - withdrawals: None, - requests: None, + .execute_and_verify_one( + ( + &BlockWithSenders { + block: Block { + header: header.clone(), + body: BlockBody { + transactions: vec![], + ommers: vec![], + withdrawals: None, + }, }, + senders: vec![], }, - senders: vec![], - }, - U256::ZERO, + U256::ZERO, + ) + .into(), ) .unwrap(); @@ -635,16 +452,17 @@ mod tests { let parent_beacon_block_root_index = timestamp_index % history_buffer_length + history_buffer_length; - // get timestamp storage and compare - let timestamp_storage = - executor.state.storage(BEACON_ROOTS_ADDRESS, U256::from(timestamp_index)).unwrap(); + let timestamp_storage = executor.with_state_mut(|state| { + state.storage(BEACON_ROOTS_ADDRESS, U256::from(timestamp_index)).unwrap() + }); assert_eq!(timestamp_storage, U256::from(header.timestamp)); // get parent beacon block root storage and compare - let parent_beacon_block_root_storage = executor - .state - .storage(BEACON_ROOTS_ADDRESS, U256::from(parent_beacon_block_root_index)) - .expect("storage value should exist"); + let parent_beacon_block_root_storage = executor.with_state_mut(|state| { + state + .storage(BEACON_ROOTS_ADDRESS, U256::from(parent_beacon_block_root_index)) + .expect("storage value should exist") + }); assert_eq!(parent_beacon_block_root_storage, U256::from(0x69)); } @@ -684,7 +502,6 @@ mod tests { transactions: vec![], ommers: vec![], withdrawals: None, - requests: None, }, }, senders: vec![], @@ -739,7 +556,6 @@ mod tests { transactions: vec![], ommers: vec![], withdrawals: None, - requests: None, }, }, senders: vec![], @@ -753,7 +569,8 @@ mod tests { ); // ensure that the nonce of the system address account has not changed - let nonce = executor.state_mut().basic(SYSTEM_ADDRESS).unwrap().unwrap().nonce; + let nonce = + executor.with_state_mut(|state| state.basic(SYSTEM_ADDRESS).unwrap().unwrap().nonce); assert_eq!(nonce, 0); } @@ -811,11 +628,12 @@ mod tests { // there is no system contract call so there should be NO STORAGE CHANGES // this means we'll check the transition state - let transition_state = executor - .state_mut() - .transition_state - .take() - .expect("the evm should be initialized with bundle updates"); + let transition_state = executor.with_state_mut(|state| { + state + .transition_state + .take() + .expect("the evm should be initialized with bundle updates") + }); // assert that it is the default (empty) transition state assert_eq!(transition_state, TransitionState::default()); @@ -873,17 +691,15 @@ mod tests { timestamp_index % history_buffer_length + history_buffer_length; // get timestamp storage and compare - let timestamp_storage = executor - .state_mut() - .storage(BEACON_ROOTS_ADDRESS, U256::from(timestamp_index)) - .unwrap(); + let timestamp_storage = executor.with_state_mut(|state| { + state.storage(BEACON_ROOTS_ADDRESS, U256::from(timestamp_index)).unwrap() + }); assert_eq!(timestamp_storage, U256::from(header.timestamp)); // get parent beacon block root storage and compare - let parent_beacon_block_root_storage = executor - .state_mut() - .storage(BEACON_ROOTS_ADDRESS, U256::from(parent_beacon_block_root_index)) - .unwrap(); + let parent_beacon_block_root_storage = executor.with_state_mut(|state| { + state.storage(BEACON_ROOTS_ADDRESS, U256::from(parent_beacon_block_root_index)).unwrap() + }); assert_eq!(parent_beacon_block_root_storage, U256::from(0x69)); } @@ -909,7 +725,6 @@ mod tests { db } - #[test] fn eip_2935_pre_fork() { let db = create_state_provider_with_block_hashes(1); @@ -948,12 +763,11 @@ mod tests { // // we load the account first, because revm expects it to be // loaded - executor.state_mut().basic(HISTORY_STORAGE_ADDRESS).unwrap(); - assert!(executor - .state_mut() + executor.with_state_mut(|state| state.basic(HISTORY_STORAGE_ADDRESS).unwrap()); + assert!(executor.with_state_mut(|state| state .storage(HISTORY_STORAGE_ADDRESS, U256::ZERO) .unwrap() - .is_zero()); + .is_zero())); } #[test] @@ -992,12 +806,11 @@ mod tests { // // we load the account first, because revm expects it to be // loaded - executor.state_mut().basic(HISTORY_STORAGE_ADDRESS).unwrap(); - assert!(executor - .state_mut() + executor.with_state_mut(|state| state.basic(HISTORY_STORAGE_ADDRESS).unwrap()); + assert!(executor.with_state_mut(|state| state .storage(HISTORY_STORAGE_ADDRESS, U256::ZERO) .unwrap() - .is_zero()); + .is_zero())); } #[test] @@ -1016,7 +829,7 @@ mod tests { parent_hash: B256::random(), timestamp: 1, number: fork_activation_block, - requests_root: Some(EMPTY_ROOT_HASH), + requests_hash: Some(EMPTY_REQUESTS_HASH), ..Header::default() }; let provider = executor_provider(chain_spec); @@ -1039,21 +852,20 @@ mod tests { ); // the hash for the ancestor of the fork activation block should be present - assert!(executor.state_mut().basic(HISTORY_STORAGE_ADDRESS).unwrap().is_some()); + assert!(executor + .with_state_mut(|state| state.basic(HISTORY_STORAGE_ADDRESS).unwrap().is_some())); assert_ne!( - executor - .state_mut() + executor.with_state_mut(|state| state .storage(HISTORY_STORAGE_ADDRESS, U256::from(fork_activation_block - 1)) - .unwrap(), + .unwrap()), U256::ZERO ); // the hash of the block itself should not be in storage - assert!(executor - .state_mut() + assert!(executor.with_state_mut(|state| state .storage(HISTORY_STORAGE_ADDRESS, U256::from(fork_activation_block)) .unwrap() - .is_zero()); + .is_zero())); } #[test] @@ -1075,7 +887,7 @@ mod tests { parent_hash: B256::random(), timestamp: 1, number: fork_activation_block, - requests_root: Some(EMPTY_ROOT_HASH), + requests_hash: Some(EMPTY_REQUESTS_HASH), ..Header::default() }; @@ -1096,15 +908,15 @@ mod tests { ); // the hash for the ancestor of the fork activation block should be present - assert!(executor.state_mut().basic(HISTORY_STORAGE_ADDRESS).unwrap().is_some()); + assert!(executor + .with_state_mut(|state| state.basic(HISTORY_STORAGE_ADDRESS).unwrap().is_some())); assert_ne!( - executor - .state_mut() + executor.with_state_mut(|state| state .storage( HISTORY_STORAGE_ADDRESS, U256::from(fork_activation_block % BLOCKHASH_SERVE_WINDOW as u64 - 1) ) - .unwrap(), + .unwrap()), U256::ZERO ); } @@ -1121,7 +933,7 @@ mod tests { ); let mut header = chain_spec.genesis_header().clone(); - header.requests_root = Some(EMPTY_ROOT_HASH); + header.requests_hash = Some(EMPTY_REQUESTS_HASH); let header_hash = header.hash_slow(); let provider = executor_provider(chain_spec); @@ -1147,19 +959,18 @@ mod tests { // // we load the account first, because revm expects it to be // loaded - executor.state_mut().basic(HISTORY_STORAGE_ADDRESS).unwrap(); - assert!(executor - .state_mut() + executor.with_state_mut(|state| state.basic(HISTORY_STORAGE_ADDRESS).unwrap()); + assert!(executor.with_state_mut(|state| state .storage(HISTORY_STORAGE_ADDRESS, U256::ZERO) .unwrap() - .is_zero()); + .is_zero())); // attempt to execute block 1, this should not fail let header = Header { parent_hash: header_hash, timestamp: 1, number: 1, - requests_root: Some(EMPTY_ROOT_HASH), + requests_hash: Some(EMPTY_REQUESTS_HASH), ..Header::default() }; let header_hash = header.hash_slow(); @@ -1180,23 +991,25 @@ mod tests { ); // the block hash of genesis should now be in storage, but not block 1 - assert!(executor.state_mut().basic(HISTORY_STORAGE_ADDRESS).unwrap().is_some()); + assert!(executor + .with_state_mut(|state| state.basic(HISTORY_STORAGE_ADDRESS).unwrap().is_some())); assert_ne!( - executor.state_mut().storage(HISTORY_STORAGE_ADDRESS, U256::ZERO).unwrap(), + executor.with_state_mut(|state| state + .storage(HISTORY_STORAGE_ADDRESS, U256::ZERO) + .unwrap()), U256::ZERO ); - assert!(executor - .state_mut() + assert!(executor.with_state_mut(|state| state .storage(HISTORY_STORAGE_ADDRESS, U256::from(1)) .unwrap() - .is_zero()); + .is_zero())); // attempt to execute block 2, this should not fail let header = Header { parent_hash: header_hash, timestamp: 1, number: 2, - requests_root: Some(EMPTY_ROOT_HASH), + requests_hash: Some(EMPTY_REQUESTS_HASH), ..Header::default() }; @@ -1216,20 +1029,24 @@ mod tests { ); // the block hash of genesis and block 1 should now be in storage, but not block 2 - assert!(executor.state_mut().basic(HISTORY_STORAGE_ADDRESS).unwrap().is_some()); + assert!(executor + .with_state_mut(|state| state.basic(HISTORY_STORAGE_ADDRESS).unwrap().is_some())); assert_ne!( - executor.state_mut().storage(HISTORY_STORAGE_ADDRESS, U256::ZERO).unwrap(), + executor.with_state_mut(|state| state + .storage(HISTORY_STORAGE_ADDRESS, U256::ZERO) + .unwrap()), U256::ZERO ); assert_ne!( - executor.state_mut().storage(HISTORY_STORAGE_ADDRESS, U256::from(1)).unwrap(), + executor.with_state_mut(|state| state + .storage(HISTORY_STORAGE_ADDRESS, U256::from(1)) + .unwrap()), U256::ZERO ); - assert!(executor - .state_mut() + assert!(executor.with_state_mut(|state| state .storage(HISTORY_STORAGE_ADDRESS, U256::from(2)) .unwrap() - .is_zero()); + .is_zero())); } #[test] @@ -1254,15 +1071,16 @@ mod tests { HashMap::default(), ); - // https://github.com/lightclient/7002asm/blob/e0d68e04d15f25057af7b6d180423d94b6b3bdb3/test/Contract.t.sol.in#L49-L64 + // https://github.com/lightclient/sys-asm/blob/9282bdb9fd64e024e27f60f507486ffb2183cba2/test/Withdrawal.t.sol.in#L36 let validator_public_key = fixed_bytes!("111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"); - let withdrawal_amount = fixed_bytes!("2222222222222222"); + let withdrawal_amount = fixed_bytes!("0203040506070809"); let input: Bytes = [&validator_public_key[..], &withdrawal_amount[..]].concat().into(); assert_eq!(input.len(), 56); let mut header = chain_spec.genesis_header().clone(); header.gas_limit = 1_500_000; - header.gas_used = 134_807; + // measured + header.gas_used = 135_856; header.receipts_root = b256!("b31a3e47b902e9211c4d349af4e4c5604ce388471e79ca008907ae4616bb0ed3"); @@ -1272,10 +1090,10 @@ mod tests { chain_id: Some(chain_spec.chain.id()), nonce: 1, gas_price: header.base_fee_per_gas.unwrap().into(), - gas_limit: 134_807, + gas_limit: header.gas_used, to: TxKind::Call(WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS), // `MIN_WITHDRAWAL_REQUEST_FEE` - value: U256::from(1), + value: U256::from(2), input, }), ); @@ -1302,11 +1120,9 @@ mod tests { let receipt = receipts.first().unwrap(); assert!(receipt.success); - let request = requests.first().unwrap(); - let withdrawal_request = request.as_withdrawal_request().unwrap(); - assert_eq!(withdrawal_request.source_address, sender_address); - assert_eq!(withdrawal_request.validator_pubkey, validator_public_key); - assert_eq!(withdrawal_request.amount, u64::from_be_bytes(withdrawal_amount.into())); + assert!(requests[0].is_empty(), "there should be no deposits"); + assert!(!requests[1].is_empty(), "there should be a withdrawal"); + assert!(requests[2].is_empty(), "there should be no consolidations"); } #[test] diff --git a/crates/ethereum/evm/src/lib.rs b/crates/ethereum/evm/src/lib.rs index cede8008b3cf..9abb11976363 100644 --- a/crates/ethereum/evm/src/lib.rs +++ b/crates/ethereum/evm/src/lib.rs @@ -17,7 +17,7 @@ extern crate alloc; -use alloc::vec::Vec; +use alloc::{sync::Arc, vec::Vec}; use alloy_primitives::{Address, Bytes, TxKind, U256}; use reth_chainspec::{ChainSpec, Head}; use reth_evm::{ConfigureEvm, ConfigureEvmEnv, NextBlockEnvAttributes}; @@ -25,7 +25,6 @@ use reth_primitives::{transaction::FillTxEnv, Header, TransactionSigned}; use revm_primitives::{ AnalysisKind, BlobExcessGasAndPrice, BlockEnv, CfgEnv, CfgEnvWithHandlerCfg, Env, SpecId, TxEnv, }; -use std::sync::Arc; mod config; pub use config::{revm_spec, revm_spec_by_timestamp_after_merge}; @@ -193,13 +192,14 @@ impl ConfigureEvm for EthEvmConfig { #[cfg(test)] mod tests { use super::*; + use alloy_consensus::constants::KECCAK_EMPTY; use alloy_genesis::Genesis; use alloy_primitives::{B256, U256}; use reth_chainspec::{Chain, ChainSpec, MAINNET}; use reth_evm::execute::ProviderError; use reth_primitives::{ revm_primitives::{BlockEnv, CfgEnv, SpecId}, - Header, KECCAK_EMPTY, + Header, }; use reth_revm::{ db::{CacheDB, EmptyDBTyped}, diff --git a/crates/ethereum/node/Cargo.toml b/crates/ethereum/node/Cargo.toml index 7a323f91d877..11555cdc4a53 100644 --- a/crates/ethereum/node/Cargo.toml +++ b/crates/ethereum/node/Cargo.toml @@ -21,6 +21,7 @@ reth-tracing.workspace = true reth-provider.workspace = true reth-transaction-pool.workspace = true reth-network.workspace = true +reth-evm.workspace = true reth-evm-ethereum.workspace = true reth-consensus.workspace = true reth-auto-seal-consensus.workspace = true @@ -43,16 +44,28 @@ reth-chainspec.workspace = true reth-db.workspace = true reth-exex.workspace = true reth-node-api.workspace = true -reth-node-core.workspace = true reth-e2e-test-utils.workspace = true reth-tasks.workspace = true futures.workspace = true alloy-primitives.workspace = true alloy-genesis.workspace = true tokio.workspace = true -futures-util.workspace = true serde_json.workspace = true +alloy-consensus.workspace = true [features] default = [] -test-utils = ["reth-node-builder/test-utils"] +test-utils = [ + "reth-node-builder/test-utils", + "reth-chainspec/test-utils", + "reth-consensus/test-utils", + "reth-network/test-utils", + "reth-payload-builder/test-utils", + "reth-primitives/test-utils", + "reth-revm/test-utils", + "reth-db/test-utils", + "reth-provider/test-utils", + "reth-transaction-pool/test-utils", + "revm/test-utils", + "reth-evm/test-utils" +] diff --git a/crates/ethereum/node/src/evm.rs b/crates/ethereum/node/src/evm.rs index d710d8d8d452..bcdcaac6bfa2 100644 --- a/crates/ethereum/node/src/evm.rs +++ b/crates/ethereum/node/src/evm.rs @@ -1,6 +1,8 @@ //! Ethereum EVM support #[doc(inline)] -pub use reth_evm_ethereum::execute::EthExecutorProvider; +pub use reth_evm::execute::BasicBlockExecutorProvider; +#[doc(inline)] +pub use reth_evm_ethereum::execute::{EthExecutionStrategyFactory, EthExecutorProvider}; #[doc(inline)] pub use reth_evm_ethereum::EthEvmConfig; diff --git a/crates/ethereum/node/src/lib.rs b/crates/ethereum/node/src/lib.rs index 37ebc33c22b8..421cee37fb03 100644 --- a/crates/ethereum/node/src/lib.rs +++ b/crates/ethereum/node/src/lib.rs @@ -14,7 +14,9 @@ use revm as _; pub use reth_ethereum_engine_primitives::EthEngineTypes; pub mod evm; -pub use evm::{EthEvmConfig, EthExecutorProvider}; +pub use evm::{ + BasicBlockExecutorProvider, EthEvmConfig, EthExecutionStrategyFactory, EthExecutorProvider, +}; pub mod node; pub use node::EthereumNode; diff --git a/crates/ethereum/node/src/node.rs b/crates/ethereum/node/src/node.rs index f17658bb32da..3df46b4856f6 100644 --- a/crates/ethereum/node/src/node.rs +++ b/crates/ethereum/node/src/node.rs @@ -9,19 +9,24 @@ use reth_chainspec::ChainSpec; use reth_ethereum_engine_primitives::{ EthBuiltPayload, EthPayloadAttributes, EthPayloadBuilderAttributes, EthereumEngineValidator, }; -use reth_evm_ethereum::execute::EthExecutorProvider; +use reth_evm::execute::BasicBlockExecutorProvider; +use reth_evm_ethereum::execute::EthExecutionStrategyFactory; use reth_network::NetworkHandle; -use reth_node_api::{ConfigureEvm, EngineValidator, FullNodeComponents, NodeAddOns}; +use reth_node_api::{ + AddOnsContext, ConfigureEvm, EngineValidator, FullNodeComponents, NodePrimitives, + NodeTypesWithDB, +}; use reth_node_builder::{ components::{ - ComponentsBuilder, ConsensusBuilder, EngineValidatorBuilder, ExecutorBuilder, - NetworkBuilder, PayloadServiceBuilder, PoolBuilder, + ComponentsBuilder, ConsensusBuilder, ExecutorBuilder, NetworkBuilder, + PayloadServiceBuilder, PoolBuilder, }, node::{FullNodeTypes, NodeTypes, NodeTypesWithEngine}, - BuilderContext, Node, PayloadBuilderConfig, PayloadTypes, + rpc::{EngineValidatorBuilder, RpcAddOns}, + BuilderContext, Node, NodeAdapter, NodeComponentsBuilder, PayloadBuilderConfig, PayloadTypes, }; use reth_payload_builder::{PayloadBuilderHandle, PayloadBuilderService}; -use reth_primitives::Header; +use reth_primitives::{Block, Header}; use reth_provider::CanonStateSubscriptions; use reth_rpc::EthApi; use reth_tracing::tracing::{debug, info}; @@ -32,6 +37,14 @@ use reth_transaction_pool::{ use crate::{EthEngineTypes, EthEvmConfig}; +/// Ethereum primitive types. +#[derive(Debug)] +pub struct EthPrimitives; + +impl NodePrimitives for EthPrimitives { + type Block = Block; +} + /// Type configuration for a regular Ethereum node. #[derive(Debug, Default, Clone, Copy)] #[non_exhaustive] @@ -46,7 +59,6 @@ impl EthereumNode { EthereumNetworkBuilder, EthereumExecutorBuilder, EthereumConsensusBuilder, - EthereumEngineValidatorBuilder, > where Node: FullNodeTypes>, @@ -63,12 +75,11 @@ impl EthereumNode { .network(EthereumNetworkBuilder::default()) .executor(EthereumExecutorBuilder::default()) .consensus(EthereumConsensusBuilder::default()) - .engine_validator(EthereumEngineValidatorBuilder::default()) } } impl NodeTypes for EthereumNode { - type Primitives = (); + type Primitives = EthPrimitives; type ChainSpec = ChainSpec; } @@ -77,17 +88,20 @@ impl NodeTypesWithEngine for EthereumNode { } /// Add-ons w.r.t. l1 ethereum. -#[derive(Debug, Clone, Default)] -#[non_exhaustive] -pub struct EthereumAddOns; - -impl NodeAddOns for EthereumAddOns { - type EthApi = EthApi; -} +pub type EthereumAddOns = RpcAddOns< + N, + EthApi< + ::Provider, + ::Pool, + NetworkHandle, + ::Evm, + >, + EthereumEngineValidatorBuilder, +>; impl Node for EthereumNode where - Types: NodeTypesWithEngine, + Types: NodeTypesWithDB + NodeTypesWithEngine, N: FullNodeTypes, { type ComponentsBuilder = ComponentsBuilder< @@ -97,10 +111,11 @@ where EthereumNetworkBuilder, EthereumExecutorBuilder, EthereumConsensusBuilder, - EthereumEngineValidatorBuilder, >; - type AddOns = EthereumAddOns; + type AddOns = EthereumAddOns< + NodeAdapter>::Components>, + >; fn components_builder(&self) -> Self::ComponentsBuilder { Self::components() @@ -122,7 +137,7 @@ where Node: FullNodeTypes, { type EVM = EthEvmConfig; - type Executor = EthExecutorProvider; + type Executor = BasicBlockExecutorProvider; async fn build_evm( self, @@ -130,7 +145,8 @@ where ) -> eyre::Result<(Self::EVM, Self::Executor)> { let chain_spec = ctx.chain_spec(); let evm_config = EthEvmConfig::new(ctx.chain_spec()); - let executor = EthExecutorProvider::new(chain_spec, evm_config.clone()); + let strategy_factory = EthExecutionStrategyFactory::new(chain_spec, evm_config.clone()); + let executor = BasicBlockExecutorProvider::new(strategy_factory); Ok((evm_config, executor)) } @@ -332,12 +348,12 @@ pub struct EthereumEngineValidatorBuilder; impl EngineValidatorBuilder for EthereumEngineValidatorBuilder where Types: NodeTypesWithEngine, - Node: FullNodeTypes, + Node: FullNodeComponents, EthereumEngineValidator: EngineValidator, { type Validator = EthereumEngineValidator; - async fn build_validator(self, ctx: &BuilderContext) -> eyre::Result { - Ok(EthereumEngineValidator::new(ctx.chain_spec())) + async fn build(self, ctx: &AddOnsContext<'_, Node>) -> eyre::Result { + Ok(EthereumEngineValidator::new(ctx.config.chain.clone())) } } diff --git a/crates/ethereum/node/tests/e2e/blobs.rs b/crates/ethereum/node/tests/e2e/blobs.rs index 9390b34f444a..b4d9a532aebe 100644 --- a/crates/ethereum/node/tests/e2e/blobs.rs +++ b/crates/ethereum/node/tests/e2e/blobs.rs @@ -1,7 +1,7 @@ use std::sync::Arc; +use alloy_consensus::constants::MAINNET_GENESIS_HASH; use alloy_genesis::Genesis; -use alloy_primitives::b256; use reth::{ args::RpcServerArgs, builder::{NodeBuilder, NodeConfig, NodeHandle}, @@ -75,13 +75,11 @@ async fn can_handle_blobs() -> eyre::Result<()> { .submit_payload(blob_payload, blob_attr, PayloadStatusEnum::Valid, versioned_hashes.clone()) .await?; - let genesis_hash = b256!("d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"); - let (_, _) = tokio::join!( // send fcu with blob hash - node.engine_api.update_forkchoice(genesis_hash, blob_block_hash), + node.engine_api.update_forkchoice(MAINNET_GENESIS_HASH, blob_block_hash), // send fcu with normal hash - node.engine_api.update_forkchoice(genesis_hash, payload.block().hash()) + node.engine_api.update_forkchoice(MAINNET_GENESIS_HASH, payload.block().hash()) ); // submit normal payload diff --git a/crates/ethereum/node/tests/e2e/dev.rs b/crates/ethereum/node/tests/e2e/dev.rs index 6b4733b6f9b4..cad2fb34e5de 100644 --- a/crates/ethereum/node/tests/e2e/dev.rs +++ b/crates/ethereum/node/tests/e2e/dev.rs @@ -6,8 +6,10 @@ use futures::StreamExt; use reth::{args::DevArgs, core::rpc::eth::helpers::EthTransactions}; use reth_chainspec::ChainSpec; use reth_e2e_test_utils::setup; -use reth_node_api::{FullNodeComponents, NodeAddOns}; -use reth_node_builder::{EngineNodeLauncher, FullNode, NodeBuilder, NodeConfig, NodeHandle}; +use reth_node_api::FullNodeComponents; +use reth_node_builder::{ + rpc::RethRpcAddOns, EngineNodeLauncher, FullNode, NodeBuilder, NodeConfig, NodeHandle, +}; use reth_node_ethereum::{node::EthereumAddOns, EthereumNode}; use reth_provider::{providers::BlockchainProvider2, CanonStateSubscriptions}; use reth_tasks::TaskManager; @@ -53,7 +55,7 @@ async fn can_run_dev_node_new_engine() -> eyre::Result<()> { async fn assert_chain_advances(node: FullNode) where N: FullNodeComponents, - AddOns: NodeAddOns, + AddOns: RethRpcAddOns, { let mut notifications = node.provider.canonical_state_stream(); diff --git a/crates/ethereum/payload/Cargo.toml b/crates/ethereum/payload/Cargo.toml index f169d58f7e8d..443e837b2ed1 100644 --- a/crates/ethereum/payload/Cargo.toml +++ b/crates/ethereum/payload/Cargo.toml @@ -33,6 +33,8 @@ revm.workspace = true revm-primitives.workspace = true # alloy +alloy-eips.workspace = true +alloy-consensus.workspace = true alloy-primitives.workspace = true # misc diff --git a/crates/ethereum/payload/src/lib.rs b/crates/ethereum/payload/src/lib.rs index 248aa3486de4..7f94acf723cf 100644 --- a/crates/ethereum/payload/src/lib.rs +++ b/crates/ethereum/payload/src/lib.rs @@ -9,6 +9,8 @@ #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #![allow(clippy::useless_let_if_seq)] +use alloy_consensus::EMPTY_OMMER_ROOT_HASH; +use alloy_eips::eip7685::Requests; use alloy_primitives::U256; use reth_basic_payload_builder::{ commit_withdrawals, is_better_payload, BuildArguments, BuildOutcome, PayloadBuilder, @@ -24,9 +26,9 @@ use reth_payload_builder::{EthBuiltPayload, EthPayloadBuilderAttributes}; use reth_payload_primitives::{PayloadBuilderAttributes, PayloadBuilderError}; use reth_primitives::{ constants::{eip4844::MAX_DATA_GAS_PER_BLOCK, BEACON_NONCE}, - proofs::{self, calculate_requests_root}, + proofs::{self}, revm_primitives::{BlockEnv, CfgEnvWithHandlerCfg}, - Block, BlockBody, EthereumHardforks, Header, Receipt, EMPTY_OMMER_ROOT_HASH, + Block, BlockBody, EthereumHardforks, Header, Receipt, }; use reth_provider::{ChainSpecProvider, StateProviderFactory}; use reth_revm::database::StateProviderDatabase; @@ -307,9 +309,7 @@ where } // calculate the requests and the requests root - let (requests, requests_root) = if chain_spec - .is_prague_active_at_timestamp(attributes.timestamp) - { + let requests = if chain_spec.is_prague_active_at_timestamp(attributes.timestamp) { let deposit_requests = parse_deposits_from_receipts(&chain_spec, receipts.iter().flatten()) .map_err(|err| PayloadBuilderError::Internal(RethError::Execution(err.into())))?; let withdrawal_requests = system_caller @@ -327,11 +327,9 @@ where ) .map_err(|err| PayloadBuilderError::Internal(err.into()))?; - let requests = [deposit_requests, withdrawal_requests, consolidation_requests].concat(); - let requests_root = calculate_requests_root(&requests); - (Some(requests.into()), Some(requests_root)) + Some(Requests::new(vec![deposit_requests, withdrawal_requests, consolidation_requests])) } else { - (None, None) + None }; let WithdrawalsOutcome { withdrawals_root, withdrawals } = @@ -341,11 +339,12 @@ where // and 4788 contract call db.merge_transitions(BundleRetention::Reverts); + let requests_hash = requests.as_ref().map(|requests| requests.requests_hash()); let execution_outcome = ExecutionOutcome::new( db.take_bundle(), - vec![receipts.clone()].into(), + vec![receipts].into(), block_number, - vec![requests.clone().unwrap_or_default()], + vec![requests.unwrap_or_default()], ); let receipts_root = execution_outcome.receipts_root_slow(block_number).expect("Number is in range"); @@ -413,13 +412,13 @@ where parent_beacon_block_root: attributes.parent_beacon_block_root, blob_gas_used: blob_gas_used.map(Into::into), excess_blob_gas: excess_blob_gas.map(Into::into), - requests_root, + requests_hash, }; // seal the block let block = Block { header, - body: BlockBody { transactions: executed_txs, ommers: vec![], withdrawals, requests }, + body: BlockBody { transactions: executed_txs, ommers: vec![], withdrawals }, }; let sealed_block = block.seal_slow(); diff --git a/crates/evm/Cargo.toml b/crates/evm/Cargo.toml index 20070d421e97..6a1e1fe0d727 100644 --- a/crates/evm/Cargo.toml +++ b/crates/evm/Cargo.toml @@ -13,16 +13,19 @@ workspace = true [dependencies] # reth reth-chainspec.workspace = true +reth-consensus.workspace = true +reth-consensus-common.workspace = true reth-execution-errors.workspace = true +reth-execution-types.workspace = true +reth-metrics = { workspace = true, optional = true } reth-primitives.workspace = true reth-primitives-traits.workspace = true -revm-primitives.workspace = true reth-prune-types.workspace = true -reth-metrics = { workspace = true, optional = true } +reth-revm.workspace = true reth-storage-errors.workspace = true -reth-execution-types.workspace = true revm.workspace = true +revm-primitives.workspace = true # alloy alloy-primitives.workspace = true @@ -35,8 +38,28 @@ parking_lot = { workspace = true, optional = true } [dev-dependencies] parking_lot.workspace = true +reth-ethereum-forks.workspace = true [features] default = ["std"] -std = ["dep:metrics", "dep:reth-metrics"] -test-utils = ["dep:parking_lot"] +std = [ + "dep:metrics", + "dep:reth-metrics", + "reth-consensus/std", + "reth-primitives/std", + "reth-primitives-traits/std", + "reth-revm/std", + "alloy-eips/std", + "alloy-primitives/std", + "revm-primitives/std", + "revm/std" +] +test-utils = [ + "dep:parking_lot", + "reth-chainspec/test-utils", + "reth-consensus/test-utils", + "reth-primitives/test-utils", + "reth-primitives-traits/test-utils", + "reth-revm/test-utils", + "revm/test-utils" +] diff --git a/crates/evm/execution-errors/Cargo.toml b/crates/evm/execution-errors/Cargo.toml index d4f8534e7520..721c8055110d 100644 --- a/crates/evm/execution-errors/Cargo.toml +++ b/crates/evm/execution-errors/Cargo.toml @@ -26,4 +26,9 @@ derive_more.workspace = true [features] default = ["std"] -std = ["reth-consensus/std"] +std = [ + "reth-consensus/std", + "alloy-eips/std", + "alloy-primitives/std", + "revm-primitives/std" +] diff --git a/crates/evm/execution-types/Cargo.toml b/crates/evm/execution-types/Cargo.toml index 9bd6537326b1..b6af3dee9afd 100644 --- a/crates/evm/execution-types/Cargo.toml +++ b/crates/evm/execution-types/Cargo.toml @@ -25,7 +25,6 @@ serde = { workspace = true, optional = true } serde_with = { workspace = true, optional = true } [dev-dependencies] -alloy-eips.workspace = true arbitrary.workspace = true bincode.workspace = true rand.workspace = true @@ -34,6 +33,24 @@ reth-primitives = { workspace = true, features = ["arbitrary", "test-utils"] } [features] default = ["std"] optimism = ["reth-primitives/optimism", "revm/optimism"] -serde = ["dep:serde", "reth-trie/serde", "revm/serde"] -serde-bincode-compat = ["reth-primitives/serde-bincode-compat", "reth-trie/serde-bincode-compat", "serde_with"] -std = [] +serde = [ + "dep:serde", + "reth-trie/serde", + "revm/serde", + "alloy-eips/serde", + "alloy-primitives/serde", + "rand/serde" +] +serde-bincode-compat = [ + "reth-primitives/serde-bincode-compat", + "reth-trie/serde-bincode-compat", + "serde_with", + "alloy-eips/serde-bincode-compat" +] +std = [ + "reth-primitives/std", + "alloy-eips/std", + "alloy-primitives/std", + "revm/std", + "serde?/std" +] diff --git a/crates/evm/execution-types/src/chain.rs b/crates/evm/execution-types/src/chain.rs index 30f1f4cd2fc3..65f96ff56387 100644 --- a/crates/evm/execution-types/src/chain.rs +++ b/crates/evm/execution-types/src/chain.rs @@ -228,11 +228,11 @@ impl Chain { /// /// Attachment includes block number, block hash, transaction hash and transaction index. pub fn receipts_with_attachment(&self) -> Vec { - let mut receipt_attach = Vec::new(); + let mut receipt_attach = Vec::with_capacity(self.blocks().len()); for ((block_num, block), receipts) in self.blocks().iter().zip(self.execution_outcome.receipts().iter()) { - let mut tx_receipts = Vec::new(); + let mut tx_receipts = Vec::with_capacity(receipts.len()); for (tx, receipt) in block.body.transactions().zip(receipts.iter()) { tx_receipts.push(( tx.hash(), @@ -656,7 +656,6 @@ pub(super) mod serde_bincode_compat { mod tests { use super::*; use alloy_primitives::B256; - use reth_primitives::{Receipt, Receipts, TxType}; use revm::primitives::{AccountInfo, HashMap}; #[test] @@ -789,7 +788,10 @@ mod tests { } #[test] + #[cfg(not(feature = "optimism"))] fn receipts_by_block_hash() { + use reth_primitives::{Receipt, Receipts, TxType}; + // Create a default SealedBlockWithSenders object let block = SealedBlockWithSenders::default(); @@ -811,10 +813,6 @@ mod tests { cumulative_gas_used: 46913, logs: vec![], success: true, - #[cfg(feature = "optimism")] - deposit_nonce: Some(18), - #[cfg(feature = "optimism")] - deposit_receipt_version: Some(34), }; // Create another random receipt object, receipt2 @@ -823,10 +821,6 @@ mod tests { cumulative_gas_used: 1325345, logs: vec![], success: true, - #[cfg(feature = "optimism")] - deposit_nonce: Some(18), - #[cfg(feature = "optimism")] - deposit_receipt_version: Some(34), }; // Create a Receipts object with a vector of receipt vectors diff --git a/crates/evm/execution-types/src/execute.rs b/crates/evm/execution-types/src/execute.rs index 0cf5d7050793..ae5ad2c0b7c5 100644 --- a/crates/evm/execution-types/src/execute.rs +++ b/crates/evm/execution-types/src/execute.rs @@ -1,5 +1,5 @@ +use alloy_eips::eip7685::Requests; use alloy_primitives::U256; -use reth_primitives::Request; use revm::db::BundleState; /// A helper type for ethereum block inputs that consists of a block and the total difficulty. @@ -33,8 +33,8 @@ pub struct BlockExecutionOutput { pub state: BundleState, /// All the receipts of the transactions in the block. pub receipts: Vec, - /// All the EIP-7685 requests of the transactions in the block. - pub requests: Vec, + /// All the EIP-7685 requests in the block. + pub requests: Requests, /// The total gas used by the block. pub gas_used: u64, } diff --git a/crates/evm/execution-types/src/execution_outcome.rs b/crates/evm/execution-types/src/execution_outcome.rs index 08ddf9e4167b..0fde01547f7e 100644 --- a/crates/evm/execution-types/src/execution_outcome.rs +++ b/crates/evm/execution-types/src/execution_outcome.rs @@ -1,6 +1,7 @@ use crate::BlockExecutionOutput; +use alloy_eips::eip7685::Requests; use alloy_primitives::{Address, BlockNumber, Bloom, Log, B256, U256}; -use reth_primitives::{logs_bloom, Account, Bytecode, Receipt, Receipts, Requests, StorageEntry}; +use reth_primitives::{logs_bloom, Account, Bytecode, Receipt, Receipts, StorageEntry}; use reth_trie::HashedPostState; use revm::{ db::{states::BundleState, BundleAccount}, @@ -357,7 +358,7 @@ impl From<(BlockExecutionOutput, BlockNumber)> for ExecutionOutcome { bundle: value.0.state, receipts: Receipts::from(value.0.receipts), first_block: value.1, - requests: vec![Requests::from(value.0.requests)], + requests: vec![value.0.requests], } } } @@ -365,9 +366,9 @@ impl From<(BlockExecutionOutput, BlockNumber)> for ExecutionOutcome { #[cfg(test)] mod tests { use super::*; - use alloy_eips::{eip6110::DepositRequest, eip7002::WithdrawalRequest}; - use alloy_primitives::{Address, FixedBytes, LogData, B256}; - use reth_primitives::{Receipts, Request, Requests, TxType}; + use alloy_eips::eip7685::Requests; + use alloy_primitives::{bytes, Address, LogData, B256}; + use reth_primitives::{Receipts, TxType}; use std::collections::HashMap; #[test] @@ -393,29 +394,8 @@ mod tests { })]], }; - // Create a Requests object with a vector of requests, including DepositRequest and - // WithdrawalRequest - let requests = vec![Requests(vec![ - Request::DepositRequest(DepositRequest { - pubkey: FixedBytes::<48>::from([1; 48]), - withdrawal_credentials: B256::from([0; 32]), - amount: 1111, - signature: FixedBytes::<96>::from([2; 96]), - index: 222, - }), - Request::DepositRequest(DepositRequest { - pubkey: FixedBytes::<48>::from([23; 48]), - withdrawal_credentials: B256::from([0; 32]), - amount: 34343, - signature: FixedBytes::<96>::from([43; 96]), - index: 1212, - }), - Request::WithdrawalRequest(WithdrawalRequest { - source_address: Address::from([1; 20]), - validator_pubkey: FixedBytes::<48>::from([10; 48]), - amount: 72, - }), - ])]; + // Create a Requests object with a vector of requests + let requests = vec![Requests::new(vec![bytes!("dead"), bytes!("beef"), bytes!("beebee")])]; // Define the first block number let first_block = 123; @@ -657,17 +637,12 @@ mod tests { // Define the first block number let first_block = 123; - // Create a DepositRequest object with specific attributes. - let request = Request::DepositRequest(DepositRequest { - pubkey: FixedBytes::<48>::from([1; 48]), - withdrawal_credentials: B256::from([0; 32]), - amount: 1111, - signature: FixedBytes::<96>::from([2; 96]), - index: 222, - }); + // Create a request. + let request = bytes!("deadbeef"); // Create a vector of Requests containing the request. - let requests = vec![Requests(vec![request]), Requests(vec![request])]; + let requests = + vec![Requests::new(vec![request.clone()]), Requests::new(vec![request.clone()])]; // Create a ExecutionOutcome object with the created bundle, receipts, requests, and // first_block @@ -681,7 +656,7 @@ mod tests { assert_eq!(exec_res.receipts, Receipts { receipt_vec: vec![vec![Some(receipt)]] }); // Assert that the requests are properly cut after reverting to the initial block number. - assert_eq!(exec_res.requests, vec![Requests(vec![request])]); + assert_eq!(exec_res.requests, vec![Requests::new(vec![request])]); // Assert that the revert_to method returns false when attempting to revert to a block // number greater than the initial block number. @@ -709,17 +684,11 @@ mod tests { // Create a Receipts object containing the receipt. let receipts = Receipts { receipt_vec: vec![vec![Some(receipt.clone())]] }; - // Create a DepositRequest object with specific attributes. - let request = Request::DepositRequest(DepositRequest { - pubkey: FixedBytes::<48>::from([1; 48]), - withdrawal_credentials: B256::from([0; 32]), - amount: 1111, - signature: FixedBytes::<96>::from([2; 96]), - index: 222, - }); + // Create a request. + let request = bytes!("deadbeef"); // Create a vector of Requests containing the request. - let requests = vec![Requests(vec![request])]; + let requests = vec![Requests::new(vec![request.clone()])]; // Define the initial block number. let first_block = 123; @@ -739,7 +708,7 @@ mod tests { receipts: Receipts { receipt_vec: vec![vec![Some(receipt.clone())], vec![Some(receipt)]] }, - requests: vec![Requests(vec![request]), Requests(vec![request])], + requests: vec![Requests::new(vec![request.clone()]), Requests::new(vec![request])], first_block: 123, } ); @@ -771,18 +740,15 @@ mod tests { // Define the first block number let first_block = 123; - // Create a DepositRequest object with specific attributes. - let request = Request::DepositRequest(DepositRequest { - pubkey: FixedBytes::<48>::from([1; 48]), - withdrawal_credentials: B256::from([0; 32]), - amount: 1111, - signature: FixedBytes::<96>::from([2; 96]), - index: 222, - }); + // Create a request. + let request = bytes!("deadbeef"); // Create a vector of Requests containing the request. - let requests = - vec![Requests(vec![request]), Requests(vec![request]), Requests(vec![request])]; + let requests = vec![ + Requests::new(vec![request.clone()]), + Requests::new(vec![request.clone()]), + Requests::new(vec![request.clone()]), + ]; // Create a ExecutionOutcome object with the created bundle, receipts, requests, and // first_block @@ -796,7 +762,7 @@ mod tests { let lower_execution_outcome = ExecutionOutcome { bundle: Default::default(), receipts: Receipts { receipt_vec: vec![vec![Some(receipt.clone())]] }, - requests: vec![Requests(vec![request])], + requests: vec![Requests::new(vec![request.clone()])], first_block, }; @@ -806,7 +772,7 @@ mod tests { receipts: Receipts { receipt_vec: vec![vec![Some(receipt.clone())], vec![Some(receipt)]], }, - requests: vec![Requests(vec![request]), Requests(vec![request])], + requests: vec![Requests::new(vec![request.clone()]), Requests::new(vec![request])], first_block: 124, }; diff --git a/crates/evm/src/execute.rs b/crates/evm/src/execute.rs index eb77d054bdcd..2b3ce85e9b94 100644 --- a/crates/evm/src/execute.rs +++ b/crates/evm/src/execute.rs @@ -7,15 +7,17 @@ pub use reth_execution_errors::{ pub use reth_execution_types::{BlockExecutionInput, BlockExecutionOutput, ExecutionOutcome}; pub use reth_storage_errors::provider::ProviderError; +use crate::system_calls::OnStateHook; use alloc::{boxed::Box, vec::Vec}; +use alloy_eips::eip7685::Requests; use alloy_primitives::BlockNumber; use core::{fmt::Display, marker::PhantomData}; -use reth_primitives::{BlockWithSenders, Receipt, Request}; +use reth_consensus::ConsensusError; +use reth_primitives::{BlockWithSenders, Receipt}; use reth_prune_types::PruneModes; +use reth_revm::batch::BlockBatchRecord; use revm::{db::BundleState, State}; -use revm_primitives::db::Database; - -use crate::system_calls::OnStateHook; +use revm_primitives::{db::Database, U256}; /// A general purpose executor trait that executes an input (e.g. block) and produces an output /// (e.g. state changes and receipts). @@ -164,31 +166,61 @@ pub trait BlockExecutorProvider: Send + Sync + Clone + Unpin + 'static { DB: Database + Display>; } +/// Helper type for the output of executing a block. +#[derive(Debug, Clone)] +pub struct ExecuteOutput { + /// Receipts obtained after executing a block. + pub receipts: Vec, + /// Cumulative gas used in the block execution. + pub gas_used: u64, +} + /// Defines the strategy for executing a single block. pub trait BlockExecutionStrategy { /// The error type returned by this strategy's methods. type Error: From + core::error::Error; /// Applies any necessary changes before executing the block's transactions. - fn apply_pre_execution_changes(&mut self) -> Result<(), Self::Error>; + fn apply_pre_execution_changes( + &mut self, + block: &BlockWithSenders, + total_difficulty: U256, + ) -> Result<(), Self::Error>; /// Executes all transactions in the block. fn execute_transactions( &mut self, block: &BlockWithSenders, - ) -> Result<(Vec, u64), Self::Error>; + total_difficulty: U256, + ) -> Result; /// Applies any necessary changes after executing the block's transactions. - fn apply_post_execution_changes(&mut self) -> Result, Self::Error>; + fn apply_post_execution_changes( + &mut self, + block: &BlockWithSenders, + total_difficulty: U256, + receipts: &[Receipt], + ) -> Result; /// Returns a reference to the current state. fn state_ref(&self) -> &State; + /// Returns a mutable reference to the current state. + fn state_mut(&mut self) -> &mut State; + /// Sets a hook to be called after each state change during execution. fn with_state_hook(&mut self, hook: Option>); /// Returns the final bundle state. - fn finish(&self) -> BundleState; + fn finish(&mut self) -> BundleState; + + /// Validate a block with regard to execution results. + fn validate_block_post_execution( + &self, + block: &BlockWithSenders, + receipts: &[Receipt], + requests: &Requests, + ) -> Result<(), ConsensusError>; } /// A strategy factory that can create block execution strategies. @@ -205,7 +237,7 @@ pub trait BlockExecutionStrategyFactory: Send + Sync + Clone + Unpin + 'static { DB: Database + Display>; } -impl Clone for GenericBlockExecutorProvider +impl Clone for BasicBlockExecutorProvider where F: Clone, { @@ -216,33 +248,33 @@ where /// A generic block executor provider that can create executors using a strategy factory. #[allow(missing_debug_implementations)] -pub struct GenericBlockExecutorProvider { +pub struct BasicBlockExecutorProvider { strategy_factory: F, } -impl GenericBlockExecutorProvider { - /// Creates a new `GenericBlockExecutorProvider` with the given strategy factory. +impl BasicBlockExecutorProvider { + /// Creates a new `BasicBlockExecutorProvider` with the given strategy factory. pub const fn new(strategy_factory: F) -> Self { Self { strategy_factory } } } -impl BlockExecutorProvider for GenericBlockExecutorProvider +impl BlockExecutorProvider for BasicBlockExecutorProvider where F: BlockExecutionStrategyFactory, { type Executor + Display>> = - GenericBlockExecutor, DB>; + BasicBlockExecutor, DB>; type BatchExecutor + Display>> = - GenericBatchExecutor, DB>; + BasicBatchExecutor, DB>; fn executor(&self, db: DB) -> Self::Executor where DB: Database + Display>, { let strategy = self.strategy_factory.create_strategy(db); - GenericBlockExecutor::new(strategy) + BasicBlockExecutor::new(strategy) } fn batch_executor(&self, db: DB) -> Self::BatchExecutor @@ -250,32 +282,34 @@ where DB: Database + Display>, { let strategy = self.strategy_factory.create_strategy(db); - GenericBatchExecutor::new(strategy) + let batch_record = BlockBatchRecord::default(); + BasicBatchExecutor::new(strategy, batch_record) } } /// A generic block executor that uses a [`BlockExecutionStrategy`] to /// execute blocks. #[allow(missing_debug_implementations, dead_code)] -pub struct GenericBlockExecutor +pub struct BasicBlockExecutor where S: BlockExecutionStrategy, { - strategy: S, + /// Block execution strategy. + pub(crate) strategy: S, _phantom: PhantomData, } -impl GenericBlockExecutor +impl BasicBlockExecutor where S: BlockExecutionStrategy, { - /// Creates a new `GenericBlockExecutor` with the given strategy. + /// Creates a new `BasicBlockExecutor` with the given strategy. pub const fn new(strategy: S) -> Self { Self { strategy, _phantom: PhantomData } } } -impl Executor for GenericBlockExecutor +impl Executor for BasicBlockExecutor where S: BlockExecutionStrategy, DB: Database + Display>, @@ -285,11 +319,13 @@ where type Error = S::Error; fn execute(mut self, input: Self::Input<'_>) -> Result { - let BlockExecutionInput { block, total_difficulty: _ } = input; + let BlockExecutionInput { block, total_difficulty } = input; - self.strategy.apply_pre_execution_changes()?; - let (receipts, gas_used) = self.strategy.execute_transactions(block)?; - let requests = self.strategy.apply_post_execution_changes()?; + self.strategy.apply_pre_execution_changes(block, total_difficulty)?; + let ExecuteOutput { receipts, gas_used } = + self.strategy.execute_transactions(block, total_difficulty)?; + let requests = + self.strategy.apply_post_execution_changes(block, total_difficulty, &receipts)?; let state = self.strategy.finish(); Ok(BlockExecutionOutput { state, receipts, requests, gas_used }) @@ -303,11 +339,13 @@ where where F: FnMut(&State), { - let BlockExecutionInput { block, total_difficulty: _ } = input; + let BlockExecutionInput { block, total_difficulty } = input; - self.strategy.apply_pre_execution_changes()?; - let (receipts, gas_used) = self.strategy.execute_transactions(block)?; - let requests = self.strategy.apply_post_execution_changes()?; + self.strategy.apply_pre_execution_changes(block, total_difficulty)?; + let ExecuteOutput { receipts, gas_used } = + self.strategy.execute_transactions(block, total_difficulty)?; + let requests = + self.strategy.apply_post_execution_changes(block, total_difficulty, &receipts)?; state(self.strategy.state_ref()); @@ -324,13 +362,15 @@ where where H: OnStateHook + 'static, { - let BlockExecutionInput { block, total_difficulty: _ } = input; + let BlockExecutionInput { block, total_difficulty } = input; self.strategy.with_state_hook(Some(Box::new(state_hook))); - self.strategy.apply_pre_execution_changes()?; - let (receipts, gas_used) = self.strategy.execute_transactions(block)?; - let requests = self.strategy.apply_post_execution_changes()?; + self.strategy.apply_pre_execution_changes(block, total_difficulty)?; + let ExecuteOutput { receipts, gas_used } = + self.strategy.execute_transactions(block, total_difficulty)?; + let requests = + self.strategy.apply_post_execution_changes(block, total_difficulty, &receipts)?; let state = self.strategy.finish(); @@ -341,19 +381,28 @@ where /// A generic batch executor that uses a [`BlockExecutionStrategy`] to /// execute batches. #[allow(missing_debug_implementations)] -pub struct GenericBatchExecutor { - _strategy: S, +pub struct BasicBatchExecutor +where + S: BlockExecutionStrategy, +{ + /// Batch execution strategy. + pub(crate) strategy: S, + /// Keeps track of batch execution receipts and requests. + pub(crate) batch_record: BlockBatchRecord, _phantom: PhantomData, } -impl GenericBatchExecutor { - /// Creates a new `GenericBatchExecutor` with the given strategy. - pub const fn new(_strategy: S) -> Self { - Self { _strategy, _phantom: PhantomData } +impl BasicBatchExecutor +where + S: BlockExecutionStrategy, +{ + /// Creates a new `BasicBatchExecutor` with the given strategy. + pub const fn new(strategy: S, batch_record: BlockBatchRecord) -> Self { + Self { strategy, batch_record, _phantom: PhantomData } } } -impl BatchExecutor for GenericBatchExecutor +impl BatchExecutor for BasicBatchExecutor where S: BlockExecutionStrategy, DB: Database + Display>, @@ -362,34 +411,63 @@ where type Output = ExecutionOutcome; type Error = BlockExecutionError; - fn execute_and_verify_one(&mut self, _input: Self::Input<'_>) -> Result<(), Self::Error> { - todo!() + fn execute_and_verify_one(&mut self, input: Self::Input<'_>) -> Result<(), Self::Error> { + let BlockExecutionInput { block, total_difficulty } = input; + + if self.batch_record.first_block().is_none() { + self.batch_record.set_first_block(block.number); + } + + self.strategy.apply_pre_execution_changes(block, total_difficulty)?; + let ExecuteOutput { receipts, .. } = + self.strategy.execute_transactions(block, total_difficulty)?; + let requests = + self.strategy.apply_post_execution_changes(block, total_difficulty, &receipts)?; + + self.strategy.validate_block_post_execution(block, &receipts, &requests)?; + + // prepare the state according to the prune mode + let retention = self.batch_record.bundle_retention(block.number); + self.strategy.state_mut().merge_transitions(retention); + + // store receipts in the set + self.batch_record.save_receipts(receipts)?; + + // store requests in the set + self.batch_record.save_requests(requests); + + Ok(()) } - fn finalize(self) -> Self::Output { - todo!() + fn finalize(mut self) -> Self::Output { + ExecutionOutcome::new( + self.strategy.state_mut().take_bundle(), + self.batch_record.take_receipts(), + self.batch_record.first_block().unwrap_or_default(), + self.batch_record.take_requests(), + ) } - fn set_tip(&mut self, _tip: BlockNumber) { - todo!() + fn set_tip(&mut self, tip: BlockNumber) { + self.batch_record.set_tip(tip); } - fn set_prune_modes(&mut self, _prune_modes: PruneModes) { - todo!() + fn set_prune_modes(&mut self, prune_modes: PruneModes) { + self.batch_record.set_prune_modes(prune_modes); } fn size_hint(&self) -> Option { - None + Some(self.strategy.state_ref().bundle_state.size_hint()) } } #[cfg(test)] mod tests { use super::*; - use alloy_eips::eip6110::DepositRequest; use alloy_primitives::U256; use reth_chainspec::{ChainSpec, MAINNET}; use revm::db::{CacheDB, EmptyDBTyped}; + use revm_primitives::bytes; use std::sync::Arc; #[derive(Clone, Default)] @@ -480,15 +558,15 @@ mod tests { _chain_spec: Arc, _evm_config: EvmConfig, state: State, - execute_transactions_result: (Vec, u64), - apply_post_execution_changes_result: Vec, + execute_transactions_result: ExecuteOutput, + apply_post_execution_changes_result: Requests, finish_result: BundleState, } #[derive(Clone)] struct TestExecutorStrategyFactory { - execute_transactions_result: (Vec, u64), - apply_post_execution_changes_result: Vec, + execute_transactions_result: ExecuteOutput, + apply_post_execution_changes_result: Requests, finish_result: BundleState, } @@ -522,18 +600,28 @@ mod tests { impl BlockExecutionStrategy for TestExecutorStrategy { type Error = BlockExecutionError; - fn apply_pre_execution_changes(&mut self) -> Result<(), Self::Error> { + fn apply_pre_execution_changes( + &mut self, + _block: &BlockWithSenders, + _total_difficulty: U256, + ) -> Result<(), Self::Error> { Ok(()) } fn execute_transactions( &mut self, _block: &BlockWithSenders, - ) -> Result<(Vec, u64), Self::Error> { + _total_difficulty: U256, + ) -> Result { Ok(self.execute_transactions_result.clone()) } - fn apply_post_execution_changes(&mut self) -> Result, Self::Error> { + fn apply_post_execution_changes( + &mut self, + _block: &BlockWithSenders, + _total_difficulty: U256, + _receipts: &[Receipt], + ) -> Result { Ok(self.apply_post_execution_changes_result.clone()) } @@ -541,11 +629,24 @@ mod tests { &self.state } + fn state_mut(&mut self) -> &mut State { + &mut self.state + } + fn with_state_hook(&mut self, _hook: Option>) {} - fn finish(&self) -> BundleState { + fn finish(&mut self) -> BundleState { self.finish_result.clone() } + + fn validate_block_post_execution( + &self, + _block: &BlockWithSenders, + _receipts: &[Receipt], + _requests: &Requests, + ) -> Result<(), ConsensusError> { + Ok(()) + } } #[derive(Clone)] @@ -563,9 +664,9 @@ mod tests { fn test_strategy() { let expected_gas_used = 10; let expected_receipts = vec![Receipt::default()]; - let expected_execute_transactions_result = (expected_receipts.clone(), expected_gas_used); - let expected_apply_post_execution_changes_result = - vec![Request::DepositRequest(DepositRequest::default())]; + let expected_execute_transactions_result = + ExecuteOutput { receipts: expected_receipts.clone(), gas_used: expected_gas_used }; + let expected_apply_post_execution_changes_result = Requests::new(vec![bytes!("deadbeef")]); let expected_finish_result = BundleState::default(); let strategy_factory = TestExecutorStrategyFactory { @@ -574,7 +675,7 @@ mod tests { .clone(), finish_result: expected_finish_result.clone(), }; - let provider = GenericBlockExecutorProvider::new(strategy_factory); + let provider = BasicBlockExecutorProvider::new(strategy_factory); let db = CacheDB::>::default(); let executor = provider.executor(db); let result = executor.execute(BlockExecutionInput::new(&Default::default(), U256::ZERO)); diff --git a/crates/evm/src/lib.rs b/crates/evm/src/lib.rs index 66026a07c94e..b75feea83a15 100644 --- a/crates/evm/src/lib.rs +++ b/crates/evm/src/lib.rs @@ -31,6 +31,7 @@ pub mod execute; pub mod metrics; pub mod noop; pub mod provider; +pub mod state_change; pub mod system_calls; #[cfg(any(test, feature = "test-utils"))] diff --git a/crates/revm/src/state_change.rs b/crates/evm/src/state_change.rs similarity index 99% rename from crates/revm/src/state_change.rs rename to crates/evm/src/state_change.rs index afe92561bcd3..2d5209015271 100644 --- a/crates/revm/src/state_change.rs +++ b/crates/evm/src/state_change.rs @@ -1,3 +1,5 @@ +//! State changes that are not related to transactions. + use alloy_primitives::{map::HashMap, Address, U256}; use reth_chainspec::EthereumHardforks; use reth_consensus_common::calc; diff --git a/crates/evm/src/system_calls/eip7002.rs b/crates/evm/src/system_calls/eip7002.rs index 9af944e42a53..5e36f2bdeb93 100644 --- a/crates/evm/src/system_calls/eip7002.rs +++ b/crates/evm/src/system_calls/eip7002.rs @@ -1,10 +1,10 @@ //! [EIP-7002](https://eips.ethereum.org/EIPS/eip-7002) system call implementation. use crate::ConfigureEvm; -use alloc::{boxed::Box, format, string::ToString, vec::Vec}; -use alloy_eips::eip7002::{WithdrawalRequest, WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS}; -use alloy_primitives::{bytes::Buf, Address, Bytes, FixedBytes}; +use alloc::{boxed::Box, format}; +use alloy_eips::eip7002::WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS; +use alloy_primitives::Bytes; use reth_execution_errors::{BlockExecutionError, BlockValidationError}; -use reth_primitives::{Header, Request}; +use reth_primitives::Header; use revm::{interpreter::Host, Database, Evm}; use revm_primitives::{ExecutionResult, ResultAndState}; @@ -62,52 +62,23 @@ where Ok(res) } -/// Parses the withdrawal requests from the execution output. +/// Calls the withdrawals requests system contract, and returns the requests from the execution +/// output. #[inline] -pub(crate) fn post_commit(result: ExecutionResult) -> Result, BlockExecutionError> { - let mut data = match result { +pub(crate) fn post_commit(result: ExecutionResult) -> Result { + match result { ExecutionResult::Success { output, .. } => Ok(output.into_data()), ExecutionResult::Revert { output, .. } => { Err(BlockValidationError::WithdrawalRequestsContractCall { message: format!("execution reverted: {output}"), - }) + } + .into()) } ExecutionResult::Halt { reason, .. } => { Err(BlockValidationError::WithdrawalRequestsContractCall { message: format!("execution halted: {reason:?}"), - }) - } - }?; - - // Withdrawals are encoded as a series of withdrawal requests, each with the following - // format: - // - // +------+--------+--------+ - // | addr | pubkey | amount | - // +------+--------+--------+ - // 20 48 8 - - const WITHDRAWAL_REQUEST_SIZE: usize = 20 + 48 + 8; - let mut withdrawal_requests = Vec::with_capacity(data.len() / WITHDRAWAL_REQUEST_SIZE); - while data.has_remaining() { - if data.remaining() < WITHDRAWAL_REQUEST_SIZE { - return Err(BlockValidationError::WithdrawalRequestsContractCall { - message: "invalid withdrawal request length".to_string(), } .into()) } - - let mut source_address = Address::ZERO; - data.copy_to_slice(source_address.as_mut_slice()); - - let mut validator_pubkey = FixedBytes::<48>::ZERO; - data.copy_to_slice(validator_pubkey.as_mut_slice()); - - let amount = data.get_u64(); - - withdrawal_requests - .push(WithdrawalRequest { source_address, validator_pubkey, amount }.into()); } - - Ok(withdrawal_requests) } diff --git a/crates/evm/src/system_calls/eip7251.rs b/crates/evm/src/system_calls/eip7251.rs index f09d4be81afc..7a55c7a5aeab 100644 --- a/crates/evm/src/system_calls/eip7251.rs +++ b/crates/evm/src/system_calls/eip7251.rs @@ -1,10 +1,10 @@ //! [EIP-7251](https://eips.ethereum.org/EIPS/eip-7251) system call implementation. use crate::ConfigureEvm; -use alloc::{boxed::Box, format, string::ToString, vec::Vec}; -use alloy_eips::eip7251::{ConsolidationRequest, CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS}; -use alloy_primitives::{bytes::Buf, Address, Bytes, FixedBytes}; +use alloc::{boxed::Box, format}; +use alloy_eips::eip7251::CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS; +use alloy_primitives::Bytes; use reth_execution_errors::{BlockExecutionError, BlockValidationError}; -use reth_primitives::{Header, Request}; +use reth_primitives::Header; use revm::{interpreter::Host, Database, Evm}; use revm_primitives::{ExecutionResult, ResultAndState}; @@ -64,56 +64,23 @@ where Ok(res) } -/// Parses the consolidation requests from the execution output. +/// Calls the consolidation requests system contract, and returns the requests from the execution +/// output. #[inline] -pub(crate) fn post_commit(result: ExecutionResult) -> Result, BlockExecutionError> { - let mut data = match result { +pub(crate) fn post_commit(result: ExecutionResult) -> Result { + match result { ExecutionResult::Success { output, .. } => Ok(output.into_data()), ExecutionResult::Revert { output, .. } => { Err(BlockValidationError::ConsolidationRequestsContractCall { message: format!("execution reverted: {output}"), - }) + } + .into()) } ExecutionResult::Halt { reason, .. } => { Err(BlockValidationError::ConsolidationRequestsContractCall { message: format!("execution halted: {reason:?}"), - }) - } - }?; - - // Consolidations are encoded as a series of consolidation requests, each with the following - // format: - // - // +------+--------+---------------+ - // | addr | pubkey | target pubkey | - // +------+--------+---------------+ - // 20 48 48 - - const CONSOLIDATION_REQUEST_SIZE: usize = 20 + 48 + 48; - let mut consolidation_requests = Vec::with_capacity(data.len() / CONSOLIDATION_REQUEST_SIZE); - while data.has_remaining() { - if data.remaining() < CONSOLIDATION_REQUEST_SIZE { - return Err(BlockValidationError::ConsolidationRequestsContractCall { - message: "invalid consolidation request length".to_string(), } .into()) } - - let mut source_address = Address::ZERO; - data.copy_to_slice(source_address.as_mut_slice()); - - let mut source_pubkey = FixedBytes::<48>::ZERO; - data.copy_to_slice(source_pubkey.as_mut_slice()); - - let mut target_pubkey = FixedBytes::<48>::ZERO; - data.copy_to_slice(target_pubkey.as_mut_slice()); - - consolidation_requests.push(Request::ConsolidationRequest(ConsolidationRequest { - source_address, - source_pubkey, - target_pubkey, - })); } - - Ok(consolidation_requests) } diff --git a/crates/evm/src/system_calls/mod.rs b/crates/evm/src/system_calls/mod.rs index 5dc3f35bd3a2..d71dcfedabb2 100644 --- a/crates/evm/src/system_calls/mod.rs +++ b/crates/evm/src/system_calls/mod.rs @@ -1,11 +1,13 @@ //! System contract call functions. use crate::ConfigureEvm; -use alloc::{boxed::Box, vec::Vec}; +use alloc::{boxed::Box, vec}; +use alloy_eips::eip7685::Requests; +use alloy_primitives::Bytes; use core::fmt::Display; use reth_chainspec::EthereumHardforks; use reth_execution_errors::BlockExecutionError; -use reth_primitives::{Block, Header, Request}; +use reth_primitives::{Block, Header}; use revm::{Database, DatabaseCommit, Evm}; use revm_primitives::{BlockEnv, CfgEnvWithHandlerCfg, EnvWithHandlerCfg, ResultAndState, B256}; @@ -119,17 +121,18 @@ where pub fn apply_post_execution_changes( &mut self, evm: &mut Evm<'_, Ext, DB>, - ) -> Result, BlockExecutionError> + ) -> Result where DB: Database + DatabaseCommit, DB::Error: Display, { + // todo // Collect all EIP-7685 requests let withdrawal_requests = self.apply_withdrawal_requests_contract_call(evm)?; // Collect all EIP-7251 requests let consolidation_requests = self.apply_consolidation_requests_contract_call(evm)?; - Ok([withdrawal_requests, consolidation_requests].concat()) + Ok(Requests::new(vec![withdrawal_requests, consolidation_requests])) } /// Applies the pre-block call to the EIP-2935 blockhashes contract. @@ -247,7 +250,7 @@ where db: &mut DB, initialized_cfg: &CfgEnvWithHandlerCfg, initialized_block_env: &BlockEnv, - ) -> Result, BlockExecutionError> + ) -> Result where DB: Database + DatabaseCommit, DB::Error: Display, @@ -263,7 +266,7 @@ where pub fn apply_withdrawal_requests_contract_call( &mut self, evm: &mut Evm<'_, Ext, DB>, - ) -> Result, BlockExecutionError> + ) -> Result where DB: Database + DatabaseCommit, DB::Error: Display, @@ -285,7 +288,7 @@ where db: &mut DB, initialized_cfg: &CfgEnvWithHandlerCfg, initialized_block_env: &BlockEnv, - ) -> Result, BlockExecutionError> + ) -> Result where DB: Database + DatabaseCommit, DB::Error: Display, @@ -301,7 +304,7 @@ where pub fn apply_consolidation_requests_contract_call( &mut self, evm: &mut Evm<'_, Ext, DB>, - ) -> Result, BlockExecutionError> + ) -> Result where DB: Database + DatabaseCommit, DB::Error: Display, diff --git a/crates/evm/src/test_utils.rs b/crates/evm/src/test_utils.rs index fc620bb42c33..c20f43dca9d4 100644 --- a/crates/evm/src/test_utils.rs +++ b/crates/evm/src/test_utils.rs @@ -2,15 +2,17 @@ use crate::{ execute::{ - BatchExecutor, BlockExecutionInput, BlockExecutionOutput, BlockExecutorProvider, Executor, + BasicBatchExecutor, BasicBlockExecutor, BatchExecutor, BlockExecutionInput, + BlockExecutionOutput, BlockExecutionStrategy, BlockExecutorProvider, Executor, }, system_calls::OnStateHook, }; +use alloy_eips::eip7685::Requests; use alloy_primitives::BlockNumber; use parking_lot::Mutex; use reth_execution_errors::BlockExecutionError; use reth_execution_types::ExecutionOutcome; -use reth_primitives::{BlockWithSenders, Receipt}; +use reth_primitives::{BlockWithSenders, Receipt, Receipts}; use reth_prune_types::PruneModes; use reth_storage_errors::provider::ProviderError; use revm::State; @@ -61,7 +63,10 @@ impl Executor for MockExecutorProvider { Ok(BlockExecutionOutput { state: bundle, receipts: receipts.into_iter().flatten().flatten().collect(), - requests: requests.into_iter().flatten().collect(), + requests: requests.into_iter().fold(Requests::default(), |mut reqs, req| { + reqs.extend(req); + reqs + }), gas_used: 0, }) } @@ -110,3 +115,50 @@ impl BatchExecutor for MockExecutorProvider { None } } + +impl BasicBlockExecutor +where + S: BlockExecutionStrategy, +{ + /// Provides safe read access to the state + pub fn with_state(&self, f: F) -> R + where + F: FnOnce(&State) -> R, + { + f(self.strategy.state_ref()) + } + + /// Provides safe write access to the state + pub fn with_state_mut(&mut self, f: F) -> R + where + F: FnOnce(&mut State) -> R, + { + f(self.strategy.state_mut()) + } +} + +impl BasicBatchExecutor +where + S: BlockExecutionStrategy, +{ + /// Provides safe read access to the state + pub fn with_state(&self, f: F) -> R + where + F: FnOnce(&State) -> R, + { + f(self.strategy.state_ref()) + } + + /// Provides safe write access to the state + pub fn with_state_mut(&mut self, f: F) -> R + where + F: FnOnce(&mut State) -> R, + { + f(self.strategy.state_mut()) + } + + /// Accessor for batch executor receipts. + pub const fn receipts(&self) -> &Receipts { + self.batch_record.receipts() + } +} diff --git a/crates/exex/exex/Cargo.toml b/crates/exex/exex/Cargo.toml index 6a3815e4045b..903e11e784ea 100644 --- a/crates/exex/exex/Cargo.toml +++ b/crates/exex/exex/Cargo.toml @@ -17,7 +17,10 @@ reth-chain-state.workspace = true reth-chainspec.workspace = true reth-config.workspace = true reth-evm.workspace = true -reth-exex-types = { workspace = true, features = ["serde", "serde-bincode-compat"] } +reth-exex-types = { workspace = true, features = [ + "serde", + "serde-bincode-compat", +] } reth-fs-util.workspace = true reth-metrics.workspace = true reth-node-api.workspace = true @@ -51,7 +54,6 @@ tracing.workspace = true [dev-dependencies] reth-blockchain-tree.workspace = true -reth-db-api.workspace = true reth-db-common.workspace = true reth-evm-ethereum.workspace = true reth-node-api.workspace = true @@ -68,4 +70,14 @@ tempfile.workspace = true [features] default = [] -serde = ["reth-provider/serde", "reth-exex-types/serde"] +serde = [ + "reth-provider/serde", + "reth-exex-types/serde", + "reth-revm/serde", + "alloy-consensus/serde", + "alloy-eips/serde", + "alloy-primitives/serde", + "parking_lot/serde", + "rand/serde", + "secp256k1/serde" +] diff --git a/crates/exex/exex/src/backfill/test_utils.rs b/crates/exex/exex/src/backfill/test_utils.rs index 1c793975c755..5eaf92bfefc8 100644 --- a/crates/exex/exex/src/backfill/test_utils.rs +++ b/crates/exex/exex/src/backfill/test_utils.rs @@ -10,7 +10,7 @@ use reth_evm::execute::{ }; use reth_evm_ethereum::execute::EthExecutorProvider; use reth_primitives::{ - constants::ETH_TO_WEI, Block, BlockBody, BlockWithSenders, Header, Receipt, Requests, + constants::ETH_TO_WEI, Block, BlockBody, BlockWithSenders, Header, Receipt, SealedBlockWithSenders, Transaction, }; use reth_provider::{ @@ -29,7 +29,7 @@ pub(crate) fn to_execution_outcome( bundle: block_execution_output.state.clone(), receipts: block_execution_output.receipts.clone().into(), first_block: block_number, - requests: vec![Requests(block_execution_output.requests.clone())], + requests: vec![block_execution_output.requests.clone()], } } diff --git a/crates/exex/exex/src/wal/storage.rs b/crates/exex/exex/src/wal/storage.rs index af3a590e5860..aaa4398fd0b6 100644 --- a/crates/exex/exex/src/wal/storage.rs +++ b/crates/exex/exex/src/wal/storage.rs @@ -9,6 +9,8 @@ use reth_exex_types::ExExNotification; use reth_tracing::tracing::debug; use tracing::instrument; +static FILE_EXTENSION: &str = "wal"; + /// The underlying WAL storage backed by a directory of files. /// /// Each notification is represented by a single file that contains a MessagePack-encoded @@ -29,7 +31,7 @@ impl Storage { } fn file_path(&self, id: u32) -> PathBuf { - self.path.join(format!("{id}.wal")) + self.path.join(format!("{id}.{FILE_EXTENSION}")) } fn parse_filename(filename: &str) -> eyre::Result { @@ -70,11 +72,14 @@ impl Storage { for entry in reth_fs_util::read_dir(&self.path)? { let entry = entry?; - let file_name = entry.file_name(); - let file_id = Self::parse_filename(&file_name.to_string_lossy())?; - min_id = min_id.map_or(Some(file_id), |min_id: u32| Some(min_id.min(file_id))); - max_id = max_id.map_or(Some(file_id), |max_id: u32| Some(max_id.max(file_id))); + if entry.path().extension() == Some(FILE_EXTENSION.as_ref()) { + let file_name = entry.file_name(); + let file_id = Self::parse_filename(&file_name.to_string_lossy())?; + + min_id = min_id.map_or(Some(file_id), |min_id: u32| Some(min_id.min(file_id))); + max_id = max_id.map_or(Some(file_id), |max_id: u32| Some(max_id.max(file_id))); + } } Ok(min_id.zip(max_id).map(|(min_id, max_id)| min_id..=max_id)) @@ -167,7 +172,7 @@ impl Storage { #[cfg(test)] mod tests { - use std::sync::Arc; + use std::{fs::File, sync::Arc}; use eyre::OptionExt; use reth_exex_types::ExExNotification; @@ -206,4 +211,24 @@ mod tests { Ok(()) } + + #[test] + fn test_files_range() -> eyre::Result<()> { + let temp_dir = tempfile::tempdir()?; + let storage = Storage::new(&temp_dir)?; + + // Create WAL files + File::create(storage.file_path(1))?; + File::create(storage.file_path(2))?; + File::create(storage.file_path(3))?; + + // Create non-WAL files that should be ignored + File::create(temp_dir.path().join("0.tmp"))?; + File::create(temp_dir.path().join("4.tmp"))?; + + // Check files range + assert_eq!(storage.files_range()?, Some(1..=3)); + + Ok(()) + } } diff --git a/crates/exex/test-utils/Cargo.toml b/crates/exex/test-utils/Cargo.toml index 8488cdb8b731..b850295a332b 100644 --- a/crates/exex/test-utils/Cargo.toml +++ b/crates/exex/test-utils/Cargo.toml @@ -31,7 +31,6 @@ reth-primitives.workspace = true reth-provider = { workspace = true, features = ["test-utils"] } reth-tasks.workspace = true reth-transaction-pool = { workspace = true, features = ["test-utils"] } -reth-ethereum-engine-primitives.workspace = true ## async futures-util.workspace = true diff --git a/crates/exex/test-utils/src/lib.rs b/crates/exex/test-utils/src/lib.rs index e219f55031db..9b86da7c77a5 100644 --- a/crates/exex/test-utils/src/lib.rs +++ b/crates/exex/test-utils/src/lib.rs @@ -24,7 +24,6 @@ use reth_db::{ DatabaseEnv, }; use reth_db_common::init::init_genesis; -use reth_ethereum_engine_primitives::EthereumEngineValidator; use reth_evm::test_utils::MockExecutorProvider; use reth_execution_types::Chain; use reth_exex::{ExExContext, ExExEvent, ExExNotification, ExExNotifications, Wal}; @@ -41,10 +40,7 @@ use reth_node_builder::{ }; use reth_node_core::node_config::NodeConfig; use reth_node_ethereum::{ - node::{ - EthereumAddOns, EthereumEngineValidatorBuilder, EthereumNetworkBuilder, - EthereumPayloadBuilder, - }, + node::{EthereumAddOns, EthereumNetworkBuilder, EthereumPayloadBuilder}, EthEngineTypes, EthEvmConfig, }; use reth_payload_builder::noop::NoopPayloadBuilderService; @@ -140,9 +136,10 @@ where EthereumNetworkBuilder, TestExecutorBuilder, TestConsensusBuilder, - EthereumEngineValidatorBuilder, >; - type AddOns = EthereumAddOns; + type AddOns = EthereumAddOns< + NodeAdapter>::Components>, + >; fn components_builder(&self) -> Self::ComponentsBuilder { ComponentsBuilder::default() @@ -152,7 +149,6 @@ where .network(EthereumNetworkBuilder::default()) .executor(TestExecutorBuilder::default()) .consensus(TestConsensusBuilder::default()) - .engine_validator(EthereumEngineValidatorBuilder::default()) } fn add_ons(&self) -> Self::AddOns { @@ -282,8 +278,6 @@ pub async fn test_exex_context_with_chain_spec( let tasks = TaskManager::current(); let task_executor = tasks.executor(); - let engine_validator = EthereumEngineValidator::new(chain_spec.clone()); - let components = NodeAdapter::, _>, _> { components: Components { transaction_pool, @@ -292,7 +286,6 @@ pub async fn test_exex_context_with_chain_spec( consensus, network, payload_builder, - engine_validator, }, task_executor, provider, diff --git a/crates/exex/types/Cargo.toml b/crates/exex/types/Cargo.toml index a146cbc22739..51097d6109c1 100644 --- a/crates/exex/types/Cargo.toml +++ b/crates/exex/types/Cargo.toml @@ -33,5 +33,16 @@ rand.workspace = true [features] default = [] -serde = ["dep:serde", "reth-execution-types/serde"] -serde-bincode-compat = ["reth-execution-types/serde-bincode-compat", "serde_with"] +serde = [ + "dep:serde", + "reth-execution-types/serde", + "alloy-eips/serde", + "alloy-primitives/serde", + "rand/serde" +] +serde-bincode-compat = [ + "reth-execution-types/serde-bincode-compat", + "serde_with", + "reth-primitives/serde-bincode-compat", + "alloy-eips/serde-bincode-compat" +] diff --git a/crates/net/discv4/Cargo.toml b/crates/net/discv4/Cargo.toml index fde652ef3978..f008d03b56fa 100644 --- a/crates/net/discv4/Cargo.toml +++ b/crates/net/discv4/Cargo.toml @@ -51,5 +51,14 @@ reth-tracing.workspace = true [features] default = ["serde"] -serde = ["dep:serde"] +serde = [ + "dep:serde", + "alloy-primitives/serde", + "discv5/serde", + "enr/serde", + "generic-array/serde", + "parking_lot/serde", + "rand?/serde", + "secp256k1/serde" +] test-utils = ["dep:rand"] diff --git a/crates/net/discv4/src/lib.rs b/crates/net/discv4/src/lib.rs index 7c14eac9b653..7963c6e6fd65 100644 --- a/crates/net/discv4/src/lib.rs +++ b/crates/net/discv4/src/lib.rs @@ -2324,9 +2324,9 @@ mod tests { let original = EnrForkIdEntry { fork_id: ForkId { hash: ForkHash([0xdc, 0xe9, 0x6c, 0x2d]), next: 0 }, }; - let mut encoded = Vec::new(); - original.encode(&mut encoded); let expected: [u8; 8] = [0xc7, 0xc6, 0x84, 0xdc, 0xe9, 0x6c, 0x2d, 0x80]; + let mut encoded = Vec::with_capacity(expected.len()); + original.encode(&mut encoded); assert_eq!(&expected[..], encoded.as_slice()); } diff --git a/crates/net/dns/Cargo.toml b/crates/net/dns/Cargo.toml index 2af72afcef65..a52f65057443 100644 --- a/crates/net/dns/Cargo.toml +++ b/crates/net/dns/Cargo.toml @@ -48,4 +48,15 @@ reth-tracing.workspace = true rand.workspace = true [features] -serde = ["dep:serde", "dep:serde_with"] +serde = [ + "dep:serde", + "dep:serde_with", + "alloy-chains/serde", + "alloy-primitives/serde", + "enr/serde", + "linked_hash_set/serde", + "parking_lot/serde", + "rand/serde", + "secp256k1/serde", + "trust-dns-resolver/serde" +] diff --git a/crates/net/downloaders/Cargo.toml b/crates/net/downloaders/Cargo.toml index 5e7f4dd47a27..272db6fc6d1c 100644 --- a/crates/net/downloaders/Cargo.toml +++ b/crates/net/downloaders/Cargo.toml @@ -71,10 +71,14 @@ tempfile.workspace = true [features] test-utils = [ - "dep:tempfile", - "dep:reth-db-api", - "reth-db/test-utils", - "reth-consensus/test-utils", - "reth-network-p2p/test-utils", - "reth-testing-utils", + "dep:tempfile", + "dep:reth-db-api", + "reth-db/test-utils", + "reth-consensus/test-utils", + "reth-network-p2p/test-utils", + "reth-testing-utils", + "reth-chainspec/test-utils", + "reth-primitives/test-utils", + "reth-db-api?/test-utils", + "reth-provider/test-utils" ] diff --git a/crates/net/eth-wire-types/Cargo.toml b/crates/net/eth-wire-types/Cargo.toml index 82c9fe37a44d..1d2b54872455 100644 --- a/crates/net/eth-wire-types/Cargo.toml +++ b/crates/net/eth-wire-types/Cargo.toml @@ -45,10 +45,22 @@ alloy-consensus.workspace = true [features] arbitrary = [ - "reth-primitives/arbitrary", - "alloy-chains/arbitrary", - "dep:arbitrary", - "dep:proptest", - "dep:proptest-arbitrary-interop", + "reth-primitives/arbitrary", + "alloy-chains/arbitrary", + "dep:arbitrary", + "dep:proptest", + "dep:proptest-arbitrary-interop", + "reth-chainspec/arbitrary", + "alloy-consensus/arbitrary", + "alloy-eips/arbitrary", + "alloy-primitives/arbitrary" +] +serde = [ + "dep:serde", + "alloy-chains/serde", + "alloy-consensus/serde", + "alloy-eips/serde", + "alloy-primitives/serde", + "bytes/serde", + "rand/serde" ] -serde = ["dep:serde"] diff --git a/crates/net/eth-wire-types/src/blocks.rs b/crates/net/eth-wire-types/src/blocks.rs index 6e5483f3a0e0..d60c63fc1f6e 100644 --- a/crates/net/eth-wire-types/src/blocks.rs +++ b/crates/net/eth-wire-types/src/blocks.rs @@ -277,7 +277,7 @@ mod tests { blob_gas_used: None, excess_blob_gas: None, parent_beacon_block_root: None, - requests_root: None + requests_hash: None }, ]), }.encode(&mut data); @@ -312,7 +312,7 @@ mod tests { blob_gas_used: None, excess_blob_gas: None, parent_beacon_block_root: None, - requests_root: None + requests_hash: None }, ]), }; @@ -412,11 +412,10 @@ mod tests { blob_gas_used: None, excess_blob_gas: None, parent_beacon_block_root: None, - requests_root: None + requests_hash: None }, ], withdrawals: None, - requests: None } ]), }; @@ -488,11 +487,10 @@ mod tests { blob_gas_used: None, excess_blob_gas: None, parent_beacon_block_root: None, - requests_root: None + requests_hash: None }, ], withdrawals: None, - requests: None } ]), }; diff --git a/crates/net/eth-wire-types/src/header.rs b/crates/net/eth-wire-types/src/header.rs index 4075a4a92fb7..8c11bfa82bb6 100644 --- a/crates/net/eth-wire-types/src/header.rs +++ b/crates/net/eth-wire-types/src/header.rs @@ -87,6 +87,7 @@ impl From for bool { #[cfg(test)] mod tests { use super::*; + use alloy_consensus::{EMPTY_OMMER_ROOT_HASH, EMPTY_ROOT_HASH}; use alloy_primitives::{address, b256, bloom, bytes, hex, Address, Bytes, B256, U256}; use alloy_rlp::{Decodable, Encodable}; use reth_primitives::Header; @@ -123,7 +124,7 @@ mod tests { .unwrap(); let header = Header { parent_hash: b256!("e0a94a7a3c9617401586b1a27025d2d9671332d22d540e0af72b069170380f2a"), - ommers_hash: b256!("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"), + ommers_hash: EMPTY_OMMER_ROOT_HASH, beneficiary: address!("ba5e000000000000000000000000000000000000"), state_root: b256!("ec3c94b18b8a1cff7d60f8d258ec723312932928626b4c9355eb4ab3568ec7f7"), transactions_root: b256!("50f738580ed699f0469702c7ccc63ed2e51bc034be9479b7bff4e68dee84accf"), @@ -142,7 +143,7 @@ mod tests { blob_gas_used: None, excess_blob_gas: None, parent_beacon_block_root: None, - requests_root: None + requests_hash: None }; assert_eq!(header.hash_slow(), expected_hash); } @@ -201,10 +202,7 @@ mod tests { gas_used: 0x0125b8, timestamp: 0x079e, extra_data: Bytes::from_str("42").unwrap(), - mix_hash: B256::from_str( - "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - ) - .unwrap(), + mix_hash: EMPTY_ROOT_HASH, base_fee_per_gas: Some(0x09), withdrawals_root: Some( B256::from_str("27f166f1d7c789251299535cb176ba34116e44894476a7886fe5d73d9be5c973") @@ -230,10 +228,7 @@ mod tests { "3a9b485972e7353edd9152712492f0c58d89ef80623686b6bf947a4a6dce6cb6", ) .unwrap(), - ommers_hash: B256::from_str( - "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - ) - .unwrap(), + ommers_hash: EMPTY_OMMER_ROOT_HASH, beneficiary: Address::from_str("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba").unwrap(), state_root: B256::from_str( "3c837fc158e3e93eafcaf2e658a02f5d8f99abc9f1c4c66cdea96c0ca26406ae", @@ -254,20 +249,14 @@ mod tests { gas_used: 0x02a865, timestamp: 0x079e, extra_data: Bytes::from(vec![0x42]), - mix_hash: B256::from_str( - "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - ) - .unwrap(), + mix_hash: EMPTY_ROOT_HASH, nonce: 0u64.into(), base_fee_per_gas: Some(9), - withdrawals_root: Some( - B256::from_str("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") - .unwrap(), - ), + withdrawals_root: Some(EMPTY_ROOT_HASH), blob_gas_used: Some(0x020000), excess_blob_gas: Some(0), parent_beacon_block_root: None, - requests_root: None, + requests_hash: None, }; let header = Header::decode(&mut data.as_slice()).unwrap(); @@ -288,15 +277,11 @@ mod tests { "13a7ec98912f917b3e804654e37c9866092043c13eb8eab94eb64818e886cff5", ) .unwrap(), - ommers_hash: b256!("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"), + ommers_hash: EMPTY_OMMER_ROOT_HASH, beneficiary: address!("f97e180c050e5ab072211ad2c213eb5aee4df134"), state_root: b256!("ec229dbe85b0d3643ad0f471e6ec1a36bbc87deffbbd970762d22a53b35d068a"), - transactions_root: b256!( - "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" - ), - receipts_root: b256!( - "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" - ), + transactions_root: EMPTY_ROOT_HASH, + receipts_root: EMPTY_ROOT_HASH, logs_bloom: Default::default(), difficulty: U256::from(0), number: 0x30598, @@ -307,13 +292,11 @@ mod tests { mix_hash: b256!("70ccadc40b16e2094954b1064749cc6fbac783c1712f1b271a8aac3eda2f2325"), nonce: 0u64.into(), base_fee_per_gas: Some(7), - withdrawals_root: Some(b256!( - "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" - )), + withdrawals_root: Some(EMPTY_ROOT_HASH), parent_beacon_block_root: None, blob_gas_used: Some(0), excess_blob_gas: Some(0x1600000), - requests_root: None, + requests_hash: None, }; let header = Header::decode(&mut data.as_slice()).unwrap(); diff --git a/crates/net/eth-wire-types/src/status.rs b/crates/net/eth-wire-types/src/status.rs index a5e7530ec09a..90e1731c90ab 100644 --- a/crates/net/eth-wire-types/src/status.rs +++ b/crates/net/eth-wire-types/src/status.rs @@ -138,10 +138,10 @@ impl Default for Status { /// /// # Example /// ``` +/// use alloy_consensus::constants::MAINNET_GENESIS_HASH; /// use alloy_primitives::{B256, U256}; /// use reth_chainspec::{Chain, EthereumHardfork, MAINNET}; /// use reth_eth_wire_types::{EthVersion, Status}; -/// use reth_primitives::MAINNET_GENESIS_HASH; /// /// // this is just an example status message! /// let status = Status::builder() @@ -216,6 +216,7 @@ impl StatusBuilder { #[cfg(test)] mod tests { use crate::{EthVersion, Status}; + use alloy_consensus::constants::MAINNET_GENESIS_HASH; use alloy_genesis::Genesis; use alloy_primitives::{hex, B256, U256}; use alloy_rlp::{Decodable, Encodable}; @@ -235,10 +236,7 @@ mod tests { "feb27336ca7923f8fab3bd617fcb6e75841538f71c1bcfc267d7838489d9e13d", ) .unwrap(), - genesis: B256::from_str( - "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3", - ) - .unwrap(), + genesis: MAINNET_GENESIS_HASH, forkid: ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 0 }, }; @@ -258,10 +256,7 @@ mod tests { "feb27336ca7923f8fab3bd617fcb6e75841538f71c1bcfc267d7838489d9e13d", ) .unwrap(), - genesis: B256::from_str( - "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3", - ) - .unwrap(), + genesis: MAINNET_GENESIS_HASH, forkid: ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 0 }, }; let status = Status::decode(&mut &data[..]).unwrap(); diff --git a/crates/net/eth-wire/Cargo.toml b/crates/net/eth-wire/Cargo.toml index 6eea4bc4ac65..b0e256fdf637 100644 --- a/crates/net/eth-wire/Cargo.toml +++ b/crates/net/eth-wire/Cargo.toml @@ -66,11 +66,22 @@ alloy-eips.workspace = true [features] arbitrary = [ - "reth-primitives/arbitrary", - "reth-eth-wire-types/arbitrary", - "dep:arbitrary", + "reth-primitives/arbitrary", + "reth-eth-wire-types/arbitrary", + "dep:arbitrary", + "reth-chainspec/arbitrary", + "alloy-eips/arbitrary", + "alloy-primitives/arbitrary" +] +serde = [ + "dep:serde", + "reth-eth-wire-types/serde", + "alloy-eips/serde", + "alloy-primitives/serde", + "bytes/serde", + "rand/serde", + "secp256k1/serde" ] -serde = ["dep:serde", "reth-eth-wire-types/serde"] [[test]] name = "fuzz_roundtrip" diff --git a/crates/net/network-api/Cargo.toml b/crates/net/network-api/Cargo.toml index 650d749048c4..6d410e9db23b 100644 --- a/crates/net/network-api/Cargo.toml +++ b/crates/net/network-api/Cargo.toml @@ -40,4 +40,10 @@ derive_more.workspace = true [features] default = ["serde"] -serde = ["dep:serde"] +serde = [ + "dep:serde", + "reth-eth-wire-types/serde", + "reth-network-types/serde", + "alloy-primitives/serde", + "enr/serde" +] diff --git a/crates/net/network-types/Cargo.toml b/crates/net/network-types/Cargo.toml index 97c8e65cbbc2..c9b8fdd5bf20 100644 --- a/crates/net/network-types/Cargo.toml +++ b/crates/net/network-types/Cargo.toml @@ -22,7 +22,7 @@ serde = { workspace = true, optional = true } humantime-serde = { workspace = true, optional = true } serde_json = { workspace = true } -# misc +# misc tracing.workspace = true [features] diff --git a/crates/net/network/Cargo.toml b/crates/net/network/Cargo.toml index 1d3af517af3f..f444aa7fe27c 100644 --- a/crates/net/network/Cargo.toml +++ b/crates/net/network/Cargo.toml @@ -101,8 +101,36 @@ criterion = { workspace = true, features = ["async_tokio", "html_reports"] } [features] default = ["serde"] geth-tests = [] -serde = ["dep:serde", "secp256k1/serde", "enr/serde", "reth-network-types/serde"] -test-utils = ["dep:reth-provider", "reth-provider?/test-utils", "dep:tempfile", "reth-transaction-pool/test-utils", "reth-network-types/test-utils"] +serde = [ + "dep:serde", + "secp256k1/serde", + "enr/serde", + "reth-network-types/serde", + "reth-dns-discovery/serde", + "reth-eth-wire/serde", + "reth-provider?/serde", + "alloy-consensus/serde", + "alloy-eips/serde", + "alloy-primitives/serde", + "discv5/serde", + "parking_lot/serde", + "rand/serde", + "smallvec/serde", + "url/serde" +] +test-utils = [ + "dep:reth-provider", + "reth-provider?/test-utils", + "dep:tempfile", + "reth-transaction-pool/test-utils", + "reth-network-types/test-utils", + "reth-chainspec/test-utils", + "reth-consensus/test-utils", + "reth-discv4/test-utils", + "reth-network/test-utils", + "reth-network-p2p/test-utils", + "reth-primitives/test-utils" +] [[bench]] name = "bench" diff --git a/crates/net/p2p/Cargo.toml b/crates/net/p2p/Cargo.toml index c43f7f5b347c..3b6d74c9dbeb 100644 --- a/crates/net/p2p/Cargo.toml +++ b/crates/net/p2p/Cargo.toml @@ -43,5 +43,15 @@ tokio = { workspace = true, features = ["full"] } [features] default = ["std"] -test-utils = ["reth-consensus/test-utils", "parking_lot"] -std = ["reth-consensus/std"] +test-utils = [ + "reth-consensus/test-utils", + "parking_lot", + "reth-network-types/test-utils", + "reth-primitives/test-utils" +] +std = [ + "reth-consensus/std", + "reth-primitives/std", + "alloy-eips/std", + "alloy-primitives/std" +] diff --git a/crates/net/p2p/src/full_block.rs b/crates/net/p2p/src/full_block.rs index 0116f1348810..e5129b686741 100644 --- a/crates/net/p2p/src/full_block.rs +++ b/crates/net/p2p/src/full_block.rs @@ -347,22 +347,6 @@ fn ensure_valid_body_response( _ => return Err(ConsensusError::WithdrawalsRootUnexpected), } - match (header.requests_root, &block.requests) { - (Some(header_requests_root), Some(requests)) => { - let requests = requests.0.as_slice(); - let requests_root = reth_primitives::proofs::calculate_requests_root(requests); - if requests_root != header_requests_root { - return Err(ConsensusError::BodyRequestsRootDiff( - GotExpected { got: requests_root, expected: header_requests_root }.into(), - )) - } - } - (None, None) => { - // this is ok because we assume the fork is not active in this case - } - _ => return Err(ConsensusError::RequestsRootUnexpected), - } - Ok(()) } diff --git a/crates/net/peers/src/lib.rs b/crates/net/peers/src/lib.rs index e80331f90468..1d60994d8e1b 100644 --- a/crates/net/peers/src/lib.rs +++ b/crates/net/peers/src/lib.rs @@ -117,6 +117,7 @@ pub enum AnyNode { impl AnyNode { /// Returns the peer id of the node. + #[allow(clippy::missing_const_for_fn)] pub fn peer_id(&self) -> PeerId { match self { Self::NodeRecord(record) => record.id, @@ -127,6 +128,7 @@ impl AnyNode { } /// Returns the full node record if available. + #[allow(clippy::missing_const_for_fn)] pub fn node_record(&self) -> Option { match self { Self::NodeRecord(record) => Some(*record), diff --git a/crates/node/api/Cargo.toml b/crates/node/api/Cargo.toml index e7685acc84fd..c2c3eb46326b 100644 --- a/crates/node/api/Cargo.toml +++ b/crates/node/api/Cargo.toml @@ -12,6 +12,8 @@ workspace = true [dependencies] # reth +reth-beacon-consensus.workspace = true +reth-consensus.workspace = true reth-evm.workspace = true reth-provider.workspace = true reth-engine-primitives.workspace = true @@ -23,3 +25,8 @@ reth-rpc-eth-api.workspace = true reth-network-api.workspace = true reth-node-types.workspace = true reth-primitives.workspace = true +reth-node-core.workspace = true + +alloy-rpc-types-engine.workspace = true + +eyre.workspace = true \ No newline at end of file diff --git a/crates/node/api/src/node.rs b/crates/node/api/src/node.rs index ce6b16c8ffcd..3173fd2b3985 100644 --- a/crates/node/api/src/node.rs +++ b/crates/node/api/src/node.rs @@ -1,14 +1,17 @@ //! Traits for configuring a node. -use std::marker::PhantomData; +use std::{future::Future, marker::PhantomData}; +use alloy_rpc_types_engine::JwtSecret; +use reth_beacon_consensus::BeaconConsensusEngineHandle; +use reth_consensus::Consensus; use reth_evm::execute::BlockExecutorProvider; use reth_network_api::FullNetwork; -use reth_node_types::{NodeTypesWithDB, NodeTypesWithEngine}; +use reth_node_core::node_config::NodeConfig; +use reth_node_types::{NodeTypes, NodeTypesWithDB, NodeTypesWithEngine}; use reth_payload_builder::PayloadBuilderHandle; use reth_primitives::Header; use reth_provider::FullProvider; -use reth_rpc_eth_api::EthApiTypes; use reth_tasks::TaskExecutor; use reth_transaction_pool::TransactionPool; @@ -54,6 +57,9 @@ pub trait FullNodeComponents: FullNodeTypes + Clone + 'static { /// The type that knows how to execute blocks. type Executor: BlockExecutorProvider; + /// The consensus type of the node. + type Consensus: Consensus + Clone + Unpin + 'static; + /// Network API. type Network: FullNetwork; @@ -66,8 +72,8 @@ pub trait FullNodeComponents: FullNodeTypes + Clone + 'static { /// Returns the node's executor type. fn block_executor(&self) -> &Self::Executor; - /// Returns the provider of the node. - fn provider(&self) -> &Self::Provider; + /// Returns the node's consensus type. + fn consensus(&self) -> &Self::Consensus; /// Returns the handle to the network fn network(&self) -> &Self::Network; @@ -77,37 +83,43 @@ pub trait FullNodeComponents: FullNodeTypes + Clone + 'static { &self, ) -> &PayloadBuilderHandle<::Engine>; + /// Returns the provider of the node. + fn provider(&self) -> &Self::Provider; + /// Returns handle to runtime. fn task_executor(&self) -> &TaskExecutor; } -/// Customizable node add-on types. -pub trait NodeAddOns: Send + Sync + Unpin + Clone + 'static { - /// The core `eth` namespace API type to install on the RPC server (see - /// `reth_rpc_eth_api::EthApiServer`). - type EthApi: EthApiTypes + Send + Clone; -} - -impl NodeAddOns for () { - type EthApi = (); +/// Context passed to [`NodeAddOns::launch_add_ons`], +#[derive(Debug)] +pub struct AddOnsContext<'a, N: FullNodeComponents> { + /// Node with all configured components. + pub node: &'a N, + /// Node configuration. + pub config: &'a NodeConfig<::ChainSpec>, + /// Handle to the beacon consensus engine. + pub beacon_engine_handle: + &'a BeaconConsensusEngineHandle<::Engine>, + /// JWT secret for the node. + pub jwt_secret: &'a JwtSecret, } -/// Returns the builder for type. -pub trait BuilderProvider: Send { - /// Context required to build type. - type Ctx<'a>; - - /// Returns builder for type. - #[allow(clippy::type_complexity)] - fn builder() -> Box Fn(Self::Ctx<'a>) -> Self + Send>; +/// Customizable node add-on types. +pub trait NodeAddOns: Send { + /// Handle to add-ons. + type Handle: Send + Sync + Clone; + + /// Configures and launches the add-ons. + fn launch_add_ons( + self, + ctx: AddOnsContext<'_, N>, + ) -> impl Future> + Send; } -impl BuilderProvider for () { - type Ctx<'a> = (); +impl NodeAddOns for () { + type Handle = (); - fn builder() -> Box Fn(Self::Ctx<'a>) -> Self + Send> { - Box::new(noop_builder) + async fn launch_add_ons(self, _components: AddOnsContext<'_, N>) -> eyre::Result { + Ok(()) } } - -const fn noop_builder(_: ()) {} diff --git a/crates/node/builder/Cargo.toml b/crates/node/builder/Cargo.toml index 53e53cd2b856..86f755cb9206 100644 --- a/crates/node/builder/Cargo.toml +++ b/crates/node/builder/Cargo.toml @@ -96,4 +96,21 @@ tempfile.workspace = true [features] default = [] -test-utils = ["reth-db/test-utils"] +test-utils = [ + "reth-db/test-utils", + "reth-blockchain-tree/test-utils", + "reth-chain-state/test-utils", + "reth-chainspec/test-utils", + "reth-consensus/test-utils", + "reth-engine-tree/test-utils", + "reth-evm/test-utils", + "reth-downloaders/test-utils", + "reth-network/test-utils", + "reth-network-p2p/test-utils", + "reth-payload-builder/test-utils", + "reth-primitives/test-utils", + "reth-stages/test-utils", + "reth-db-api/test-utils", + "reth-provider/test-utils", + "reth-transaction-pool/test-utils" +] diff --git a/crates/node/builder/src/builder/add_ons.rs b/crates/node/builder/src/builder/add_ons.rs index 26d7553bb86d..7be0411b2f01 100644 --- a/crates/node/builder/src/builder/add_ons.rs +++ b/crates/node/builder/src/builder/add_ons.rs @@ -1,8 +1,8 @@ //! Node add-ons. Depend on core [`NodeComponents`](crate::NodeComponents). -use reth_node_api::{EthApiTypes, FullNodeComponents, NodeAddOns}; +use reth_node_api::{FullNodeComponents, NodeAddOns}; -use crate::{exex::BoxedLaunchExEx, hooks::NodeHooks, rpc::RpcHooks}; +use crate::{exex::BoxedLaunchExEx, hooks::NodeHooks}; /// Additional node extensions. /// @@ -12,16 +12,6 @@ pub struct AddOns> { pub hooks: NodeHooks, /// The `ExExs` (execution extensions) of the node. 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 -/// required for launching the node, such as RPC. -#[derive(Default)] -pub struct RpcAddOns { - /// Additional RPC hooks. - pub hooks: RpcHooks, + pub add_ons: AddOns, } diff --git a/crates/node/builder/src/builder/mod.rs b/crates/node/builder/src/builder/mod.rs index fb94413fe756..82d8d96f6f55 100644 --- a/crates/node/builder/src/builder/mod.rs +++ b/crates/node/builder/src/builder/mod.rs @@ -13,7 +13,7 @@ use crate::{ common::WithConfigs, components::NodeComponentsBuilder, node::FullNode, - rpc::{EthApiBuilderProvider, RethRpcServerHandles, RpcContext}, + rpc::{RethRpcAddOns, RethRpcServerHandles, RpcContext}, DefaultNodeLauncher, LaunchNode, Node, NodeHandle, }; use futures::Future; @@ -37,7 +37,6 @@ use reth_node_core::{ dirs::{ChainPath, DataDirPath}, node_config::NodeConfig, primitives::Head, - rpc::eth::{helpers::AddDevSigners, FullEthApiServer}, }; use reth_primitives::revm_primitives::EnvKzgSettings; use reth_provider::{providers::BlockchainProvider, ChainSpecProvider, FullProvider}; @@ -358,19 +357,11 @@ where > where N: Node, ChainSpec = ChainSpec>, - N::AddOns: NodeAddOns< + N::AddOns: RethRpcAddOns< NodeAdapter< RethFullAdapter, >>::Components, >, - EthApi: EthApiBuilderProvider< - NodeAdapter< - RethFullAdapter, - >>::Components, - > - > - + FullEthApiServer - + AddDevSigners >, { self.node(node).launch().await @@ -418,7 +409,7 @@ impl WithLaunchContext> where T: FullNodeTypes, CB: NodeComponentsBuilder, - AO: NodeAddOns, EthApi: FullEthApiServer + AddDevSigners>, + AO: RethRpcAddOns>, { /// Returns a reference to the node builder's config. pub const fn config(&self) -> &NodeConfig<::ChainSpec> { @@ -466,6 +457,14 @@ where Self { builder: self.builder.on_node_started(hook), task_executor: self.task_executor } } + /// Modifies the addons with the given closure. + pub fn map_add_ons(self, f: F) -> Self + where + F: FnOnce(AO) -> AO, + { + Self { builder: self.builder.map_add_ons(f), task_executor: self.task_executor } + } + /// Sets the hook that is run once the rpc server is started. pub fn on_rpc_started(self, hook: F) -> Self where @@ -553,12 +552,7 @@ where DB: Database + DatabaseMetrics + DatabaseMetadata + Clone + Unpin + 'static, T: NodeTypesWithEngine, CB: NodeComponentsBuilder>, - AO: NodeAddOns< - NodeAdapter, CB::Components>, - EthApi: EthApiBuilderProvider, CB::Components>> - + FullEthApiServer - + AddDevSigners, - >, + AO: RethRpcAddOns, CB::Components>>, { /// Launches the node with the [`DefaultNodeLauncher`] that sets up engine API consensus and rpc pub async fn launch( diff --git a/crates/node/builder/src/builder/states.rs b/crates/node/builder/src/builder/states.rs index 80930ef743cd..e75a07802a6e 100644 --- a/crates/node/builder/src/builder/states.rs +++ b/crates/node/builder/src/builder/states.rs @@ -11,10 +11,7 @@ use reth_exex::ExExContext; use reth_node_api::{ FullNodeComponents, FullNodeTypes, NodeAddOns, NodeTypes, NodeTypesWithDB, NodeTypesWithEngine, }; -use reth_node_core::{ - node_config::NodeConfig, - rpc::eth::{helpers::AddDevSigners, FullEthApiServer}, -}; +use reth_node_core::node_config::NodeConfig; use reth_payload_builder::PayloadBuilderHandle; use reth_tasks::TaskExecutor; @@ -22,8 +19,8 @@ use crate::{ components::{NodeComponents, NodeComponentsBuilder}, hooks::NodeHooks, launch::LaunchNode, - rpc::{EthApiBuilderProvider, RethRpcServerHandles, RpcContext, RpcHooks}, - AddOns, FullNode, RpcAddOns, + rpc::{RethRpcAddOns, RethRpcServerHandles, RpcContext}, + AddOns, FullNode, }; /// A node builder that also has the configured types. @@ -54,12 +51,7 @@ impl NodeBuilderWithTypes { config, adapter, components_builder, - add_ons: AddOns { - hooks: NodeHooks::default(), - rpc: RpcAddOns { hooks: RpcHooks::default() }, - exexs: Vec::new(), - addons: (), - }, + add_ons: AddOns { hooks: NodeHooks::default(), exexs: Vec::new(), add_ons: () }, } } } @@ -83,8 +75,8 @@ impl fmt::Debug for NodeTypesAdapter { } } -/// Container for the node's types and the components and other internals that can be used by addons -/// of the node. +/// Container for the node's types and the components and other internals that can be used by +/// addons of the node. pub struct NodeAdapter> { /// The components of the node. pub components: C, @@ -104,6 +96,7 @@ impl> FullNodeComponents for NodeAdapter< type Evm = C::Evm; type Executor = C::Executor; type Network = C::Network; + type Consensus = C::Consensus; fn pool(&self) -> &Self::Pool { self.components.pool() @@ -132,6 +125,10 @@ impl> FullNodeComponents for NodeAdapter< fn task_executor(&self) -> &TaskExecutor { &self.task_executor } + + fn consensus(&self) -> &Self::Consensus { + self.components.consensus() + } } impl> Clone for NodeAdapter { @@ -169,7 +166,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, addons: AO) -> NodeBuilderWithComponents + pub fn with_add_ons(self, add_ons: AO) -> NodeBuilderWithComponents where AO: NodeAddOns>, { @@ -179,12 +176,7 @@ where config, adapter, components_builder, - add_ons: AddOns { - hooks: NodeHooks::default(), - rpc: RpcAddOns { hooks: RpcHooks::default() }, - exexs: Vec::new(), - addons, - }, + add_ons: AddOns { hooks: NodeHooks::default(), exexs: Vec::new(), add_ons }, } } } @@ -215,31 +207,6 @@ where self } - /// Sets the hook that is run once the rpc server is started. - pub fn on_rpc_started(mut self, hook: F) -> Self - where - F: FnOnce( - RpcContext<'_, NodeAdapter, AO::EthApi>, - RethRpcServerHandles, - ) -> eyre::Result<()> - + Send - + 'static, - { - self.add_ons.rpc.hooks.set_on_rpc_started(hook); - self - } - - /// Sets the hook that is run to configure the rpc modules. - pub fn extend_rpc_modules(mut self, hook: F) -> Self - where - F: FnOnce(RpcContext<'_, NodeAdapter, AO::EthApi>) -> eyre::Result<()> - + Send - + 'static, - { - self.add_ons.rpc.hooks.set_extend_rpc_modules(hook); - self - } - /// Installs an `ExEx` (Execution Extension) in the node. /// /// # Note @@ -269,18 +236,22 @@ where pub const fn check_launch(self) -> Self { self } + + /// Modifies the addons with the given closure. + pub fn map_add_ons(mut self, f: F) -> Self + where + F: FnOnce(AO) -> AO, + { + self.add_ons.add_ons = f(self.add_ons.add_ons); + self + } } impl NodeBuilderWithComponents where T: FullNodeTypes, CB: NodeComponentsBuilder, - AO: NodeAddOns< - NodeAdapter, - EthApi: EthApiBuilderProvider> - + FullEthApiServer - + AddDevSigners, - >, + AO: RethRpcAddOns>, { /// Launches the node with the given launcher. pub async fn launch_with(self, launcher: L) -> eyre::Result @@ -289,4 +260,33 @@ where { launcher.launch_node(self).await } + + /// Sets the hook that is run once the rpc server is started. + pub fn on_rpc_started(self, hook: F) -> Self + where + F: FnOnce( + RpcContext<'_, NodeAdapter, AO::EthApi>, + RethRpcServerHandles, + ) -> eyre::Result<()> + + Send + + 'static, + { + self.map_add_ons(|mut add_ons| { + add_ons.hooks_mut().set_on_rpc_started(hook); + add_ons + }) + } + + /// Sets the hook that is run to configure the rpc modules. + pub fn extend_rpc_modules(self, hook: F) -> Self + where + F: FnOnce(RpcContext<'_, NodeAdapter, AO::EthApi>) -> eyre::Result<()> + + Send + + 'static, + { + self.map_add_ons(|mut add_ons| { + add_ons.hooks_mut().set_extend_rpc_modules(hook); + add_ons + }) + } } diff --git a/crates/node/builder/src/components/builder.rs b/crates/node/builder/src/components/builder.rs index ab8e29929a92..48a0ba9b5fdf 100644 --- a/crates/node/builder/src/components/builder.rs +++ b/crates/node/builder/src/components/builder.rs @@ -4,7 +4,6 @@ use std::{future::Future, marker::PhantomData}; use reth_consensus::Consensus; use reth_evm::execute::BlockExecutorProvider; -use reth_node_api::{EngineValidator, NodeTypesWithEngine}; use reth_primitives::Header; use reth_transaction_pool::TransactionPool; @@ -16,8 +15,6 @@ use crate::{ BuilderContext, ConfigureEvm, FullNodeTypes, }; -use super::EngineValidatorBuilder; - /// A generic, general purpose and customizable [`NodeComponentsBuilder`] implementation. /// /// This type is stateful and captures the configuration of the node's components. @@ -38,23 +35,22 @@ use super::EngineValidatorBuilder; /// All component builders are captured in the builder state and will be consumed once the node is /// launched. #[derive(Debug)] -pub struct ComponentsBuilder { +pub struct ComponentsBuilder { pool_builder: PoolB, payload_builder: PayloadB, network_builder: NetworkB, executor_builder: ExecB, consensus_builder: ConsB, - engine_validator_builder: EVB, _marker: PhantomData, } -impl - ComponentsBuilder +impl + ComponentsBuilder { /// Configures the node types. pub fn node_types( self, - ) -> ComponentsBuilder + ) -> ComponentsBuilder where Types: FullNodeTypes, { @@ -64,7 +60,6 @@ impl network_builder, executor_builder: evm_builder, consensus_builder, - engine_validator_builder, _marker, } = self; ComponentsBuilder { @@ -73,7 +68,6 @@ impl payload_builder, network_builder, consensus_builder, - engine_validator_builder, _marker: Default::default(), } } @@ -86,7 +80,6 @@ impl network_builder: self.network_builder, executor_builder: self.executor_builder, consensus_builder: self.consensus_builder, - engine_validator_builder: self.engine_validator_builder, _marker: self._marker, } } @@ -99,7 +92,6 @@ impl network_builder: self.network_builder, executor_builder: self.executor_builder, consensus_builder: self.consensus_builder, - engine_validator_builder: self.engine_validator_builder, _marker: self._marker, } } @@ -112,7 +104,6 @@ impl network_builder: f(self.network_builder), executor_builder: self.executor_builder, consensus_builder: self.consensus_builder, - engine_validator_builder: self.engine_validator_builder, _marker: self._marker, } } @@ -125,7 +116,6 @@ impl network_builder: self.network_builder, executor_builder: f(self.executor_builder), consensus_builder: self.consensus_builder, - engine_validator_builder: self.engine_validator_builder, _marker: self._marker, } } @@ -138,14 +128,13 @@ impl network_builder: self.network_builder, executor_builder: self.executor_builder, consensus_builder: f(self.consensus_builder), - engine_validator_builder: self.engine_validator_builder, _marker: self._marker, } } } -impl - ComponentsBuilder +impl + ComponentsBuilder where Node: FullNodeTypes, { @@ -156,7 +145,7 @@ where pub fn pool( self, pool_builder: PB, - ) -> ComponentsBuilder + ) -> ComponentsBuilder where PB: PoolBuilder, { @@ -166,7 +155,6 @@ where network_builder, executor_builder: evm_builder, consensus_builder, - engine_validator_builder, _marker, } = self; ComponentsBuilder { @@ -175,14 +163,13 @@ where network_builder, executor_builder: evm_builder, consensus_builder, - engine_validator_builder, _marker, } } } -impl - ComponentsBuilder +impl + ComponentsBuilder where Node: FullNodeTypes, PoolB: PoolBuilder, @@ -194,7 +181,7 @@ where pub fn network( self, network_builder: NB, - ) -> ComponentsBuilder + ) -> ComponentsBuilder where NB: NetworkBuilder, { @@ -204,7 +191,6 @@ where network_builder: _, executor_builder: evm_builder, consensus_builder, - engine_validator_builder, _marker, } = self; ComponentsBuilder { @@ -213,7 +199,6 @@ where network_builder, executor_builder: evm_builder, consensus_builder, - engine_validator_builder, _marker, } } @@ -225,7 +210,7 @@ where pub fn payload( self, payload_builder: PB, - ) -> ComponentsBuilder + ) -> ComponentsBuilder where PB: PayloadServiceBuilder, { @@ -235,7 +220,6 @@ where network_builder, executor_builder: evm_builder, consensus_builder, - engine_validator_builder, _marker, } = self; ComponentsBuilder { @@ -244,7 +228,6 @@ where network_builder, executor_builder: evm_builder, consensus_builder, - engine_validator_builder, _marker, } } @@ -256,7 +239,7 @@ where pub fn executor( self, executor_builder: EB, - ) -> ComponentsBuilder + ) -> ComponentsBuilder where EB: ExecutorBuilder, { @@ -266,7 +249,6 @@ where network_builder, executor_builder: _, consensus_builder, - engine_validator_builder, _marker, } = self; ComponentsBuilder { @@ -275,7 +257,6 @@ where network_builder, executor_builder, consensus_builder, - engine_validator_builder, _marker, } } @@ -287,7 +268,7 @@ where pub fn consensus( self, consensus_builder: CB, - ) -> ComponentsBuilder + ) -> ComponentsBuilder where CB: ConsensusBuilder, { @@ -297,38 +278,7 @@ where network_builder, executor_builder, consensus_builder: _, - engine_validator_builder, - _marker, - } = self; - ComponentsBuilder { - pool_builder, - payload_builder, - network_builder, - executor_builder, - consensus_builder, - engine_validator_builder, - _marker, - } - } - /// Configures the consensus builder. - /// - /// This accepts a [`ConsensusBuilder`] instance that will be used to create the node's - /// components for consensus. - pub fn engine_validator( - self, - engine_validator_builder: EngineVB, - ) -> ComponentsBuilder - where - EngineVB: EngineValidatorBuilder, - { - let Self { - pool_builder, - payload_builder, - network_builder, - executor_builder, - consensus_builder, - engine_validator_builder: _, _marker, } = self; ComponentsBuilder { @@ -337,14 +287,13 @@ where network_builder, executor_builder, consensus_builder, - engine_validator_builder, _marker, } } } -impl NodeComponentsBuilder - for ComponentsBuilder +impl NodeComponentsBuilder + for ComponentsBuilder where Node: FullNodeTypes, PoolB: PoolBuilder, @@ -352,16 +301,8 @@ where PayloadB: PayloadServiceBuilder, ExecB: ExecutorBuilder, ConsB: ConsensusBuilder, - EVB: EngineValidatorBuilder, { - type Components = Components< - Node, - PoolB::Pool, - ExecB::EVM, - ExecB::Executor, - ConsB::Consensus, - EVB::Validator, - >; + type Components = Components; async fn build_components( self, @@ -373,7 +314,6 @@ where network_builder, executor_builder: evm_builder, consensus_builder, - engine_validator_builder, _marker, } = self; @@ -382,7 +322,6 @@ where let network = network_builder.build_network(context, pool.clone()).await?; let payload_builder = payload_builder.spawn_payload_service(context, pool.clone()).await?; let consensus = consensus_builder.build_consensus(context).await?; - let engine_validator = engine_validator_builder.build_validator(context).await?; Ok(Components { transaction_pool: pool, @@ -391,12 +330,11 @@ where payload_builder, executor, consensus, - engine_validator, }) } } -impl Default for ComponentsBuilder<(), (), (), (), (), (), ()> { +impl Default for ComponentsBuilder<(), (), (), (), (), ()> { fn default() -> Self { Self { pool_builder: (), @@ -404,7 +342,6 @@ impl Default for ComponentsBuilder<(), (), (), (), (), (), ()> { network_builder: (), executor_builder: (), consensus_builder: (), - engine_validator_builder: (), _marker: Default::default(), } } @@ -430,18 +367,17 @@ pub trait NodeComponentsBuilder: Send { ) -> impl Future> + Send; } -impl NodeComponentsBuilder for F +impl NodeComponentsBuilder for F where Node: FullNodeTypes, F: FnOnce(&BuilderContext) -> Fut + Send, - Fut: Future>> + Send, + Fut: Future>> + Send, Pool: TransactionPool + Unpin + 'static, EVM: ConfigureEvm
, Executor: BlockExecutorProvider, Cons: Consensus + Clone + Unpin + 'static, - Val: EngineValidator<::Engine> + Clone + Unpin + 'static, { - type Components = Components; + type Components = Components; fn build_components( self, diff --git a/crates/node/builder/src/components/engine.rs b/crates/node/builder/src/components/engine.rs deleted file mode 100644 index b3ee7cbbbf2f..000000000000 --- a/crates/node/builder/src/components/engine.rs +++ /dev/null @@ -1,38 +0,0 @@ -//! Consensus component for the node builder. -use reth_node_api::{EngineValidator, NodeTypesWithEngine}; - -use crate::{BuilderContext, FullNodeTypes}; -use std::future::Future; - -/// A type that knows how to build the engine validator. -pub trait EngineValidatorBuilder: Send { - /// The consensus implementation to build. - type Validator: EngineValidator<::Engine> - + Clone - + Unpin - + 'static; - - /// Creates the engine validator. - fn build_validator( - self, - ctx: &BuilderContext, - ) -> impl Future> + Send; -} - -impl EngineValidatorBuilder for F -where - Node: FullNodeTypes, - Validator: - EngineValidator<::Engine> + Clone + Unpin + 'static, - F: FnOnce(&BuilderContext) -> Fut + Send, - Fut: Future> + Send, -{ - type Validator = Validator; - - fn build_validator( - self, - ctx: &BuilderContext, - ) -> impl Future> { - self(ctx) - } -} diff --git a/crates/node/builder/src/components/mod.rs b/crates/node/builder/src/components/mod.rs index ff1646593edf..42001fc10056 100644 --- a/crates/node/builder/src/components/mod.rs +++ b/crates/node/builder/src/components/mod.rs @@ -9,7 +9,6 @@ mod builder; mod consensus; -mod engine; mod execute; mod network; mod payload; @@ -17,7 +16,6 @@ mod pool; pub use builder::*; pub use consensus::*; -pub use engine::*; pub use execute::*; pub use network::*; pub use payload::*; @@ -27,7 +25,7 @@ use reth_consensus::Consensus; use reth_evm::execute::BlockExecutorProvider; use reth_network::NetworkHandle; use reth_network_api::FullNetwork; -use reth_node_api::{EngineValidator, NodeTypesWithEngine}; +use reth_node_api::NodeTypesWithEngine; use reth_payload_builder::PayloadBuilderHandle; use reth_primitives::Header; use reth_transaction_pool::TransactionPool; @@ -55,9 +53,6 @@ pub trait NodeComponents: Clone + Unpin + Send + Sync + 'stati /// Network API. type Network: FullNetwork; - /// Validator for the engine API. - type EngineValidator: EngineValidator<::Engine>; - /// Returns the transaction pool of the node. fn pool(&self) -> &Self::Pool; @@ -75,16 +70,13 @@ pub trait NodeComponents: Clone + Unpin + Send + Sync + 'stati /// Returns the handle to the payload builder service. fn payload_builder(&self) -> &PayloadBuilderHandle<::Engine>; - - /// Returns the engine validator. - fn engine_validator(&self) -> &Self::EngineValidator; } /// All the components of the node. /// /// This provides access to all the components of the node. #[derive(Debug)] -pub struct Components { +pub struct Components { /// The transaction pool of the node. pub transaction_pool: Pool, /// The node's EVM configuration, defining settings for the Ethereum Virtual Machine. @@ -97,26 +89,22 @@ pub struct Components::Engine>, - /// The validator for the engine API. - pub engine_validator: Validator, } -impl NodeComponents - for Components +impl NodeComponents + for Components where Node: FullNodeTypes, Pool: TransactionPool + Unpin + 'static, EVM: ConfigureEvm
, Executor: BlockExecutorProvider, Cons: Consensus + Clone + Unpin + 'static, - Val: EngineValidator<::Engine> + Clone + Unpin + 'static, { type Pool = Pool; type Evm = EVM; type Executor = Executor; type Consensus = Cons; type Network = NetworkHandle; - type EngineValidator = Val; fn pool(&self) -> &Self::Pool { &self.transaction_pool @@ -143,21 +131,15 @@ where ) -> &PayloadBuilderHandle<::Engine> { &self.payload_builder } - - fn engine_validator(&self) -> &Self::EngineValidator { - &self.engine_validator - } } -impl Clone - for Components +impl Clone for Components where Node: FullNodeTypes, Pool: TransactionPool, EVM: ConfigureEvm
, Executor: BlockExecutorProvider, Cons: Consensus + Clone, - Val: EngineValidator<::Engine>, { fn clone(&self) -> Self { Self { @@ -167,7 +149,6 @@ where consensus: self.consensus.clone(), network: self.network.clone(), payload_builder: self.payload_builder.clone(), - engine_validator: self.engine_validator.clone(), } } } diff --git a/crates/node/builder/src/handle.rs b/crates/node/builder/src/handle.rs index c81aa9420dde..2997a8687afd 100644 --- a/crates/node/builder/src/handle.rs +++ b/crates/node/builder/src/handle.rs @@ -1,13 +1,13 @@ use std::fmt; -use reth_node_api::{FullNodeComponents, NodeAddOns}; +use reth_node_api::FullNodeComponents; use reth_node_core::exit::NodeExitFuture; -use crate::node::FullNode; +use crate::{node::FullNode, rpc::RethRpcAddOns}; /// A Handle to the launched node. #[must_use = "Needs to await the node exit future"] -pub struct NodeHandle> { +pub struct NodeHandle> { /// All node components. pub node: FullNode, /// The exit future of the node. @@ -17,7 +17,7 @@ pub struct NodeHandle> { impl NodeHandle where Node: FullNodeComponents, - AddOns: NodeAddOns, + AddOns: RethRpcAddOns, { /// Waits for the node to exit, if it was configured to exit. pub async fn wait_for_node_exit(self) -> eyre::Result<()> { @@ -28,7 +28,7 @@ where impl fmt::Debug for NodeHandle where Node: FullNodeComponents, - AddOns: NodeAddOns, + AddOns: RethRpcAddOns, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("NodeHandle") diff --git a/crates/node/builder/src/launch/common.rs b/crates/node/builder/src/launch/common.rs index 3e8f92e707c0..ac2339fa6cfc 100644 --- a/crates/node/builder/src/launch/common.rs +++ b/crates/node/builder/src/launch/common.rs @@ -1072,7 +1072,7 @@ mod tests { let node_config = NodeConfig { pruning: PruningArgs { full: true, - block_interval: 0, + block_interval: None, sender_recovery_full: false, sender_recovery_distance: None, sender_recovery_before: None, diff --git a/crates/node/builder/src/launch/engine.rs b/crates/node/builder/src/launch/engine.rs index f9e26f202fc4..3de651cdcd0b 100644 --- a/crates/node/builder/src/launch/engine.rs +++ b/crates/node/builder/src/launch/engine.rs @@ -1,6 +1,5 @@ //! Engine node related functionality. -use alloy_rpc_types::engine::ClientVersionV1; use futures::{future::Either, stream, stream_select, StreamExt}; use reth_beacon_consensus::{ hooks::{EngineHooks, StaticFileHook}, @@ -20,21 +19,17 @@ use reth_exex::ExExManagerHandle; use reth_network::{NetworkSyncUpdater, SyncState}; use reth_network_api::{BlockDownloaderProvider, NetworkEventListenerProvider}; use reth_node_api::{ - BuiltPayload, FullNodeTypes, NodeAddOns, NodeTypesWithEngine, PayloadAttributesBuilder, - PayloadTypes, + BuiltPayload, FullNodeTypes, NodeTypesWithEngine, PayloadAttributesBuilder, PayloadTypes, }; use reth_node_core::{ dirs::{ChainPath, DataDirPath}, exit::NodeExitFuture, primitives::Head, - rpc::eth::{helpers::AddDevSigners, FullEthApiServer}, - version::{CARGO_PKG_VERSION, CLIENT_CODE, NAME_CLIENT, VERGEN_GIT_SHA}, }; use reth_node_events::{cl::ConsensusLayerHealthEvents, node}; use reth_payload_primitives::PayloadBuilder; use reth_primitives::EthereumHardforks; use reth_provider::providers::{BlockchainProvider2, ProviderNodeTypes}; -use reth_rpc_engine_api::{capabilities::EngineCapabilities, EngineApi}; use reth_tasks::TaskExecutor; use reth_tokio_util::EventSender; use reth_tracing::tracing::{debug, error, info}; @@ -45,9 +40,9 @@ use tokio_stream::wrappers::UnboundedReceiverStream; use crate::{ common::{Attached, LaunchContextWith, WithConfigs}, hooks::NodeHooks, - rpc::{launch_rpc_servers, EthApiBuilderProvider}, + rpc::{RethRpcAddOns, RpcHandle}, setup::build_networked_pipeline, - AddOns, ExExLauncher, FullNode, LaunchContext, LaunchNode, NodeAdapter, + AddOns, AddOnsContext, ExExLauncher, FullNode, LaunchContext, LaunchNode, NodeAdapter, NodeBuilderWithComponents, NodeComponents, NodeComponentsBuilder, NodeHandle, NodeTypesAdapter, }; @@ -78,12 +73,7 @@ where Types: ProviderNodeTypes + NodeTypesWithEngine, T: FullNodeTypes>, CB: NodeComponentsBuilder, - AO: NodeAddOns< - NodeAdapter, - EthApi: EthApiBuilderProvider> - + FullEthApiServer - + AddDevSigners, - >, + AO: RethRpcAddOns>, LocalPayloadAttributesBuilder: PayloadAttributesBuilder< <::Engine as PayloadTypes>::PayloadAttributes, >, @@ -98,7 +88,7 @@ where let NodeBuilderWithComponents { adapter: NodeTypesAdapter { database }, components_builder, - add_ons: AddOns { hooks, rpc, exexs: installed_exex, .. }, + add_ons: AddOns { hooks, exexs: installed_exex, add_ons }, config, } = target; let NodeHooks { on_component_initialized, on_node_started, .. } = hooks; @@ -292,37 +282,18 @@ where ), ); - let client = ClientVersionV1 { - code: CLIENT_CODE, - name: NAME_CLIENT.to_string(), - version: CARGO_PKG_VERSION.to_string(), - commit: VERGEN_GIT_SHA.to_string(), - }; - let engine_api = EngineApi::new( - ctx.blockchain_db().clone(), - ctx.chain_spec(), - beacon_engine_handle, - ctx.components().payload_builder().clone().into(), - ctx.components().pool().clone(), - Box::new(ctx.task_executor().clone()), - client, - EngineCapabilities::default(), - ctx.components().engine_validator().clone(), - ); - info!(target: "reth::cli", "Engine API handler initialized"); - // extract the jwt secret from the args if possible let jwt_secret = ctx.auth_jwt_secret()?; - // Start RPC servers - let (rpc_server_handles, rpc_registry) = launch_rpc_servers( - ctx.node_adapter().clone(), - engine_api, - ctx.node_config(), - jwt_secret, - rpc, - ) - .await?; + let add_ons_ctx = AddOnsContext { + node: ctx.node_adapter(), + config: ctx.node_config(), + beacon_engine_handle: &beacon_engine_handle, + jwt_secret: &jwt_secret, + }; + + let RpcHandle { rpc_server_handles, rpc_registry } = + add_ons.launch_add_ons(add_ons_ctx).await?; // TODO: migrate to devmode with https://github.com/paradigmxyz/reth/issues/10104 if let Some(maybe_custom_etherscan_url) = ctx.node_config().debug.etherscan.clone() { @@ -442,13 +413,12 @@ where provider: ctx.node_adapter().provider.clone(), payload_builder: ctx.components().payload_builder().clone(), task_executor: ctx.task_executor().clone(), - rpc_server_handles, - rpc_registry, config: ctx.node_config().clone(), data_dir: ctx.data_dir().clone(), + add_ons_handle: RpcHandle { rpc_server_handles, rpc_registry }, }; // Notify on node started - on_node_started.on_event(full_node.clone())?; + on_node_started.on_event(FullNode::clone(&full_node))?; let handle = NodeHandle { node_exit_future: NodeExitFuture::new( diff --git a/crates/node/builder/src/launch/mod.rs b/crates/node/builder/src/launch/mod.rs index 3188cde4b157..36aa55541e00 100644 --- a/crates/node/builder/src/launch/mod.rs +++ b/crates/node/builder/src/launch/mod.rs @@ -12,7 +12,6 @@ pub use exex::ExExLauncher; use std::{future::Future, sync::Arc}; use alloy_primitives::utils::format_ether; -use alloy_rpc_types::engine::ClientVersionV1; use futures::{future::Either, stream, stream_select, StreamExt}; use reth_beacon_consensus::{ hooks::{EngineHooks, PruneHook, StaticFileHook}, @@ -25,17 +24,14 @@ use reth_engine_util::EngineMessageStreamExt; use reth_exex::ExExManagerHandle; use reth_network::{BlockDownloaderProvider, NetworkEventListenerProvider}; use reth_node_api::{ - FullNodeComponents, FullNodeTypes, NodeAddOns, NodeTypesWithDB, NodeTypesWithEngine, + AddOnsContext, FullNodeComponents, FullNodeTypes, NodeTypesWithDB, NodeTypesWithEngine, }; use reth_node_core::{ dirs::{ChainPath, DataDirPath}, exit::NodeExitFuture, - rpc::eth::{helpers::AddDevSigners, FullEthApiServer}, - version::{CARGO_PKG_VERSION, CLIENT_CODE, NAME_CLIENT, VERGEN_GIT_SHA}, }; use reth_node_events::{cl::ConsensusLayerHealthEvents, node}; use reth_provider::providers::BlockchainProvider; -use reth_rpc_engine_api::{capabilities::EngineCapabilities, EngineApi}; use reth_tasks::TaskExecutor; use reth_tracing::tracing::{debug, info}; use reth_transaction_pool::TransactionPool; @@ -47,19 +43,18 @@ use crate::{ components::{NodeComponents, NodeComponentsBuilder}, hooks::NodeHooks, node::FullNode, - rpc::EthApiBuilderProvider, + rpc::{RethRpcAddOns, RpcHandle}, AddOns, NodeBuilderWithComponents, NodeHandle, }; /// Alias for [`reth_rpc_eth_types::EthApiBuilderCtx`], adapter for [`FullNodeComponents`]. -pub type EthApiBuilderCtx = reth_rpc_eth_types::EthApiBuilderCtx< +pub type EthApiBuilderCtx = reth_rpc_eth_types::EthApiBuilderCtx< ::Provider, ::Pool, ::Evm, ::Network, TaskExecutor, ::Provider, - Eth, >; /// A general purpose trait that launches a new node of any kind. @@ -109,12 +104,7 @@ where Types: NodeTypesWithDB + NodeTypesWithEngine, T: FullNodeTypes, Types = Types>, CB: NodeComponentsBuilder, - AO: NodeAddOns< - NodeAdapter, - EthApi: EthApiBuilderProvider> - + FullEthApiServer - + AddDevSigners, - >, + AO: RethRpcAddOns>, { type Node = NodeHandle, AO>; @@ -126,7 +116,7 @@ where let NodeBuilderWithComponents { adapter: NodeTypesAdapter { database }, components_builder, - add_ons: AddOns { hooks, rpc, exexs: installed_exex, .. }, + add_ons: AddOns { hooks, exexs: installed_exex, add_ons }, config, } = target; let NodeHooks { on_component_initialized, on_node_started, .. } = hooks; @@ -336,42 +326,18 @@ where ), ); - let client = ClientVersionV1 { - code: CLIENT_CODE, - name: NAME_CLIENT.to_string(), - version: CARGO_PKG_VERSION.to_string(), - commit: VERGEN_GIT_SHA.to_string(), - }; - let engine_api = EngineApi::new( - ctx.blockchain_db().clone(), - ctx.chain_spec(), - beacon_engine_handle, - ctx.components().payload_builder().clone().into(), - ctx.components().pool().clone(), - Box::new(ctx.task_executor().clone()), - client, - EngineCapabilities::default(), - ctx.components().engine_validator().clone(), - ); - info!(target: "reth::cli", "Engine API handler initialized"); - // extract the jwt secret from the args if possible let jwt_secret = ctx.auth_jwt_secret()?; - // Start RPC servers - let (rpc_server_handles, rpc_registry) = crate::rpc::launch_rpc_servers( - ctx.node_adapter().clone(), - engine_api, - ctx.node_config(), - jwt_secret, - rpc, - ) - .await?; + let add_ons_ctx = AddOnsContext { + node: ctx.node_adapter(), + config: ctx.node_config(), + beacon_engine_handle: &beacon_engine_handle, + jwt_secret: &jwt_secret, + }; - // in dev mode we generate 20 random dev-signer accounts - if ctx.is_dev() { - rpc_registry.eth_api().with_dev_accounts(); - } + let RpcHandle { rpc_server_handles, rpc_registry } = + add_ons.launch_add_ons(add_ons_ctx).await?; // Run consensus engine to completion let (tx, rx) = oneshot::channel(); @@ -431,13 +397,12 @@ where provider: ctx.node_adapter().provider.clone(), payload_builder: ctx.components().payload_builder().clone(), task_executor: ctx.task_executor().clone(), - rpc_server_handles, - rpc_registry, config: ctx.node_config().clone(), data_dir: ctx.data_dir().clone(), + add_ons_handle: RpcHandle { rpc_server_handles, rpc_registry }, }; // Notify on node started - on_node_started.on_event(full_node.clone())?; + on_node_started.on_event(FullNode::clone(&full_node))?; let handle = NodeHandle { node_exit_future: NodeExitFuture::new( diff --git a/crates/node/builder/src/lib.rs b/crates/node/builder/src/lib.rs index cfe16074a59d..899317f158c6 100644 --- a/crates/node/builder/src/lib.rs +++ b/crates/node/builder/src/lib.rs @@ -20,10 +20,7 @@ pub mod components; pub use components::{NodeComponents, NodeComponentsBuilder}; mod builder; -pub use builder::{ - add_ons::{AddOns, RpcAddOns}, - *, -}; +pub use builder::{add_ons::AddOns, *}; mod launch; pub use launch::{engine::EngineNodeLauncher, *}; diff --git a/crates/node/builder/src/node.rs b/crates/node/builder/src/node.rs index 3a70c08c1031..3e3d5b696c39 100644 --- a/crates/node/builder/src/node.rs +++ b/crates/node/builder/src/node.rs @@ -1,7 +1,11 @@ // re-export the node api types pub use reth_node_api::{FullNodeTypes, NodeTypes, NodeTypesWithEngine}; -use std::{marker::PhantomData, sync::Arc}; +use std::{ + marker::PhantomData, + ops::{Deref, DerefMut}, + sync::Arc, +}; use reth_node_api::{EngineTypes, FullNodeComponents}; use reth_node_core::{ @@ -14,11 +18,7 @@ use reth_provider::ChainSpecProvider; use reth_rpc_builder::{auth::AuthServerHandle, RpcServerHandle}; use reth_tasks::TaskExecutor; -use crate::{ - components::NodeComponentsBuilder, - rpc::{RethRpcServerHandles, RpcRegistry}, - NodeAdapter, NodeAddOns, -}; +use crate::{components::NodeComponentsBuilder, rpc::RethRpcAddOns, NodeAdapter, NodeAddOns}; /// A [`crate::Node`] is a [`NodeTypesWithEngine`] that comes with preconfigured components. /// @@ -84,7 +84,7 @@ impl Node for AnyNode where N: FullNodeTypes + Clone, C: NodeComponentsBuilder + Clone + Sync + Unpin + 'static, - AO: NodeAddOns>, + AO: NodeAddOns> + Clone + Sync + Unpin + 'static, { type ComponentsBuilder = C; type AddOns = AO; @@ -117,14 +117,12 @@ pub struct FullNode> { pub payload_builder: PayloadBuilderHandle<::Engine>, /// Task executor for the node. pub task_executor: TaskExecutor, - /// Handles to the node's rpc servers - pub rpc_server_handles: RethRpcServerHandles, - /// The configured rpc namespaces - pub rpc_registry: RpcRegistry, /// The initial node config. pub config: NodeConfig<::ChainSpec>, /// The data dir of the node. pub data_dir: ChainPath, + /// The handle to launched add-ons + pub add_ons_handle: AddOns::Handle, } impl> Clone for FullNode { @@ -137,10 +135,9 @@ impl> Clone for FullNode Arc<::ChainSpec> { self.provider.chain_spec() } +} +impl FullNode +where + Engine: EngineTypes, + Node: FullNodeComponents>, + AddOns: RethRpcAddOns, +{ /// Returns the [`RpcServerHandle`] to the started rpc server. pub const fn rpc_server_handle(&self) -> &RpcServerHandle { - &self.rpc_server_handles.rpc + &self.add_ons_handle.rpc_server_handles.rpc } /// Returns the [`AuthServerHandle`] to the started authenticated engine API server. pub const fn auth_server_handle(&self) -> &AuthServerHandle { - &self.rpc_server_handles.auth + &self.add_ons_handle.rpc_server_handles.auth } /// Returns the [`EngineApiClient`] interface for the authenticated engine API. @@ -188,3 +192,17 @@ where self.auth_server_handle().ipc_client().await } } + +impl> Deref for FullNode { + type Target = AddOns::Handle; + + fn deref(&self) -> &Self::Target { + &self.add_ons_handle + } +} + +impl> DerefMut for FullNode { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.add_ons_handle + } +} diff --git a/crates/node/builder/src/rpc.rs b/crates/node/builder/src/rpc.rs index cb8d8f355dac..18293118dc66 100644 --- a/crates/node/builder/src/rpc.rs +++ b/crates/node/builder/src/rpc.rs @@ -1,31 +1,36 @@ //! Builder support for rpc components. use std::{ - fmt, + fmt::{self, Debug}, + future::Future, + marker::PhantomData, ops::{Deref, DerefMut}, }; +use alloy_rpc_types::engine::ClientVersionV1; use futures::TryFutureExt; -use reth_node_api::{BuilderProvider, FullNodeComponents, NodeTypes, NodeTypesWithEngine}; +use reth_node_api::{ + AddOnsContext, EngineValidator, FullNodeComponents, NodeAddOns, NodeTypes, NodeTypesWithEngine, +}; use reth_node_core::{ node_config::NodeConfig, - rpc::{ - api::EngineApiServer, - eth::{EthApiTypes, FullEthApiServer}, - }, + rpc::eth::{EthApiTypes, FullEthApiServer}, + version::{CARGO_PKG_VERSION, CLIENT_CODE, NAME_CLIENT, VERGEN_GIT_SHA}, }; use reth_payload_builder::PayloadBuilderHandle; use reth_provider::providers::ProviderNodeTypes; +use reth_rpc::EthApi; +use reth_rpc_api::eth::helpers::AddDevSigners; use reth_rpc_builder::{ auth::{AuthRpcModule, AuthServerHandle}, config::RethRpcServerConfig, RpcModuleBuilder, RpcRegistryInner, RpcServerHandle, TransportRpcModules, }; -use reth_rpc_layer::JwtSecret; +use reth_rpc_engine_api::{capabilities::EngineCapabilities, EngineApi}; use reth_tasks::TaskExecutor; use reth_tracing::tracing::{debug, info}; -use crate::{EthApiBuilderCtx, RpcAddOns}; +use crate::EthApiBuilderCtx; /// Contains the handles to the spawned RPC servers. /// @@ -292,102 +297,277 @@ where } } -/// Launch the rpc servers. -pub async fn launch_rpc_servers( - node: Node, - engine_api: Engine, - config: &NodeConfig<::ChainSpec>, - jwt_secret: JwtSecret, - add_ons: RpcAddOns, -) -> eyre::Result<(RethRpcServerHandles, RpcRegistry)> +/// Handle to the launched RPC servers. +#[derive(Clone)] +pub struct RpcHandle { + /// Handles to launched servers. + pub rpc_server_handles: RethRpcServerHandles, + /// Configured RPC modules. + pub rpc_registry: RpcRegistry, +} + +impl Deref for RpcHandle { + type Target = RpcRegistry; + + fn deref(&self) -> &Self::Target { + &self.rpc_registry + } +} + +impl Debug for RpcHandle where - Node: FullNodeComponents + Clone, - Engine: EngineApiServer<::Engine>, - EthApi: EthApiBuilderProvider + FullEthApiServer, + RpcRegistry: Debug, { - let auth_config = config.rpc.auth_server_config(jwt_secret)?; - let module_config = config.rpc.transport_rpc_module_config(); - debug!(target: "reth::cli", http=?module_config.http(), ws=?module_config.ws(), "Using RPC module config"); - - let (mut modules, mut auth_module, registry) = RpcModuleBuilder::default() - .with_provider(node.provider().clone()) - .with_pool(node.pool().clone()) - .with_network(node.network().clone()) - .with_events(node.provider().clone()) - .with_executor(node.task_executor().clone()) - .with_evm_config(node.evm_config().clone()) - .with_block_executor(node.block_executor().clone()) - .build_with_auth_server(module_config, engine_api, EthApi::eth_api_builder()); - - let mut registry = RpcRegistry { registry }; - let ctx = RpcContext { - node: node.clone(), - config, - registry: &mut registry, - modules: &mut modules, - auth_module: &mut auth_module, - }; - - let RpcAddOns { hooks, .. } = add_ons; - let RpcHooks { on_rpc_started, extend_rpc_modules } = hooks; - - extend_rpc_modules.extend_rpc_modules(ctx)?; - - let server_config = config.rpc.rpc_server_config(); - let cloned_modules = modules.clone(); - let launch_rpc = server_config.start(&cloned_modules).map_ok(|handle| { - if let Some(path) = handle.ipc_endpoint() { - info!(target: "reth::cli", %path, "RPC IPC server started"); - } - if let Some(addr) = handle.http_local_addr() { - info!(target: "reth::cli", url=%addr, "RPC HTTP server started"); - } - if let Some(addr) = handle.ws_local_addr() { - info!(target: "reth::cli", url=%addr, "RPC WS server started"); + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RpcHandle") + .field("rpc_server_handles", &self.rpc_server_handles) + .field("rpc_registry", &self.rpc_registry) + .finish() + } +} + +/// Node add-ons containing RPC server configuration, with customizable eth API handler. +#[allow(clippy::type_complexity)] +pub struct RpcAddOns { + /// Additional RPC add-ons. + pub hooks: RpcHooks, + /// Builder for `EthApi` + eth_api_builder: Box) -> EthApi + Send + Sync>, + /// Engine validator + engine_validator_builder: EV, + _pd: PhantomData<(Node, EthApi)>, +} + +impl Debug + for RpcAddOns +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RpcAddOns") + .field("hooks", &self.hooks) + .field("eth_api_builder", &"...") + .field("engine_validator_builder", &self.engine_validator_builder) + .finish() + } +} + +impl RpcAddOns { + /// Creates a new instance of the RPC add-ons. + pub fn new( + eth_api_builder: impl FnOnce(&EthApiBuilderCtx) -> EthApi + Send + Sync + 'static, + engine_validator_builder: EV, + ) -> Self { + Self { + hooks: RpcHooks::default(), + eth_api_builder: Box::new(eth_api_builder), + engine_validator_builder, + _pd: PhantomData, } - handle - }); - - let launch_auth = auth_module.clone().start_server(auth_config).map_ok(|handle| { - let addr = handle.local_addr(); - if let Some(ipc_endpoint) = handle.ipc_endpoint() { - info!(target: "reth::cli", url=%addr, ipc_endpoint=%ipc_endpoint,"RPC auth server started"); - } else { - info!(target: "reth::cli", url=%addr, "RPC auth server started"); + } + + /// Sets the hook that is run once the rpc server is started. + pub fn on_rpc_started(mut self, hook: F) -> Self + where + F: FnOnce(RpcContext<'_, Node, EthApi>, RethRpcServerHandles) -> eyre::Result<()> + + Send + + 'static, + { + self.hooks.set_on_rpc_started(hook); + self + } + + /// Sets the hook that is run to configure the rpc modules. + pub fn extend_rpc_modules(mut self, hook: F) -> Self + where + F: FnOnce(RpcContext<'_, Node, EthApi>) -> eyre::Result<()> + Send + 'static, + { + self.hooks.set_extend_rpc_modules(hook); + self + } +} + +impl Default for RpcAddOns +where + Node: FullNodeComponents, + EthApi: EthApiTypes + EthApiBuilder, + EV: Default, +{ + fn default() -> Self { + Self::new(EthApi::build, EV::default()) + } +} + +impl NodeAddOns for RpcAddOns +where + N: FullNodeComponents, + EthApi: EthApiTypes + FullEthApiServer + AddDevSigners + Unpin + 'static, + EV: EngineValidatorBuilder, +{ + type Handle = RpcHandle; + + async fn launch_add_ons(self, ctx: AddOnsContext<'_, N>) -> eyre::Result { + let AddOnsContext { node, config, beacon_engine_handle, jwt_secret } = ctx; + let Self { eth_api_builder, engine_validator_builder, hooks, _pd: _ } = self; + + let client = ClientVersionV1 { + code: CLIENT_CODE, + name: NAME_CLIENT.to_string(), + version: CARGO_PKG_VERSION.to_string(), + commit: VERGEN_GIT_SHA.to_string(), + }; + + let engine_api = EngineApi::new( + node.provider().clone(), + config.chain.clone(), + beacon_engine_handle.clone(), + node.payload_builder().clone().into(), + node.pool().clone(), + Box::new(node.task_executor().clone()), + client, + EngineCapabilities::default(), + engine_validator_builder.build(&ctx).await?, + ); + info!(target: "reth::cli", "Engine API handler initialized"); + + let auth_config = config.rpc.auth_server_config(*jwt_secret)?; + let module_config = config.rpc.transport_rpc_module_config(); + debug!(target: "reth::cli", http=?module_config.http(), ws=?module_config.ws(), "Using RPC module config"); + + let (mut modules, mut auth_module, registry) = RpcModuleBuilder::default() + .with_provider(node.provider().clone()) + .with_pool(node.pool().clone()) + .with_network(node.network().clone()) + .with_events(node.provider().clone()) + .with_executor(node.task_executor().clone()) + .with_evm_config(node.evm_config().clone()) + .with_block_executor(node.block_executor().clone()) + .build_with_auth_server(module_config, engine_api, eth_api_builder); + + // in dev mode we generate 20 random dev-signer accounts + if config.dev.dev { + registry.eth_api().with_dev_accounts(); } - handle - }); - // launch servers concurrently - let (rpc, auth) = futures::future::try_join(launch_rpc, launch_auth).await?; - let handles = RethRpcServerHandles { rpc, auth }; + let mut registry = RpcRegistry { registry }; + let ctx = RpcContext { + node: node.clone(), + config, + registry: &mut registry, + modules: &mut modules, + auth_module: &mut auth_module, + }; + + let RpcHooks { on_rpc_started, extend_rpc_modules } = hooks; + + extend_rpc_modules.extend_rpc_modules(ctx)?; + + let server_config = config.rpc.rpc_server_config(); + let cloned_modules = modules.clone(); + let launch_rpc = server_config.start(&cloned_modules).map_ok(|handle| { + if let Some(path) = handle.ipc_endpoint() { + info!(target: "reth::cli", %path, "RPC IPC server started"); + } + if let Some(addr) = handle.http_local_addr() { + info!(target: "reth::cli", url=%addr, "RPC HTTP server started"); + } + if let Some(addr) = handle.ws_local_addr() { + info!(target: "reth::cli", url=%addr, "RPC WS server started"); + } + handle + }); + + let launch_auth = auth_module.clone().start_server(auth_config).map_ok(|handle| { + let addr = handle.local_addr(); + if let Some(ipc_endpoint) = handle.ipc_endpoint() { + info!(target: "reth::cli", url=%addr, ipc_endpoint=%ipc_endpoint,"RPC auth server started"); + } else { + info!(target: "reth::cli", url=%addr, "RPC auth server started"); + } + handle + }); + + // launch servers concurrently + let (rpc, auth) = futures::future::try_join(launch_rpc, launch_auth).await?; + + let handles = RethRpcServerHandles { rpc, auth }; + + let ctx = RpcContext { + node: node.clone(), + config, + registry: &mut registry, + modules: &mut modules, + auth_module: &mut auth_module, + }; + + on_rpc_started.on_rpc_started(ctx, handles.clone())?; + + Ok(RpcHandle { rpc_server_handles: handles, rpc_registry: registry }) + } +} + +/// Helper trait implemented for add-ons producing [`RpcHandle`]. Used by common node launcher +/// implementations. +pub trait RethRpcAddOns: + NodeAddOns> +{ + /// eth API implementation. + type EthApi: EthApiTypes; - let ctx = RpcContext { - node, - config, - registry: &mut registry, - modules: &mut modules, - auth_module: &mut auth_module, - }; + /// Returns a mutable reference to RPC hooks. + fn hooks_mut(&mut self) -> &mut RpcHooks; +} - on_rpc_started.on_rpc_started(ctx, handles.clone())?; +impl RethRpcAddOns for RpcAddOns +where + Self: NodeAddOns>, +{ + type EthApi = EthApi; - Ok((handles, registry)) + fn hooks_mut(&mut self) -> &mut RpcHooks { + &mut self.hooks + } } -/// Provides builder for the core `eth` API type. -pub trait EthApiBuilderProvider: BuilderProvider + EthApiTypes { - /// Returns the eth api builder. - #[allow(clippy::type_complexity)] - fn eth_api_builder() -> Box) -> Self + Send>; +/// A `EthApi` that knows how to build itself from [`EthApiBuilderCtx`]. +pub trait EthApiBuilder: 'static { + /// Builds the `EthApi` from the given context. + fn build(ctx: &EthApiBuilderCtx) -> Self; } -impl EthApiBuilderProvider for F +impl EthApiBuilder for EthApi { + fn build(ctx: &EthApiBuilderCtx) -> Self { + Self::with_spawner(ctx) + } +} + +/// A type that knows how to build the engine validator. +pub trait EngineValidatorBuilder: Send { + /// The consensus implementation to build. + type Validator: EngineValidator<::Engine> + + Clone + + Unpin + + 'static; + + /// Creates the engine validator. + fn build( + self, + ctx: &AddOnsContext<'_, Node>, + ) -> impl Future> + Send; +} + +impl EngineValidatorBuilder for F where - N: FullNodeComponents, - for<'a> F: BuilderProvider = &'a EthApiBuilderCtx> + EthApiTypes, + Node: FullNodeComponents, + Validator: + EngineValidator<::Engine> + Clone + Unpin + 'static, + F: FnOnce(&AddOnsContext<'_, Node>) -> Fut + Send, + Fut: Future> + Send, { - fn eth_api_builder() -> Box) -> Self + Send> { - F::builder() + type Validator = Validator; + + fn build( + self, + ctx: &AddOnsContext<'_, Node>, + ) -> impl Future> { + self(ctx) } } diff --git a/crates/node/core/Cargo.toml b/crates/node/core/Cargo.toml index 3ac90a888705..73c552f4d7a8 100644 --- a/crates/node/core/Cargo.toml +++ b/crates/node/core/Cargo.toml @@ -39,6 +39,7 @@ reth-stages-types.workspace = true # ethereum alloy-primitives.workspace = true alloy-rpc-types-engine = { workspace = true, features = ["jwt"] } +alloy-consensus.workspace = true # misc eyre.workspace = true @@ -73,15 +74,20 @@ futures.workspace = true # test vectors generation proptest.workspace = true tokio.workspace = true -tempfile.workspace = true [features] optimism = [ - "reth-primitives/optimism" + "reth-primitives/optimism", + "reth-db/optimism" ] # Features for vergen to generate correct env vars -jemalloc = [] -asm-keccak = [] +jemalloc = [ + "reth-cli-util/jemalloc" +] +asm-keccak = [ + "reth-primitives/asm-keccak", + "alloy-primitives/asm-keccak" +] [build-dependencies] vergen = { version = "8.0.0", features = ["build", "cargo", "git", "gitcl"] } diff --git a/crates/node/core/src/args/database.rs b/crates/node/core/src/args/database.rs index 09e9de82f52d..da96deb70c1b 100644 --- a/crates/node/core/src/args/database.rs +++ b/crates/node/core/src/args/database.rs @@ -6,6 +6,7 @@ use clap::{ error::ErrorKind, Arg, Args, Command, Error, }; +use reth_db::ClientVersion; use reth_storage_errors::db::LogLevel; /// Parameters for database configuration @@ -24,7 +25,15 @@ pub struct DatabaseArgs { impl DatabaseArgs { /// Returns default database arguments with configured log level and client version. pub fn database_args(&self) -> reth_db::mdbx::DatabaseArguments { - reth_db::mdbx::DatabaseArguments::new(default_client_version()) + self.get_database_args(default_client_version()) + } + + /// Returns the database arguments with configured log level and given client version. + pub const fn get_database_args( + &self, + client_version: ClientVersion, + ) -> reth_db::mdbx::DatabaseArguments { + reth_db::mdbx::DatabaseArguments::new(client_version) .with_log_level(self.log_level) .with_exclusive(self.exclusive) } diff --git a/crates/node/core/src/args/payload_builder.rs b/crates/node/core/src/args/payload_builder.rs index aec35253af26..4a18fd5b0b7d 100644 --- a/crates/node/core/src/args/payload_builder.rs +++ b/crates/node/core/src/args/payload_builder.rs @@ -1,12 +1,11 @@ use crate::{cli::config::PayloadBuilderConfig, version::default_extradata}; +use alloy_consensus::constants::MAXIMUM_EXTRA_DATA_SIZE; use clap::{ builder::{RangedU64ValueParser, TypedValueParser}, Arg, Args, Command, }; use reth_cli_util::{parse_duration_from_secs, parse_duration_from_secs_or_ms}; -use reth_primitives::constants::{ - ETHEREUM_BLOCK_GAS_LIMIT, MAXIMUM_EXTRA_DATA_SIZE, SLOT_DURATION, -}; +use reth_primitives::constants::{ETHEREUM_BLOCK_GAS_LIMIT, SLOT_DURATION}; use std::{borrow::Cow, ffi::OsStr, time::Duration}; /// Parameters for configuring the Payload Builder diff --git a/crates/node/core/src/args/pruning.rs b/crates/node/core/src/args/pruning.rs index 2bee5ec164fe..cd852b9e1685 100644 --- a/crates/node/core/src/args/pruning.rs +++ b/crates/node/core/src/args/pruning.rs @@ -2,7 +2,7 @@ use crate::args::error::ReceiptsLogError; use alloy_primitives::{Address, BlockNumber}; -use clap::Args; +use clap::{builder::RangedU64ValueParser, Args}; use reth_chainspec::EthChainSpec; use reth_config::config::PruneConfig; use reth_prune_types::{PruneMode, PruneModes, ReceiptsLogPruneConfig, MINIMUM_PRUNING_DISTANCE}; @@ -17,8 +17,8 @@ pub struct PruningArgs { pub full: bool, /// Minimum pruning interval measured in blocks. - #[arg(long, default_value_t = 0)] - pub block_interval: u64, + #[arg(long, value_parser = RangedU64ValueParser::::new().range(1..),)] + pub block_interval: Option, // Sender Recovery /// Prunes all sender recovery data. @@ -99,7 +99,7 @@ impl PruningArgs { // If --full is set, use full node defaults. if self.full { config = PruneConfig { - block_interval: 5, + block_interval: config.block_interval, segments: PruneModes { sender_recovery: Some(PruneMode::Full), transaction_lookup: None, @@ -123,6 +123,9 @@ impl PruningArgs { } // Override with any explicitly set prune.* flags. + if let Some(block_interval) = self.block_interval { + config.block_interval = block_interval as usize; + } if let Some(mode) = self.sender_recovery_prune_mode() { config.segments.sender_recovery = Some(mode); } diff --git a/crates/node/core/src/args/rpc_server.rs b/crates/node/core/src/args/rpc_server.rs index 15771e9897ef..382f22d37764 100644 --- a/crates/node/core/src/args/rpc_server.rs +++ b/crates/node/core/src/args/rpc_server.rs @@ -135,6 +135,11 @@ pub struct RpcServerArgs { pub rpc_max_connections: MaxU32, /// Maximum number of concurrent tracing requests. + /// + /// By default this chooses a sensible value based on the number of available cores. + /// Tracing requests are generally CPU bound. + /// Choosing a value that is higher than the available CPU cores can have a negative impact on + /// the performance of the node and affect the node's ability to maintain sync. #[arg(long = "rpc.max-tracing-requests", alias = "rpc-max-tracing-requests", value_name = "COUNT", default_value_t = constants::default_max_tracing_requests())] pub rpc_max_tracing_requests: usize, diff --git a/crates/node/metrics/Cargo.toml b/crates/node/metrics/Cargo.toml index 76a3a7f66329..9efdbd4959db 100644 --- a/crates/node/metrics/Cargo.toml +++ b/crates/node/metrics/Cargo.toml @@ -35,7 +35,6 @@ procfs = "0.16.0" [dev-dependencies] reqwest.workspace = true -reth-chainspec.workspace = true socket2 = { version = "0.5", default-features = false } reth-provider = { workspace = true, features = ["test-utils"] } diff --git a/crates/node/types/Cargo.toml b/crates/node/types/Cargo.toml index f04925d9cd41..b28dcfba5911 100644 --- a/crates/node/types/Cargo.toml +++ b/crates/node/types/Cargo.toml @@ -14,4 +14,6 @@ workspace = true # reth reth-chainspec.workspace = true reth-db-api.workspace = true -reth-engine-primitives.workspace = true \ No newline at end of file +reth-engine-primitives.workspace = true +reth-primitives.workspace = true +reth-primitives-traits.workspace = true \ No newline at end of file diff --git a/crates/node/types/src/lib.rs b/crates/node/types/src/lib.rs index 2c72e02d3edc..5ba03e6795ab 100644 --- a/crates/node/types/src/lib.rs +++ b/crates/node/types/src/lib.rs @@ -8,6 +8,8 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +pub use reth_primitives_traits::{Block, BlockBody}; + use std::marker::PhantomData; use reth_chainspec::EthChainSpec; @@ -18,11 +20,14 @@ use reth_db_api::{ use reth_engine_primitives::EngineTypes; /// Configures all the primitive types of the node. -// TODO(mattsse): this is currently a placeholder -pub trait NodePrimitives {} +pub trait NodePrimitives { + /// Block primitive. + type Block; +} -// TODO(mattsse): Placeholder -impl NodePrimitives for () {} +impl NodePrimitives for () { + type Block = reth_primitives::Block; +} /// The type that configures the essential types of an Ethereum-like node. /// diff --git a/crates/optimism/bin/Cargo.toml b/crates/optimism/bin/Cargo.toml index 2de0bb6ee181..f60ef36a4669 100644 --- a/crates/optimism/bin/Cargo.toml +++ b/crates/optimism/bin/Cargo.toml @@ -37,7 +37,15 @@ tracy-allocator = ["reth-cli-util/tracy-allocator"] asm-keccak = ["reth-optimism-cli/asm-keccak", "reth-optimism-node/asm-keccak"] -optimism = ["reth-optimism-cli/optimism", "reth-optimism-node/optimism"] +optimism = [ + "reth-optimism-cli/optimism", + "reth-optimism-node/optimism", + "reth-optimism-consensus/optimism", + "reth-optimism-evm/optimism", + "reth-optimism-payload-builder/optimism", + "reth-optimism-rpc/optimism", + "reth-provider/optimism" +] min-error-logs = ["tracing/release_max_level_error"] min-warn-logs = ["tracing/release_max_level_warn"] diff --git a/crates/optimism/bin/src/main.rs b/crates/optimism/bin/src/main.rs index 6822a6a50ec4..c6d3e32b7cf1 100644 --- a/crates/optimism/bin/src/main.rs +++ b/crates/optimism/bin/src/main.rs @@ -6,7 +6,6 @@ use clap::Parser; use reth_node_builder::{engine_tree_config::TreeConfig, EngineNodeLauncher}; use reth_optimism_cli::{chainspec::OpChainSpecParser, Cli}; use reth_optimism_node::{args::RollupArgs, node::OptimismAddOns, OptimismNode}; -use reth_optimism_rpc::SequencerClient; use reth_provider::providers::BlockchainProvider2; use tracing as _; @@ -34,17 +33,7 @@ fn main() { let handle = builder .with_types_and_provider::>() .with_components(OptimismNode::components(rollup_args)) - .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 { - ctx.registry - .eth_api() - .set_sequencer_client(SequencerClient::new(sequencer_http))?; - } - - Ok(()) - }) + .with_add_ons(OptimismAddOns::new(sequencer_http_arg)) .launch_with_fn(|builder| { let launcher = EngineNodeLauncher::new( builder.task_executor().clone(), @@ -58,20 +47,8 @@ fn main() { handle.node_exit_future.await } false => { - let handle = builder - .node(OptimismNode::new(rollup_args.clone())) - .extend_rpc_modules(move |ctx| { - // register sequencer tx forwarder - if let Some(sequencer_http) = sequencer_http_arg { - ctx.registry - .eth_api() - .set_sequencer_client(SequencerClient::new(sequencer_http))?; - } - - Ok(()) - }) - .launch() - .await?; + let handle = + builder.node(OptimismNode::new(rollup_args.clone())).launch().await?; handle.node_exit_future.await } diff --git a/crates/optimism/chainspec/Cargo.toml b/crates/optimism/chainspec/Cargo.toml index c9f951c8d20d..6b068dabbf0c 100644 --- a/crates/optimism/chainspec/Cargo.toml +++ b/crates/optimism/chainspec/Cargo.toml @@ -25,6 +25,7 @@ reth-optimism-forks.workspace = true alloy-chains.workspace = true alloy-genesis.workspace = true alloy-primitives.workspace = true +alloy-consensus.workspace = true # op op-alloy-rpc-types.workspace = true @@ -44,12 +45,14 @@ op-alloy-rpc-types.workspace = true [features] default = ["std"] std = [ - "alloy-chains/std", - "alloy-genesis/std", - "alloy-primitives/std", - "op-alloy-rpc-types/std", - "reth-chainspec/std", - "reth-ethereum-forks/std", - "reth-primitives-traits/std", - "reth-optimism-forks/std", + "alloy-chains/std", + "alloy-genesis/std", + "alloy-primitives/std", + "op-alloy-rpc-types/std", + "reth-chainspec/std", + "reth-ethereum-forks/std", + "reth-primitives-traits/std", + "reth-optimism-forks/std", + "alloy-consensus/std", + "once_cell/std" ] diff --git a/crates/optimism/chainspec/src/dev.rs b/crates/optimism/chainspec/src/dev.rs index cb8163dfc521..eae25f73e01e 100644 --- a/crates/optimism/chainspec/src/dev.rs +++ b/crates/optimism/chainspec/src/dev.rs @@ -3,10 +3,10 @@ use alloc::sync::Arc; use alloy_chains::Chain; +use alloy_consensus::constants::DEV_GENESIS_HASH; use alloy_primitives::U256; use reth_chainspec::{once_cell_set, BaseFeeParams, BaseFeeParamsKind, ChainSpec}; use reth_optimism_forks::DEV_HARDFORKS; -use reth_primitives_traits::constants::DEV_GENESIS_HASH; use crate::{LazyLock, OpChainSpec}; diff --git a/crates/optimism/cli/Cargo.toml b/crates/optimism/cli/Cargo.toml index d53270cd62f8..7db41ccbe843 100644 --- a/crates/optimism/cli/Cargo.toml +++ b/crates/optimism/cli/Cargo.toml @@ -73,11 +73,14 @@ reth-cli-commands.workspace = true [features] optimism = [ - "reth-primitives/optimism", - "reth-optimism-evm/optimism", - "reth-provider/optimism", - "reth-node-core/optimism", - "reth-optimism-node/optimism", + "reth-primitives/optimism", + "reth-optimism-evm/optimism", + "reth-provider/optimism", + "reth-node-core/optimism", + "reth-optimism-node/optimism", + "reth-execution-types/optimism", + "reth-db/optimism", + "reth-db-api/optimism" ] asm-keccak = [ "alloy-primitives/asm-keccak", diff --git a/crates/optimism/consensus/Cargo.toml b/crates/optimism/consensus/Cargo.toml index f5f061c59929..e2520c89340d 100644 --- a/crates/optimism/consensus/Cargo.toml +++ b/crates/optimism/consensus/Cargo.toml @@ -25,6 +25,7 @@ reth-optimism-chainspec.workspace = true # ethereum alloy-primitives.workspace = true +alloy-consensus.workspace = true tracing.workspace = true diff --git a/crates/optimism/consensus/src/lib.rs b/crates/optimism/consensus/src/lib.rs index fe67ff1bcd9e..16c1d5d37d7c 100644 --- a/crates/optimism/consensus/src/lib.rs +++ b/crates/optimism/consensus/src/lib.rs @@ -9,6 +9,7 @@ // The `optimism` feature must be enabled to use this crate. #![cfg(feature = "optimism")] +use alloy_consensus::EMPTY_OMMER_ROOT_HASH; use alloy_primitives::{B64, U256}; use reth_chainspec::EthereumHardforks; use reth_consensus::{Consensus, ConsensusError, PostExecutionInput}; @@ -20,9 +21,7 @@ use reth_consensus_common::validation::{ }; use reth_optimism_chainspec::OpChainSpec; use reth_optimism_forks::OptimismHardforks; -use reth_primitives::{ - BlockWithSenders, GotExpected, Header, SealedBlock, SealedHeader, EMPTY_OMMER_ROOT_HASH, -}; +use reth_primitives::{BlockWithSenders, GotExpected, Header, SealedBlock, SealedHeader}; use std::{sync::Arc, time::SystemTime}; mod proof; diff --git a/crates/optimism/evm/Cargo.toml b/crates/optimism/evm/Cargo.toml index 76fa3ce69e4d..f6b22ad14c8d 100644 --- a/crates/optimism/evm/Cargo.toml +++ b/crates/optimism/evm/Cargo.toml @@ -20,10 +20,13 @@ reth-revm.workspace = true reth-execution-errors.workspace = true reth-execution-types.workspace = true reth-prune-types.workspace = true +reth-consensus.workspace = true # ethereum +alloy-eips.workspace = true alloy-primitives.workspace = true op-alloy-consensus.workspace = true +alloy-consensus.workspace = true # Optimism reth-optimism-consensus.workspace = true @@ -35,21 +38,34 @@ revm.workspace = true revm-primitives.workspace = true # misc -thiserror.workspace = true +derive_more.workspace = true tracing.workspace = true [dev-dependencies] -alloy-eips.workspace = true - +reth-evm = { workspace = true, features = ["test-utils"] } reth-revm = { workspace = true, features = ["test-utils"] } +reth-primitives = { workspace = true, features = ["test-utils"] } reth-optimism-chainspec.workspace = true alloy-genesis.workspace = true alloy-consensus.workspace = true [features] +default = ["std"] +std = [ + "reth-consensus/std", + "reth-primitives/std", + "reth-revm/std", + "alloy-consensus/std", + "alloy-eips/std", + "alloy-genesis/std", + "alloy-primitives/std", + "revm-primitives/std", + "revm/std" +] optimism = [ - "reth-primitives/optimism", - "reth-execution-types/optimism", - "reth-optimism-consensus/optimism", - "reth-revm/optimism", + "reth-primitives/optimism", + "reth-execution-types/optimism", + "reth-optimism-consensus/optimism", + "revm/optimism", + "revm-primitives/optimism" ] diff --git a/crates/optimism/evm/src/error.rs b/crates/optimism/evm/src/error.rs index c5c6a0a4a3d3..71f8709e1ade 100644 --- a/crates/optimism/evm/src/error.rs +++ b/crates/optimism/evm/src/error.rs @@ -1,27 +1,30 @@ //! Error types for the Optimism EVM module. +use alloc::string::String; use reth_evm::execute::BlockExecutionError; /// Optimism Block Executor Errors -#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, derive_more::Display)] pub enum OptimismBlockExecutionError { /// Error when trying to parse L1 block info - #[error("could not get L1 block info from L2 block: {message:?}")] + #[display("could not get L1 block info from L2 block: {message}")] L1BlockInfoError { /// The inner error message message: String, }, /// Thrown when force deploy of create2deployer code fails. - #[error("failed to force create2deployer account code")] + #[display("failed to force create2deployer account code")] ForceCreate2DeployerFail, /// Thrown when a blob transaction is included in a sequencer's block. - #[error("blob transaction included in sequencer block")] + #[display("blob transaction included in sequencer block")] BlobTransactionRejected, /// Thrown when a database account could not be loaded. - #[error("failed to load account {0}")] + #[display("failed to load account {_0}")] AccountLoadFailed(alloy_primitives::Address), } +impl core::error::Error for OptimismBlockExecutionError {} + impl From for BlockExecutionError { fn from(err: OptimismBlockExecutionError) -> Self { Self::other(err) diff --git a/crates/optimism/evm/src/execute.rs b/crates/optimism/evm/src/execute.rs index ee0d028e4251..20ef64457bb7 100644 --- a/crates/optimism/evm/src/execute.rs +++ b/crates/optimism/evm/src/execute.rs @@ -1,145 +1,146 @@ -//! Optimism block executor. - -use crate::{ - l1::ensure_create2_deployer, OpChainSpec, OptimismBlockExecutionError, OptimismEvmConfig, -}; -use alloy_primitives::{BlockNumber, U256}; -use reth_chainspec::{ChainSpec, EthereumHardforks}; +//! Optimism block execution strategy. + +use crate::{l1::ensure_create2_deployer, OptimismBlockExecutionError, OptimismEvmConfig}; +use alloc::{boxed::Box, sync::Arc, vec::Vec}; +use alloy_consensus::Transaction as _; +use alloy_eips::eip7685::Requests; +use core::fmt::Display; +use reth_chainspec::EthereumHardforks; +use reth_consensus::ConsensusError; use reth_evm::{ execute::{ - BatchExecutor, BlockExecutionError, BlockExecutionInput, BlockExecutionOutput, - BlockExecutorProvider, BlockValidationError, Executor, ProviderError, + BasicBlockExecutorProvider, BlockExecutionError, BlockExecutionStrategy, + BlockExecutionStrategyFactory, BlockValidationError, ExecuteOutput, ProviderError, }, - system_calls::{NoopHook, OnStateHook, SystemCaller}, + state_change::post_block_balance_increments, + system_calls::{OnStateHook, SystemCaller}, ConfigureEvm, }; -use reth_execution_types::ExecutionOutcome; +use reth_optimism_chainspec::OpChainSpec; use reth_optimism_consensus::validate_block_post_execution; use reth_optimism_forks::OptimismHardfork; -use reth_primitives::{BlockWithSenders, Header, Receipt, Receipts, TxType}; -use reth_prune_types::PruneModes; +use reth_primitives::{BlockWithSenders, Header, Receipt, TxType}; use reth_revm::{ - batch::BlockBatchRecord, db::states::bundle_state::BundleRetention, - state_change::post_block_balance_increments, Evm, State, + db::{states::bundle_state::BundleRetention, BundleState}, + Database, State, }; use revm_primitives::{ - db::{Database, DatabaseCommit}, - BlockEnv, CfgEnvWithHandlerCfg, EnvWithHandlerCfg, ResultAndState, + db::DatabaseCommit, BlockEnv, CfgEnvWithHandlerCfg, EnvWithHandlerCfg, ResultAndState, U256, }; -use std::{fmt::Display, sync::Arc}; use tracing::trace; -/// Provides executors to execute regular optimism blocks +/// Factory for [`OpExecutionStrategy`]. #[derive(Debug, Clone)] -pub struct OpExecutorProvider { +pub struct OpExecutionStrategyFactory { + /// The chainspec chain_spec: Arc, + /// How to create an EVM. evm_config: EvmConfig, } -impl OpExecutorProvider { - /// Creates a new default optimism executor provider. +impl OpExecutionStrategyFactory { + /// Creates a new default optimism executor strategy factory. pub fn optimism(chain_spec: Arc) -> Self { Self::new(chain_spec.clone(), OptimismEvmConfig::new(chain_spec)) } } -impl OpExecutorProvider { - /// Creates a new executor provider. +impl OpExecutionStrategyFactory { + /// Creates a new executor strategy factory. pub const fn new(chain_spec: Arc, evm_config: EvmConfig) -> Self { Self { chain_spec, evm_config } } } -impl OpExecutorProvider +impl BlockExecutionStrategyFactory for OpExecutionStrategyFactory where - EvmConfig: ConfigureEvm
, + EvmConfig: + Clone + Unpin + Sync + Send + 'static + ConfigureEvm
, { - fn op_executor(&self, db: DB) -> OpBlockExecutor + type Strategy + Display>> = + OpExecutionStrategy; + + fn create_strategy(&self, db: DB) -> Self::Strategy where DB: Database + Display>, { - OpBlockExecutor::new( - self.chain_spec.clone(), - self.evm_config.clone(), - State::builder().with_database(db).with_bundle_update().without_state_clear().build(), - ) + let state = + State::builder().with_database(db).with_bundle_update().without_state_clear().build(); + OpExecutionStrategy::new(state, self.chain_spec.clone(), self.evm_config.clone()) } } -impl BlockExecutorProvider for OpExecutorProvider +/// Block execution strategy for Optimism. +#[allow(missing_debug_implementations)] +pub struct OpExecutionStrategy where - EvmConfig: ConfigureEvm
, + EvmConfig: Clone, { - type Executor + Display>> = - OpBlockExecutor; - - type BatchExecutor + Display>> = - OpBatchExecutor; - fn executor(&self, db: DB) -> Self::Executor - where - DB: Database + Display>, - { - self.op_executor(db) - } - - fn batch_executor(&self, db: DB) -> Self::BatchExecutor - where - DB: Database + Display>, - { - let executor = self.op_executor(db); - OpBatchExecutor { executor, batch_record: BlockBatchRecord::default() } - } -} - -/// Helper container type for EVM with chain spec. -#[derive(Debug, Clone)] -pub struct OpEvmExecutor { /// The chainspec chain_spec: Arc, /// How to create an EVM. evm_config: EvmConfig, + /// Current state for block execution. + state: State, + /// Utility to call system smart contracts. + system_caller: SystemCaller, } -impl OpEvmExecutor +impl OpExecutionStrategy where - EvmConfig: ConfigureEvm
, + EvmConfig: Clone, { - /// Executes the transactions in the block and returns the receipts. - /// - /// This applies the pre-execution changes, and executes the transactions. - /// - /// The optional `state_hook` will be executed with the state changes if present. - /// - /// # Note + /// Creates a new [`OpExecutionStrategy`] + pub fn new(state: State, chain_spec: Arc, evm_config: EvmConfig) -> Self { + let system_caller = SystemCaller::new(evm_config.clone(), (*chain_spec).clone()); + Self { state, chain_spec, evm_config, system_caller } + } +} + +impl OpExecutionStrategy +where + DB: Database + Display>, + EvmConfig: ConfigureEvm
, +{ + /// Configures a new evm configuration and block environment for the given block. /// - /// It does __not__ apply post-execution changes. - fn execute_pre_and_transactions( - &self, + /// Caution: this does not initialize the tx environment. + fn evm_env_for_block(&self, header: &Header, total_difficulty: U256) -> EnvWithHandlerCfg { + let mut cfg = CfgEnvWithHandlerCfg::new(Default::default(), Default::default()); + let mut block_env = BlockEnv::default(); + self.evm_config.fill_cfg_and_block_env(&mut cfg, &mut block_env, header, total_difficulty); + + EnvWithHandlerCfg::new_with_cfg_env(cfg, block_env, Default::default()) + } +} + +impl BlockExecutionStrategy for OpExecutionStrategy +where + DB: Database + Display>, + EvmConfig: ConfigureEvm
, +{ + type Error = BlockExecutionError; + + fn apply_pre_execution_changes( + &mut self, block: &BlockWithSenders, - mut evm: Evm<'_, Ext, &mut State>, - state_hook: Option, - ) -> Result<(Vec, u64), BlockExecutionError> - where - DB: Database + Display>, - F: OnStateHook + 'static, - { - let mut system_caller = SystemCaller::new(self.evm_config.clone(), &self.chain_spec); - if let Some(hook) = state_hook { - system_caller.with_state_hook(Some(Box::new(hook) as Box)); - } + total_difficulty: U256, + ) -> Result<(), Self::Error> { + // Set state clear flag if the block is after the Spurious Dragon hardfork. + let state_clear_flag = + (*self.chain_spec).is_spurious_dragon_active_at_block(block.header.number); + self.state.set_state_clear_flag(state_clear_flag); + + let env = self.evm_env_for_block(&block.header, total_difficulty); + let mut evm = self.evm_config.evm_with_env(&mut self.state, env); - // apply pre execution changes - system_caller.apply_beacon_root_contract_call( + self.system_caller.apply_beacon_root_contract_call( block.timestamp, block.number, block.parent_beacon_block_root, &mut evm, )?; - // execute transactions - let is_regolith = - self.chain_spec.fork(OptimismHardfork::Regolith).active_at_timestamp(block.timestamp); - // Ensure that the create2deployer is force-deployed at the canyon transition. Optimism // blocks will always have at least a single transaction in them (the L1 info transaction), // so we can safely assume that this will always be triggered upon the transition and that @@ -147,6 +148,20 @@ where ensure_create2_deployer(self.chain_spec.clone(), block.timestamp, evm.db_mut()) .map_err(|_| OptimismBlockExecutionError::ForceCreate2DeployerFail)?; + Ok(()) + } + + fn execute_transactions( + &mut self, + block: &BlockWithSenders, + total_difficulty: U256, + ) -> Result { + let env = self.evm_env_for_block(&block.header, total_difficulty); + let mut evm = self.evm_config.evm_with_env(&mut self.state, env); + + let is_regolith = + self.chain_spec.fork(OptimismHardfork::Regolith).active_at_timestamp(block.timestamp); + let mut cumulative_gas_used = 0; let mut receipts = Vec::with_capacity(block.body.transactions.len()); for (sender, transaction) in block.transactions_with_sender() { @@ -199,7 +214,7 @@ where ?transaction, "Executed transaction" ); - system_caller.on_state(&result_and_state); + self.system_caller.on_state(&result_and_state); let ResultAndState { result, state } = result_and_state; evm.db_mut().commit(state); @@ -224,288 +239,63 @@ where .then_some(1), }); } - drop(evm); - - Ok((receipts, cumulative_gas_used)) - } -} - -/// A basic Optimism block executor. -/// -/// Expected usage: -/// - Create a new instance of the executor. -/// - Execute the block. -#[derive(Debug)] -pub struct OpBlockExecutor { - /// Chain specific evm config that's used to execute a block. - executor: OpEvmExecutor, - /// The state to use for execution - state: State, -} - -impl OpBlockExecutor { - /// Creates a new Optimism block executor. - pub const fn new( - chain_spec: Arc, - evm_config: EvmConfig, - state: State, - ) -> Self { - Self { executor: OpEvmExecutor { chain_spec, evm_config }, state } - } - - /// Returns the chain spec. - #[inline] - pub fn chain_spec(&self) -> &ChainSpec { - &self.executor.chain_spec - } - - /// Returns mutable reference to the state that wraps the underlying database. - pub fn state_mut(&mut self) -> &mut State { - &mut self.state - } -} - -impl OpBlockExecutor -where - EvmConfig: ConfigureEvm
, - DB: Database + Display>, -{ - /// Configures a new evm configuration and block environment for the given block. - /// - /// Caution: this does not initialize the tx environment. - fn evm_env_for_block(&self, header: &Header, total_difficulty: U256) -> EnvWithHandlerCfg { - let mut cfg = CfgEnvWithHandlerCfg::new(Default::default(), Default::default()); - let mut block_env = BlockEnv::default(); - self.executor.evm_config.fill_cfg_and_block_env( - &mut cfg, - &mut block_env, - header, - total_difficulty, - ); - - 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_with_state_hook( - &mut self, - block: &BlockWithSenders, - total_difficulty: U256, - state_hook: Option, - ) -> Result<(Vec, u64), BlockExecutionError> - where - F: OnStateHook + 'static, - { - // 1. prepare state on new block - self.on_new_block(&block.header); - - // 2. configure the evm and execute - let env = self.evm_env_for_block(&block.header, total_difficulty); - - 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, state_hook) - }?; - - // 3. apply post execution changes - self.post_execution(block, total_difficulty)?; - Ok((receipts, gas_used)) + Ok(ExecuteOutput { receipts, gas_used: cumulative_gas_used }) } - /// Apply settings before a new block is executed. - pub(crate) fn on_new_block(&mut self, header: &Header) { - // Set state clear flag if the block is after the Spurious Dragon hardfork. - let state_clear_flag = self.chain_spec().is_spurious_dragon_active_at_block(header.number); - self.state.set_state_clear_flag(state_clear_flag); - } - - /// Apply post execution state changes, including block rewards, withdrawals, and irregular DAO - /// hardfork state change. - pub fn post_execution( + fn apply_post_execution_changes( &mut self, block: &BlockWithSenders, total_difficulty: U256, - ) -> Result<(), BlockExecutionError> { + _receipts: &[Receipt], + ) -> Result { let balance_increments = - post_block_balance_increments(self.chain_spec(), block, total_difficulty); + post_block_balance_increments(&self.chain_spec.clone(), block, total_difficulty); // increment balances self.state .increment_balances(balance_increments) .map_err(|_| BlockValidationError::IncrementBalanceFailed)?; - Ok(()) + Ok(Requests::default()) } -} - -impl Executor for OpBlockExecutor -where - EvmConfig: ConfigureEvm
, - DB: Database + Display>, -{ - type Input<'a> = BlockExecutionInput<'a, BlockWithSenders>; - type Output = BlockExecutionOutput; - type Error = BlockExecutionError; - - /// Executes the block and commits the state changes. - /// - /// Returns the receipts of the transactions in the block. - /// - /// Returns an error if the block could not be executed or failed verification. - /// - /// State changes are committed to the database. - fn execute(mut self, input: Self::Input<'_>) -> Result { - let BlockExecutionInput { block, total_difficulty } = input; - let (receipts, gas_used) = self.execute_without_verification(block, total_difficulty)?; - - // 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, - }) + fn state_ref(&self) -> &State { + &self.state } - fn execute_with_state_closure( - mut self, - input: Self::Input<'_>, - mut witness: F, - ) -> Result - where - F: FnMut(&State), - { - let BlockExecutionInput { block, total_difficulty } = input; - let (receipts, gas_used) = self.execute_without_verification(block, total_difficulty)?; - - // NOTE: we need to merge keep the reverts for the bundle retention - self.state.merge_transitions(BundleRetention::Reverts); - witness(&self.state); - - Ok(BlockExecutionOutput { - state: self.state.take_bundle(), - receipts, - requests: vec![], - gas_used, - }) + fn state_mut(&mut self) -> &mut State { + &mut self.state } - fn execute_with_state_hook( - mut self, - input: Self::Input<'_>, - state_hook: F, - ) -> Result - where - F: OnStateHook + 'static, - { - 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, - }) + fn with_state_hook(&mut self, hook: Option>) { + self.system_caller.with_state_hook(hook); } -} - -/// An executor for a batch of blocks. -/// -/// State changes are tracked until the executor is finalized. -#[derive(Debug)] -pub struct OpBatchExecutor { - /// The executor used to execute blocks. - executor: OpBlockExecutor, - /// Keeps track of the batch and record receipts based on the configured prune mode - batch_record: BlockBatchRecord, -} -impl OpBatchExecutor { - /// Returns the receipts of the executed blocks. - pub const fn receipts(&self) -> &Receipts { - self.batch_record.receipts() + fn finish(&mut self) -> BundleState { + self.state.merge_transitions(BundleRetention::Reverts); + self.state.take_bundle() } - /// Returns mutable reference to the state that wraps the underlying database. - pub fn state_mut(&mut self) -> &mut State { - self.executor.state_mut() + fn validate_block_post_execution( + &self, + block: &BlockWithSenders, + receipts: &[Receipt], + _requests: &Requests, + ) -> Result<(), ConsensusError> { + validate_block_post_execution(block, &self.chain_spec.clone(), receipts) } } -impl BatchExecutor for OpBatchExecutor -where - EvmConfig: ConfigureEvm
, - DB: Database + Display>, -{ - type Input<'a> = BlockExecutionInput<'a, BlockWithSenders>; - type Output = ExecutionOutcome; - type Error = BlockExecutionError; - - fn execute_and_verify_one(&mut self, input: Self::Input<'_>) -> Result<(), Self::Error> { - let BlockExecutionInput { block, total_difficulty } = input; - - if self.batch_record.first_block().is_none() { - self.batch_record.set_first_block(block.number); - } - - let (receipts, _gas_used) = - self.executor.execute_without_verification(block, total_difficulty)?; - - validate_block_post_execution(block, self.executor.chain_spec(), &receipts)?; - - // prepare the state according to the prune mode - let retention = self.batch_record.bundle_retention(block.number); - self.executor.state.merge_transitions(retention); - - // store receipts in the set - self.batch_record.save_receipts(receipts)?; - - Ok(()) - } - - fn finalize(mut self) -> Self::Output { - ExecutionOutcome::new( - self.executor.state.take_bundle(), - self.batch_record.take_receipts(), - self.batch_record.first_block().unwrap_or_default(), - self.batch_record.take_requests(), - ) - } - - fn set_tip(&mut self, tip: BlockNumber) { - self.batch_record.set_tip(tip); - } - - fn set_prune_modes(&mut self, prune_modes: PruneModes) { - self.batch_record.set_prune_modes(prune_modes); - } +/// Helper type with backwards compatible methods to obtain executor providers. +#[derive(Debug)] +pub struct OpExecutorProvider; - fn size_hint(&self) -> Option { - Some(self.executor.state.bundle_state.size_hint()) +impl OpExecutorProvider { + /// Creates a new default optimism executor strategy factory. + pub fn optimism( + chain_spec: Arc, + ) -> BasicBlockExecutorProvider { + BasicBlockExecutorProvider::new(OpExecutionStrategyFactory::optimism(chain_spec)) } } @@ -516,6 +306,7 @@ mod tests { use alloy_consensus::TxEip1559; use alloy_primitives::{b256, Address, StorageKey, StorageValue}; use reth_chainspec::MIN_TRANSACTION_GAS; + use reth_evm::execute::{BasicBlockExecutorProvider, BatchExecutor, BlockExecutorProvider}; use reth_optimism_chainspec::{optimism_deposit_tx_signature, OpChainSpecBuilder}; use reth_primitives::{Account, Block, BlockBody, Signature, Transaction, TransactionSigned}; use reth_revm::{ @@ -550,8 +341,13 @@ mod tests { db } - fn executor_provider(chain_spec: Arc) -> OpExecutorProvider { - OpExecutorProvider { evm_config: OptimismEvmConfig::new(chain_spec.clone()), chain_spec } + fn executor_provider( + chain_spec: Arc, + ) -> BasicBlockExecutorProvider { + let strategy_factory = + OpExecutionStrategyFactory::new(chain_spec.clone(), OptimismEvmConfig::new(chain_spec)); + + BasicBlockExecutorProvider::new(strategy_factory) } #[test] @@ -599,7 +395,10 @@ mod tests { let provider = executor_provider(chain_spec); let mut executor = provider.batch_executor(StateProviderDatabase::new(&db)); - executor.state_mut().load_cache_account(L1_BLOCK_CONTRACT).unwrap(); + // make sure the L1 block contract state is preloaded. + executor.with_state_mut(|state| { + state.load_cache_account(L1_BLOCK_CONTRACT).unwrap(); + }); // Attempt to execute a block with one deposit and one non-deposit transaction executor @@ -621,8 +420,9 @@ mod tests { ) .unwrap(); - let tx_receipt = executor.receipts()[0][0].as_ref().unwrap(); - let deposit_receipt = executor.receipts()[0][1].as_ref().unwrap(); + let receipts = executor.receipts(); + let tx_receipt = receipts[0][0].as_ref().unwrap(); + let deposit_receipt = receipts[0][1].as_ref().unwrap(); // deposit_receipt_version is not present in pre canyon transactions assert!(deposit_receipt.deposit_receipt_version.is_none()); @@ -679,7 +479,10 @@ mod tests { let provider = executor_provider(chain_spec); let mut executor = provider.batch_executor(StateProviderDatabase::new(&db)); - executor.state_mut().load_cache_account(L1_BLOCK_CONTRACT).unwrap(); + // make sure the L1 block contract state is preloaded. + executor.with_state_mut(|state| { + state.load_cache_account(L1_BLOCK_CONTRACT).unwrap(); + }); // attempt to execute an empty block with parent beacon block root, this should not fail executor @@ -701,8 +504,9 @@ mod tests { ) .expect("Executing a block while canyon is active should not fail"); - let tx_receipt = executor.receipts()[0][0].as_ref().unwrap(); - let deposit_receipt = executor.receipts()[0][1].as_ref().unwrap(); + let receipts = executor.receipts(); + let tx_receipt = receipts[0][0].as_ref().unwrap(); + let deposit_receipt = receipts[0][1].as_ref().unwrap(); // deposit_receipt_version is set to 1 for post canyon deposit transactions assert_eq!(deposit_receipt.deposit_receipt_version, Some(1)); diff --git a/crates/optimism/evm/src/l1.rs b/crates/optimism/evm/src/l1.rs index 3412501eb99b..e0668ab02042 100644 --- a/crates/optimism/evm/src/l1.rs +++ b/crates/optimism/evm/src/l1.rs @@ -1,6 +1,7 @@ //! Optimism-specific implementation and utilities for the executor use crate::OptimismBlockExecutionError; +use alloc::{string::ToString, sync::Arc}; use alloy_primitives::{address, b256, hex, Address, Bytes, B256, U256}; use reth_chainspec::ChainSpec; use reth_execution_errors::BlockExecutionError; @@ -11,7 +12,6 @@ use revm::{ primitives::{Bytecode, HashMap, SpecId}, DatabaseCommit, L1BlockInfo, }; -use std::sync::Arc; use tracing::trace; /// The address of the create2 deployer diff --git a/crates/optimism/evm/src/lib.rs b/crates/optimism/evm/src/lib.rs index 63d0ee6b4f4e..60aa9f7db083 100644 --- a/crates/optimism/evm/src/lib.rs +++ b/crates/optimism/evm/src/lib.rs @@ -6,9 +6,13 @@ issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +#![cfg_attr(not(feature = "std"), no_std)] // The `optimism` feature must be enabled to use this crate. #![cfg(feature = "optimism")] +extern crate alloc; + +use alloc::{sync::Arc, vec::Vec}; use alloy_primitives::{Address, U256}; use reth_evm::{ConfigureEvm, ConfigureEvmEnv, NextBlockEnvAttributes}; use reth_optimism_chainspec::OpChainSpec; @@ -18,7 +22,6 @@ use reth_primitives::{ Head, Header, TransactionSigned, }; use reth_revm::{inspector_handle_register, Database, Evm, EvmBuilder, GetInspector}; -use std::sync::Arc; mod config; pub use config::{revm_spec, revm_spec_by_timestamp_after_bedrock}; @@ -202,14 +205,16 @@ impl ConfigureEvm for OptimismEvmConfig { #[cfg(test)] mod tests { use super::*; + use alloy_consensus::constants::KECCAK_EMPTY; use alloy_genesis::Genesis; use alloy_primitives::{B256, U256}; - use reth_chainspec::{Chain, ChainSpec}; + use reth_chainspec::ChainSpec; use reth_evm::execute::ProviderError; + use reth_execution_types::{Chain, ExecutionOutcome}; use reth_optimism_chainspec::BASE_MAINNET; use reth_primitives::{ revm_primitives::{BlockEnv, CfgEnv, SpecId}, - Header, KECCAK_EMPTY, + Header, Receipt, Receipts, SealedBlockWithSenders, TxType, }; use reth_revm::{ db::{CacheDB, EmptyDBTyped}, @@ -237,7 +242,7 @@ mod tests { // Build the ChainSpec for Ethereum mainnet, activating London, Paris, and Shanghai // hardforks let chain_spec = ChainSpec::builder() - .chain(Chain::mainnet()) + .chain(0.into()) .genesis(Genesis::default()) .london_activated() .paris_activated() @@ -540,4 +545,79 @@ mod tests { // Optimism in handler assert_eq!(evm.handler.cfg, HandlerCfg { spec_id: SpecId::ECOTONE, is_optimism: true }); } + + #[test] + fn receipts_by_block_hash() { + // Create a default SealedBlockWithSenders object + let block = SealedBlockWithSenders::default(); + + // Define block hashes for block1 and block2 + let block1_hash = B256::new([0x01; 32]); + let block2_hash = B256::new([0x02; 32]); + + // Clone the default block into block1 and block2 + let mut block1 = block.clone(); + let mut block2 = block; + + // Set the hashes of block1 and block2 + block1.block.header.set_block_number(10); + block1.block.header.set_hash(block1_hash); + + block2.block.header.set_block_number(11); + block2.block.header.set_hash(block2_hash); + + // Create a random receipt object, receipt1 + let receipt1 = Receipt { + tx_type: TxType::Legacy, + cumulative_gas_used: 46913, + logs: vec![], + success: true, + deposit_nonce: Some(18), + deposit_receipt_version: Some(34), + }; + + // Create another random receipt object, receipt2 + let receipt2 = Receipt { + tx_type: TxType::Legacy, + cumulative_gas_used: 1325345, + logs: vec![], + success: true, + deposit_nonce: Some(18), + deposit_receipt_version: Some(34), + }; + + // Create a Receipts object with a vector of receipt vectors + let receipts = + Receipts { receipt_vec: vec![vec![Some(receipt1.clone())], vec![Some(receipt2)]] }; + + // Create an ExecutionOutcome object with the created bundle, receipts, an empty requests + // vector, and first_block set to 10 + let execution_outcome = ExecutionOutcome { + bundle: Default::default(), + receipts, + requests: vec![], + first_block: 10, + }; + + // Create a Chain object with a BTreeMap of blocks mapped to their block numbers, + // including block1_hash and block2_hash, and the execution_outcome + let chain = Chain::new([block1, block2], execution_outcome.clone(), None); + + // Assert that the proper receipt vector is returned for block1_hash + assert_eq!(chain.receipts_by_block_hash(block1_hash), Some(vec![&receipt1])); + + // Create an ExecutionOutcome object with a single receipt vector containing receipt1 + let execution_outcome1 = ExecutionOutcome { + bundle: Default::default(), + receipts: Receipts { receipt_vec: vec![vec![Some(receipt1)]] }, + requests: vec![], + first_block: 10, + }; + + // Assert that the execution outcome at the first block contains only the first receipt + assert_eq!(chain.execution_outcome_at_block(10), Some(execution_outcome1)); + + // Assert that the execution outcome at the tip block contains the whole execution outcome + assert_eq!(chain.execution_outcome_at_block(11), Some(execution_outcome)); + } } diff --git a/crates/optimism/hardforks/Cargo.toml b/crates/optimism/hardforks/Cargo.toml index 815d50c6bcc4..c30566a54eb4 100644 --- a/crates/optimism/hardforks/Cargo.toml +++ b/crates/optimism/hardforks/Cargo.toml @@ -27,5 +27,13 @@ once_cell.workspace = true [features] default = ["std"] -std = [] -serde = ["dep:serde"] \ No newline at end of file +std = [ + "alloy-primitives/std", + "once_cell/std", + "serde?/std" +] +serde = [ + "dep:serde", + "alloy-chains/serde", + "alloy-primitives/serde" +] diff --git a/crates/optimism/node/Cargo.toml b/crates/optimism/node/Cargo.toml index f9e038a3d9e0..37cf4a328ea0 100644 --- a/crates/optimism/node/Cargo.toml +++ b/crates/optimism/node/Cargo.toml @@ -19,7 +19,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-compat.workspace = true reth-node-api.workspace = true reth-node-builder.workspace = true reth-tracing.workspace = true @@ -29,10 +28,6 @@ reth-network.workspace = true reth-evm.workspace = true reth-revm = { workspace = true, features = ["std"] } reth-beacon-consensus.workspace = true -reth-discv5.workspace = true -reth-rpc-eth-types.workspace = true -reth-rpc-eth-api.workspace = true -reth-rpc.workspace = true # op-reth reth-optimism-payload-builder.workspace = true @@ -51,21 +46,13 @@ alloy-primitives.workspace = true op-alloy-rpc-types-engine.workspace = true alloy-rpc-types-engine.workspace = true -# async -async-trait.workspace = true -reqwest = { workspace = true, features = ["rustls-tls-native-roots"] } -tracing.workspace = true - # misc clap.workspace = true serde.workspace = true eyre.workspace = true parking_lot.workspace = true -thiserror.workspace = true # rpc -jsonrpsee.workspace = true -jsonrpsee-types.workspace = true serde_json.workspace = true [dev-dependencies] @@ -82,16 +69,35 @@ op-alloy-consensus.workspace = true [features] optimism = [ - "reth-primitives/optimism", - "reth-provider/optimism", - "reth-optimism-evm/optimism", - "reth-optimism-payload-builder/optimism", - "reth-beacon-consensus/optimism", - "reth-revm/optimism", - "revm/optimism", - "reth-auto-seal-consensus/optimism", - "reth-optimism-rpc/optimism", - "reth-engine-local/optimism", + "reth-primitives/optimism", + "reth-provider/optimism", + "reth-optimism-evm/optimism", + "reth-optimism-payload-builder/optimism", + "reth-beacon-consensus/optimism", + "revm/optimism", + "reth-auto-seal-consensus/optimism", + "reth-optimism-rpc/optimism", + "reth-engine-local/optimism", + "reth-optimism-consensus/optimism", + "reth-db/optimism" +] +asm-keccak = [ + "reth-primitives/asm-keccak", + "reth/asm-keccak", + "alloy-primitives/asm-keccak", + "revm/asm-keccak" +] +test-utils = [ + "reth-node-builder/test-utils", + "reth-chainspec/test-utils", + "reth-consensus/test-utils", + "reth-evm/test-utils", + "reth-network/test-utils", + "reth-payload-builder/test-utils", + "reth-primitives/test-utils", + "reth-revm/test-utils", + "reth-db/test-utils", + "reth-provider/test-utils", + "reth-transaction-pool/test-utils", + "revm/test-utils" ] -asm-keccak = ["reth-primitives/asm-keccak"] -test-utils = ["reth-node-builder/test-utils"] diff --git a/crates/optimism/node/src/engine.rs b/crates/optimism/node/src/engine.rs index a83f4c696a1c..cec609671a38 100644 --- a/crates/optimism/node/src/engine.rs +++ b/crates/optimism/node/src/engine.rs @@ -38,9 +38,9 @@ where + TryInto, { type ExecutionPayloadV1 = ExecutionPayloadV1; - type ExecutionPayloadV2 = ExecutionPayloadEnvelopeV2; - type ExecutionPayloadV3 = OpExecutionPayloadEnvelopeV3; - type ExecutionPayloadV4 = OpExecutionPayloadEnvelopeV4; + type ExecutionPayloadEnvelopeV2 = ExecutionPayloadEnvelopeV2; + type ExecutionPayloadEnvelopeV3 = OpExecutionPayloadEnvelopeV3; + type ExecutionPayloadEnvelopeV4 = OpExecutionPayloadEnvelopeV4; } /// A default payload type for [`OptimismEngineTypes`] diff --git a/crates/optimism/node/src/node.rs b/crates/optimism/node/src/node.rs index b6e64f7e0e43..22fc1a88ff73 100644 --- a/crates/optimism/node/src/node.rs +++ b/crates/optimism/node/src/node.rs @@ -4,23 +4,26 @@ use std::sync::Arc; use reth_basic_payload_builder::{BasicPayloadJobGenerator, BasicPayloadJobGeneratorConfig}; use reth_chainspec::{EthChainSpec, Hardforks}; -use reth_evm::ConfigureEvm; +use reth_evm::{execute::BasicBlockExecutorProvider, ConfigureEvm}; use reth_network::{NetworkConfig, NetworkHandle, NetworkManager}; -use reth_node_api::{EngineValidator, FullNodeComponents, NodeAddOns}; +use reth_node_api::{ + AddOnsContext, EngineValidator, FullNodeComponents, NodeAddOns, NodePrimitives, +}; use reth_node_builder::{ components::{ - ComponentsBuilder, ConsensusBuilder, EngineValidatorBuilder, ExecutorBuilder, - NetworkBuilder, PayloadServiceBuilder, PoolBuilder, PoolBuilderConfigOverrides, + ComponentsBuilder, ConsensusBuilder, ExecutorBuilder, NetworkBuilder, + PayloadServiceBuilder, PoolBuilder, PoolBuilderConfigOverrides, }, node::{FullNodeTypes, NodeTypes, NodeTypesWithEngine}, - BuilderContext, Node, PayloadBuilderConfig, + rpc::{EngineValidatorBuilder, RethRpcAddOns, RpcAddOns, RpcHandle}, + BuilderContext, Node, NodeAdapter, NodeComponentsBuilder, PayloadBuilderConfig, }; use reth_optimism_chainspec::OpChainSpec; use reth_optimism_consensus::OptimismBeaconConsensus; -use reth_optimism_evm::{OpExecutorProvider, OptimismEvmConfig}; +use reth_optimism_evm::{OpExecutionStrategyFactory, OptimismEvmConfig}; use reth_optimism_rpc::OpEthApi; use reth_payload_builder::{PayloadBuilderHandle, PayloadBuilderService}; -use reth_primitives::Header; +use reth_primitives::{Block, Header}; use reth_provider::CanonStateSubscriptions; use reth_tracing::tracing::{debug, info}; use reth_transaction_pool::{ @@ -35,6 +38,14 @@ use crate::{ OptimismEngineTypes, }; +/// Optimism primitive types. +#[derive(Debug)] +pub struct OpPrimitives; + +impl NodePrimitives for OpPrimitives { + type Block = Block; +} + /// Type configuration for a regular Optimism node. #[derive(Debug, Default, Clone)] #[non_exhaustive] @@ -59,7 +70,6 @@ impl OptimismNode { OptimismNetworkBuilder, OptimismExecutorBuilder, OptimismConsensusBuilder, - OptimismEngineValidatorBuilder, > where Node: FullNodeTypes< @@ -77,7 +87,6 @@ impl OptimismNode { }) .executor(OptimismExecutorBuilder::default()) .consensus(OptimismConsensusBuilder::default()) - .engine_validator(OptimismEngineValidatorBuilder::default()) } } @@ -94,10 +103,11 @@ where OptimismNetworkBuilder, OptimismExecutorBuilder, OptimismConsensusBuilder, - OptimismEngineValidatorBuilder, >; - type AddOns = OptimismAddOns; + type AddOns = OptimismAddOns< + NodeAdapter>::Components>, + >; fn components_builder(&self) -> Self::ComponentsBuilder { let Self { args } = self; @@ -110,7 +120,7 @@ where } impl NodeTypes for OptimismNode { - type Primitives = (); + type Primitives = OpPrimitives; type ChainSpec = OpChainSpec; } @@ -119,25 +129,49 @@ impl NodeTypesWithEngine for OptimismNode { } /// Add-ons w.r.t. optimism. -#[derive(Debug, Clone)] -pub struct OptimismAddOns { - sequencer_http: Option, +#[derive(Debug)] +pub struct OptimismAddOns( + pub RpcAddOns, OptimismEngineValidatorBuilder>, +); + +impl Default for OptimismAddOns { + fn default() -> Self { + Self::new(None) + } } -impl OptimismAddOns { +impl OptimismAddOns { /// Create a new instance with the given `sequencer_http` URL. - pub const fn new(sequencer_http: Option) -> Self { - Self { sequencer_http } + pub fn new(sequencer_http: Option) -> Self { + Self(RpcAddOns::new(move |ctx| OpEthApi::new(ctx, sequencer_http), Default::default())) } +} - /// Returns the sequencer HTTP URL. - pub fn sequencer_http(&self) -> Option<&str> { - self.sequencer_http.as_deref() +impl NodeAddOns for OptimismAddOns +where + N: FullNodeComponents>, + OptimismEngineValidator: EngineValidator<::Engine>, +{ + type Handle = RpcHandle>; + + async fn launch_add_ons( + self, + ctx: reth_node_api::AddOnsContext<'_, N>, + ) -> eyre::Result { + self.0.launch_add_ons(ctx).await } } -impl NodeAddOns for OptimismAddOns { +impl RethRpcAddOns for OptimismAddOns +where + N: FullNodeComponents>, + OptimismEngineValidator: EngineValidator<::Engine>, +{ type EthApi = OpEthApi; + + fn hooks_mut(&mut self) -> &mut reth_node_builder::rpc::RpcHooks { + self.0.hooks_mut() + } } /// A regular optimism evm and executor builder. @@ -150,14 +184,16 @@ where Node: FullNodeTypes>, { type EVM = OptimismEvmConfig; - type Executor = OpExecutorProvider; + type Executor = BasicBlockExecutorProvider; async fn build_evm( self, ctx: &BuilderContext, ) -> eyre::Result<(Self::EVM, Self::Executor)> { let evm_config = OptimismEvmConfig::new(ctx.chain_spec()); - let executor = OpExecutorProvider::new(ctx.chain_spec(), evm_config.clone()); + let strategy_factory = + OpExecutionStrategyFactory::new(ctx.chain_spec(), evm_config.clone()); + let executor = BasicBlockExecutorProvider::new(strategy_factory); Ok((evm_config, executor)) } @@ -429,12 +465,12 @@ pub struct OptimismEngineValidatorBuilder; impl EngineValidatorBuilder for OptimismEngineValidatorBuilder where Types: NodeTypesWithEngine, - Node: FullNodeTypes, + Node: FullNodeComponents, OptimismEngineValidator: EngineValidator, { type Validator = OptimismEngineValidator; - async fn build_validator(self, ctx: &BuilderContext) -> eyre::Result { - Ok(OptimismEngineValidator::new(ctx.chain_spec())) + async fn build(self, ctx: &AddOnsContext<'_, Node>) -> eyre::Result { + Ok(OptimismEngineValidator::new(ctx.config.chain.clone())) } } diff --git a/crates/optimism/node/tests/e2e/utils.rs b/crates/optimism/node/tests/e2e/utils.rs index 863bf254e494..8ea8df380b0b 100644 --- a/crates/optimism/node/tests/e2e/utils.rs +++ b/crates/optimism/node/tests/e2e/utils.rs @@ -1,7 +1,9 @@ use alloy_genesis::Genesis; use alloy_primitives::{Address, B256}; use reth::{rpc::types::engine::PayloadAttributes, tasks::TaskManager}; -use reth_e2e_test_utils::{transaction::TransactionTestContext, wallet::Wallet, NodeHelperType}; +use reth_e2e_test_utils::{ + transaction::TransactionTestContext, wallet::Wallet, Adapter, NodeHelperType, +}; use reth_optimism_chainspec::OpChainSpecBuilder; use reth_optimism_node::{ node::OptimismAddOns, OptimismBuiltPayload, OptimismNode, OptimismPayloadBuilderAttributes, @@ -11,7 +13,7 @@ use std::sync::Arc; use tokio::sync::Mutex; /// Optimism Node Helper type -pub(crate) type OpNode = NodeHelperType; +pub(crate) type OpNode = NodeHelperType>>; pub(crate) async fn setup(num_nodes: usize) -> eyre::Result<(Vec, TaskManager, Wallet)> { let genesis: Genesis = serde_json::from_str(include_str!("../assets/genesis.json")).unwrap(); diff --git a/crates/optimism/payload/Cargo.toml b/crates/optimism/payload/Cargo.toml index 414b2c358118..de61def83506 100644 --- a/crates/optimism/payload/Cargo.toml +++ b/crates/optimism/payload/Cargo.toml @@ -41,6 +41,7 @@ alloy-rlp.workspace = true op-alloy-rpc-types-engine.workspace = true revm-primitives.workspace = true alloy-rpc-types-engine.workspace = true +alloy-consensus.workspace = true # misc tracing.workspace = true @@ -49,8 +50,11 @@ sha2.workspace = true [features] optimism = [ - "reth-primitives/optimism", - "reth-provider/optimism", - "reth-optimism-evm/optimism", - "reth-revm/optimism", + "reth-primitives/optimism", + "reth-provider/optimism", + "reth-optimism-evm/optimism", + "revm/optimism", + "reth-execution-types/optimism", + "reth-optimism-consensus/optimism", + "revm-primitives/optimism" ] diff --git a/crates/optimism/payload/src/builder.rs b/crates/optimism/payload/src/builder.rs index 0a8dcdb12449..b6ab9b87956b 100644 --- a/crates/optimism/payload/src/builder.rs +++ b/crates/optimism/payload/src/builder.rs @@ -2,6 +2,7 @@ use std::sync::Arc; +use alloy_consensus::EMPTY_OMMER_ROOT_HASH; use alloy_primitives::U256; use reth_basic_payload_builder::*; use reth_chain_state::ExecutedBlock; @@ -16,7 +17,7 @@ use reth_primitives::{ constants::BEACON_NONCE, proofs, revm_primitives::{BlockEnv, CfgEnvWithHandlerCfg}, - Block, BlockBody, Header, Receipt, TxType, EMPTY_OMMER_ROOT_HASH, + Block, BlockBody, Header, Receipt, TxType, }; use reth_provider::StateProviderFactory; use reth_revm::database::StateProviderDatabase; @@ -506,13 +507,13 @@ where parent_beacon_block_root: attributes.payload_attributes.parent_beacon_block_root, blob_gas_used, excess_blob_gas: excess_blob_gas.map(Into::into), - requests_root: None, + requests_hash: None, }; // seal the block let block = Block { header, - body: BlockBody { transactions: executed_txs, ommers: vec![], withdrawals, requests: None }, + body: BlockBody { transactions: executed_txs, ommers: vec![], withdrawals }, }; let sealed_block = block.seal_slow(); diff --git a/crates/optimism/payload/src/payload.rs b/crates/optimism/payload/src/payload.rs index 122c2fde5269..d5d1620e54b9 100644 --- a/crates/optimism/payload/src/payload.rs +++ b/crates/optimism/payload/src/payload.rs @@ -18,8 +18,7 @@ use reth_primitives::{ transaction::WithEncoded, BlobTransactionSidecar, SealedBlock, TransactionSigned, Withdrawals, }; use reth_rpc_types_compat::engine::payload::{ - block_to_payload_v1, block_to_payload_v3, block_to_payload_v4, - convert_block_to_payload_field_v2, + block_to_payload_v1, block_to_payload_v3, convert_block_to_payload_field_v2, }; use std::sync::Arc; @@ -249,7 +248,7 @@ impl From for OpExecutionPayloadEnvelopeV4 { B256::ZERO }; Self { - execution_payload: block_to_payload_v4(block), + execution_payload: block_to_payload_v3(block), block_value: fees, // From the engine API spec: // @@ -262,6 +261,7 @@ impl From for OpExecutionPayloadEnvelopeV4 { should_override_builder: false, blobs_bundle: sidecars.into_iter().map(Into::into).collect::>().into(), parent_beacon_block_root, + execution_requests: vec![], } } } diff --git a/crates/optimism/primitives/Cargo.toml b/crates/optimism/primitives/Cargo.toml index 73a3bab1e445..a2d4c20a8b72 100644 --- a/crates/optimism/primitives/Cargo.toml +++ b/crates/optimism/primitives/Cargo.toml @@ -13,5 +13,5 @@ workspace = true [dependencies] reth-primitives.workspace = true -reth-primitives-traits.workspace = true alloy-primitives.workspace = true +alloy-consensus.workspace = true diff --git a/crates/optimism/primitives/src/bedrock.rs b/crates/optimism/primitives/src/bedrock.rs index 4ece12ad679e..7153ae3155c7 100644 --- a/crates/optimism/primitives/src/bedrock.rs +++ b/crates/optimism/primitives/src/bedrock.rs @@ -1,8 +1,8 @@ //! OP mainnet bedrock related data. +use alloy_consensus::{EMPTY_OMMER_ROOT_HASH, EMPTY_ROOT_HASH}; use alloy_primitives::{address, b256, bloom, bytes, B256, B64, U256}; use reth_primitives::Header; -use reth_primitives_traits::constants::EMPTY_OMMER_ROOT_HASH; /// Transaction 0x9ed8f713b2cc6439657db52dcd2fdb9cc944915428f3c6e2a7703e242b259cb9 in block 985, /// replayed in blocks: @@ -73,10 +73,10 @@ pub const BEDROCK_HEADER: Header = Header { nonce: B64::ZERO, number: 105235063, parent_hash: b256!("21a168dfa5e727926063a28ba16fd5ee84c814e847c81a699c7a0ea551e4ca50"), - receipts_root: b256!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"), + receipts_root: EMPTY_ROOT_HASH, state_root: b256!("920314c198da844a041d63bf6cbe8b59583165fd2229d1b3f599da812fd424cb"), timestamp: 1686068903, - transactions_root: b256!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"), + transactions_root: EMPTY_ROOT_HASH, ommers_hash: EMPTY_OMMER_ROOT_HASH, beneficiary: address!("4200000000000000000000000000000000000011"), withdrawals_root: None, @@ -85,7 +85,7 @@ pub const BEDROCK_HEADER: Header = Header { blob_gas_used: None, excess_blob_gas: None, parent_beacon_block_root: None, - requests_root: None, + requests_hash: None, }; /// Bedrock total difficulty on Optimism Mainnet. diff --git a/crates/optimism/rpc/Cargo.toml b/crates/optimism/rpc/Cargo.toml index 65dce7510b0e..dc0f96c4012d 100644 --- a/crates/optimism/rpc/Cargo.toml +++ b/crates/optimism/rpc/Cargo.toml @@ -38,6 +38,7 @@ alloy-eips.workspace = true alloy-primitives.workspace = true alloy-rpc-types-eth.workspace = true alloy-rpc-types.workspace = true +alloy-consensus.workspace = true op-alloy-network.workspace = true op-alloy-rpc-types.workspace = true op-alloy-consensus.workspace = true @@ -62,8 +63,9 @@ reth-optimism-chainspec.workspace = true [features] optimism = [ - "reth-optimism-evm/optimism", - "reth-primitives/optimism", - "reth-provider/optimism", - "revm/optimism", + "reth-optimism-evm/optimism", + "reth-primitives/optimism", + "reth-provider/optimism", + "revm/optimism", + "reth-optimism-consensus/optimism" ] diff --git a/crates/optimism/rpc/src/eth/mod.rs b/crates/optimism/rpc/src/eth/mod.rs index 57ce44100f26..d65dd8edd1d5 100644 --- a/crates/optimism/rpc/src/eth/mod.rs +++ b/crates/optimism/rpc/src/eth/mod.rs @@ -17,7 +17,7 @@ use op_alloy_network::Optimism; use reth_chainspec::EthereumHardforks; use reth_evm::ConfigureEvm; use reth_network_api::NetworkInfo; -use reth_node_api::{BuilderProvider, FullNodeComponents, FullNodeTypes, NodeTypes}; +use reth_node_api::{FullNodeComponents, FullNodeTypes, NodeTypes}; use reth_node_builder::EthApiBuilderCtx; use reth_primitives::Header; use reth_provider::{ @@ -38,7 +38,6 @@ use reth_tasks::{ TaskSpawner, }; use reth_transaction_pool::TransactionPool; -use tokio::sync::OnceCell; use crate::{OpEthApiError, OpTxBuilder, SequencerClient}; @@ -67,13 +66,12 @@ pub struct OpEthApi { inner: Arc>, /// Sequencer client, configured to forward submitted transactions to sequencer of given OP /// network. - sequencer_client: Arc>, + sequencer_client: Option, } impl OpEthApi { /// Creates a new instance for given context. - #[allow(clippy::type_complexity)] - pub fn with_spawner(ctx: &EthApiBuilderCtx) -> Self { + pub fn new(ctx: &EthApiBuilderCtx, sequencer_http: Option) -> Self { let blocking_task_pool = BlockingTaskPool::build().expect("failed to build blocking task pool"); @@ -93,7 +91,7 @@ impl OpEthApi { ctx.config.proof_permits, ); - Self { inner: Arc::new(inner), sequencer_client: Arc::new(OnceCell::new()) } + Self { inner: Arc::new(inner), sequencer_client: sequencer_http.map(SequencerClient::new) } } } @@ -246,18 +244,6 @@ where } } -impl BuilderProvider for OpEthApi -where - Self: Send, - N: FullNodeComponents, -{ - type Ctx<'a> = &'a EthApiBuilderCtx; - - fn builder() -> Box Fn(Self::Ctx<'a>) -> Self + Send> { - Box::new(Self::with_spawner) - } -} - impl fmt::Debug for OpEthApi { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("OpEthApi").finish_non_exhaustive() diff --git a/crates/optimism/rpc/src/eth/receipt.rs b/crates/optimism/rpc/src/eth/receipt.rs index 200b626d8c3a..f2f09cdc7ff4 100644 --- a/crates/optimism/rpc/src/eth/receipt.rs +++ b/crates/optimism/rpc/src/eth/receipt.rs @@ -227,7 +227,6 @@ impl OpReceiptBuilder { from, to, contract_address, - state_root, authorization_list, } = core_receipt; @@ -265,7 +264,6 @@ impl OpReceiptBuilder { from, to, contract_address, - state_root, authorization_list, }; diff --git a/crates/optimism/rpc/src/eth/transaction.rs b/crates/optimism/rpc/src/eth/transaction.rs index ab7525016a1a..b7575c24416a 100644 --- a/crates/optimism/rpc/src/eth/transaction.rs +++ b/crates/optimism/rpc/src/eth/transaction.rs @@ -1,5 +1,6 @@ //! Loads and formats OP transaction RPC response. +use alloy_consensus::Transaction as _; use alloy_primitives::{Bytes, B256}; use alloy_rpc_types::TransactionInfo; use op_alloy_rpc_types::Transaction; @@ -40,7 +41,7 @@ where // On optimism, transactions are forwarded directly to the sequencer to be included in // blocks that it builds. if let Some(client) = self.raw_tx_forwarder().as_ref() { - tracing::debug!( target: "rpc::eth", "forwarding raw transaction to"); + tracing::debug!(target: "rpc::eth", hash = %pool_transaction.hash(), "forwarding raw transaction to sequencer"); let _ = client.forward_raw_transaction(&tx).await.inspect_err(|err| { tracing::debug!(target: "rpc::eth", %err, hash=% *pool_transaction.hash(), "failed to forward raw transaction"); }); @@ -81,17 +82,9 @@ impl OpEthApi where N: FullNodeComponents, { - /// Sets a [`SequencerClient`] for `eth_sendRawTransaction` to forward transactions to. - pub fn set_sequencer_client( - &self, - sequencer_client: SequencerClient, - ) -> Result<(), tokio::sync::SetError> { - self.sequencer_client.set(sequencer_client) - } - /// Returns the [`SequencerClient`] if one is set. pub fn raw_tx_forwarder(&self) -> Option { - self.sequencer_client.get().cloned() + self.sequencer_client.clone() } } diff --git a/crates/optimism/storage/Cargo.toml b/crates/optimism/storage/Cargo.toml index 107b64db3de0..2b18897d94a0 100644 --- a/crates/optimism/storage/Cargo.toml +++ b/crates/optimism/storage/Cargo.toml @@ -20,4 +20,8 @@ reth-prune-types.workspace = true reth-stages-types.workspace = true [features] -optimism = ["reth-primitives/optimism"] \ No newline at end of file +optimism = [ + "reth-primitives/optimism", + "reth-codecs/optimism", + "reth-db-api/optimism" +] diff --git a/crates/optimism/storage/src/lib.rs b/crates/optimism/storage/src/lib.rs index d435ed1d884a..347b690c5c72 100644 --- a/crates/optimism/storage/src/lib.rs +++ b/crates/optimism/storage/src/lib.rs @@ -16,7 +16,7 @@ mod tests { CompactClientVersion, CompactU256, CompactU64, StoredBlockBodyIndices, StoredBlockOmmers, StoredBlockWithdrawals, }; - use reth_primitives::{Account, Receipt, ReceiptWithBloom, Requests, Withdrawals}; + use reth_primitives::{Account, Receipt, ReceiptWithBloom, Withdrawals}; use reth_prune_types::{PruneCheckpoint, PruneMode, PruneSegment}; use reth_stages_types::{ AccountHashingCheckpoint, CheckpointBlockRange, EntitiesCheckpoint, ExecutionCheckpoint, @@ -74,6 +74,5 @@ mod tests { validate_bitflag_backwards_compat!(StoredBlockWithdrawals, UnusedBits::Zero); validate_bitflag_backwards_compat!(StorageHashingCheckpoint, UnusedBits::NotZero); validate_bitflag_backwards_compat!(Withdrawals, UnusedBits::Zero); - validate_bitflag_backwards_compat!(Requests, UnusedBits::Zero); } } diff --git a/crates/payload/basic/Cargo.toml b/crates/payload/basic/Cargo.toml index 939eb5b54b73..9047768892a9 100644 --- a/crates/payload/basic/Cargo.toml +++ b/crates/payload/basic/Cargo.toml @@ -15,17 +15,18 @@ workspace = true # reth reth-chainspec.workspace = true reth-primitives.workspace = true -reth-revm.workspace = true reth-transaction-pool.workspace = true reth-provider.workspace = true reth-payload-builder.workspace = true reth-payload-primitives.workspace = true reth-tasks.workspace = true +reth-evm.workspace = true # ethereum alloy-rlp.workspace = true alloy-primitives.workspace = true revm.workspace = true +alloy-consensus.workspace = true # async tokio = { workspace = true, features = ["sync", "time"] } diff --git a/crates/payload/basic/src/lib.rs b/crates/payload/basic/src/lib.rs index f9487ec784ca..fcc8be9a88ed 100644 --- a/crates/payload/basic/src/lib.rs +++ b/crates/payload/basic/src/lib.rs @@ -9,22 +9,25 @@ #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] use crate::metrics::PayloadBuilderMetrics; +use alloy_consensus::constants::EMPTY_WITHDRAWALS; use alloy_primitives::{Bytes, B256, U256}; use futures_core::ready; use futures_util::FutureExt; use reth_chainspec::{ChainSpec, EthereumHardforks}; +use reth_evm::state_change::post_block_withdrawals_balance_increments; use reth_payload_builder::{ database::CachedReads, KeepPayloadJobAlive, PayloadId, PayloadJob, PayloadJobGenerator, }; -use reth_payload_primitives::{BuiltPayload, PayloadBuilderAttributes, PayloadBuilderError}; +use reth_payload_primitives::{ + BuiltPayload, PayloadBuilderAttributes, PayloadBuilderError, PayloadKind, +}; use reth_primitives::{ - constants::{EMPTY_WITHDRAWALS, RETH_CLIENT_VERSION, SLOT_DURATION}, + constants::{RETH_CLIENT_VERSION, SLOT_DURATION}, proofs, BlockNumberOrTag, SealedBlock, Withdrawals, }; use reth_provider::{ BlockReaderIdExt, BlockSource, CanonStateNotification, ProviderError, StateProviderFactory, }; -use reth_revm::state_change::post_block_withdrawals_balance_increments; use reth_tasks::TaskSpawner; use reth_transaction_pool::TransactionPool; use revm::{Database, State}; @@ -473,7 +476,10 @@ where Ok(self.config.attributes.clone()) } - fn resolve(&mut self) -> (Self::ResolvePayloadFuture, KeepPayloadJobAlive) { + fn resolve_kind( + &mut self, + kind: PayloadKind, + ) -> (Self::ResolvePayloadFuture, KeepPayloadJobAlive) { let best_payload = self.best_payload.take(); if best_payload.is_none() && self.pending_block.is_none() { @@ -529,7 +535,11 @@ where }; } - let fut = ResolveBestPayload { best_payload, maybe_better, empty_payload }; + let fut = ResolveBestPayload { + best_payload, + maybe_better, + empty_payload: empty_payload.filter(|_| kind != PayloadKind::WaitForPending), + }; (fut, KeepPayloadJobAlive::No) } @@ -789,6 +799,21 @@ impl BuildArguments(self, f: F) -> BuildArguments + where + F: FnOnce(Pool) -> P, + { + BuildArguments { + client: self.client, + pool: f(self.pool), + cached_reads: self.cached_reads, + config: self.config, + cancel: self.cancel, + best_payload: self.best_payload, + } + } } /// A trait for building payloads that encapsulate Ethereum transactions. diff --git a/crates/payload/builder/Cargo.toml b/crates/payload/builder/Cargo.toml index 71f63ce34c2d..3b71011e02e5 100644 --- a/crates/payload/builder/Cargo.toml +++ b/crates/payload/builder/Cargo.toml @@ -40,4 +40,10 @@ tracing.workspace = true revm.workspace = true [features] -test-utils = ["reth-chain-state"] +test-utils = [ + "reth-chain-state", + "reth-chain-state?/test-utils", + "reth-primitives/test-utils", + "reth-provider/test-utils", + "revm/test-utils" +] diff --git a/crates/payload/builder/src/lib.rs b/crates/payload/builder/src/lib.rs index 70b4296da4e6..0df15f5b0de0 100644 --- a/crates/payload/builder/src/lib.rs +++ b/crates/payload/builder/src/lib.rs @@ -28,7 +28,7 @@ //! use std::pin::Pin; //! use std::task::{Context, Poll}; //! use alloy_primitives::U256; -//! use reth_payload_builder::{EthBuiltPayload, PayloadBuilderError, KeepPayloadJobAlive, EthPayloadBuilderAttributes, PayloadJob, PayloadJobGenerator}; +//! use reth_payload_builder::{EthBuiltPayload, PayloadBuilderError, KeepPayloadJobAlive, EthPayloadBuilderAttributes, PayloadJob, PayloadJobGenerator, PayloadKind}; //! use reth_primitives::{Block, Header}; //! //! /// The generator type that creates new jobs that builds empty blocks. @@ -73,7 +73,7 @@ //! Ok(self.attributes.clone()) //! } //! -//! fn resolve(&mut self) -> (Self::ResolvePayloadFuture, KeepPayloadJobAlive) { +//! fn resolve_kind(&mut self, _kind: PayloadKind) -> (Self::ResolvePayloadFuture, KeepPayloadJobAlive) { //! let payload = self.best_payload(); //! (futures_util::future::ready(payload), KeepPayloadJobAlive::No) //! } @@ -112,7 +112,7 @@ pub mod noop; pub mod test_utils; pub use alloy_rpc_types::engine::PayloadId; -pub use reth_payload_primitives::PayloadBuilderError; +pub use reth_payload_primitives::{PayloadBuilderError, PayloadKind}; pub use service::{ PayloadBuilderHandle, PayloadBuilderService, PayloadServiceCommand, PayloadStore, }; diff --git a/crates/payload/builder/src/noop.rs b/crates/payload/builder/src/noop.rs index 06da7dcfada1..cbf21f1cebf2 100644 --- a/crates/payload/builder/src/noop.rs +++ b/crates/payload/builder/src/noop.rs @@ -51,7 +51,7 @@ where } PayloadServiceCommand::BestPayload(_, tx) => tx.send(None).ok(), PayloadServiceCommand::PayloadAttributes(_, tx) => tx.send(None).ok(), - PayloadServiceCommand::Resolve(_, tx) => tx.send(None).ok(), + PayloadServiceCommand::Resolve(_, _, tx) => tx.send(None).ok(), PayloadServiceCommand::Subscribe(_) => None, }; } diff --git a/crates/payload/builder/src/service.rs b/crates/payload/builder/src/service.rs index 1ebf6770c991..853c69e90d83 100644 --- a/crates/payload/builder/src/service.rs +++ b/crates/payload/builder/src/service.rs @@ -11,7 +11,7 @@ use alloy_rpc_types::engine::PayloadId; use futures_util::{future::FutureExt, Stream, StreamExt}; use reth_payload_primitives::{ BuiltPayload, Events, PayloadBuilder, PayloadBuilderAttributes, PayloadBuilderError, - PayloadEvents, PayloadTypes, + PayloadEvents, PayloadKind, PayloadTypes, }; use reth_provider::CanonStateNotification; use std::{ @@ -45,11 +45,20 @@ where /// /// Note: depending on the installed [`PayloadJobGenerator`], this may or may not terminate the /// job, See [`PayloadJob::resolve`]. + pub async fn resolve_kind( + &self, + id: PayloadId, + kind: PayloadKind, + ) -> Option> { + self.inner.resolve_kind(id, kind).await + } + + /// Resolves the payload job and returns the best payload that has been built so far. pub async fn resolve( &self, id: PayloadId, ) -> Option> { - self.inner.resolve(id).await + self.resolve_kind(id, PayloadKind::Earliest).await } /// Returns the best payload for the given identifier. @@ -110,16 +119,13 @@ where type PayloadType = T; type Error = PayloadBuilderError; - async fn send_and_resolve_payload( + fn send_new_payload( &self, attr: ::PayloadBuilderAttributes, - ) -> Result::BuiltPayload>, Self::Error> { - let rx = self.send_new_payload(attr); - let id = rx.await??; - + ) -> Receiver> { let (tx, rx) = oneshot::channel(); - let _ = self.to_service.send(PayloadServiceCommand::Resolve(id, tx)); - rx.await?.ok_or(PayloadBuilderError::MissingPayload) + let _ = self.to_service.send(PayloadServiceCommand::BuildNewPayload(attr, tx)); + rx } /// Note: this does not resolve the job if it's still in progress. @@ -132,21 +138,17 @@ where rx.await.ok()? } - fn send_new_payload( + async fn resolve_kind( &self, - attr: ::PayloadBuilderAttributes, - ) -> Receiver> { + id: PayloadId, + kind: PayloadKind, + ) -> Option> { let (tx, rx) = oneshot::channel(); - let _ = self.to_service.send(PayloadServiceCommand::BuildNewPayload(attr, tx)); - rx - } - - /// Note: if there's already payload in progress with same identifier, it will be returned. - async fn new_payload( - &self, - attr: ::PayloadBuilderAttributes, - ) -> Result { - self.send_new_payload(attr).await? + self.to_service.send(PayloadServiceCommand::Resolve(id, kind, tx)).ok()?; + match rx.await.transpose()? { + Ok(fut) => Some(fut.await), + Err(e) => Some(Err(e.into())), + } } async fn subscribe(&self) -> Result, Self::Error> { @@ -168,19 +170,6 @@ where Self { to_service } } - /// Resolves the payload job and returns the best payload that has been built so far. - /// - /// Note: depending on the installed [`PayloadJobGenerator`], this may or may not terminate the - /// job, See [`PayloadJob::resolve`]. - async fn resolve(&self, id: PayloadId) -> Option> { - let (tx, rx) = oneshot::channel(); - self.to_service.send(PayloadServiceCommand::Resolve(id, tx)).ok()?; - match rx.await.transpose()? { - Ok(fut) => Some(fut.await), - Err(e) => Some(Err(e.into())), - } - } - /// Returns the payload attributes associated with the given identifier. /// /// Note: this returns the attributes of the payload and does not resolve the job. @@ -296,11 +285,15 @@ where /// Returns the best payload for the given identifier that has been built so far and terminates /// the job if requested. - fn resolve(&mut self, id: PayloadId) -> Option> { + fn resolve( + &mut self, + id: PayloadId, + kind: PayloadKind, + ) -> Option> { trace!(%id, "resolving payload job"); let job = self.payload_jobs.iter().position(|(_, job_id)| *job_id == id)?; - let (fut, keep_alive) = self.payload_jobs[job].0.resolve(); + let (fut, keep_alive) = self.payload_jobs[job].0.resolve_kind(kind); if keep_alive == KeepPayloadJobAlive::No { let (_, id) = self.payload_jobs.swap_remove(job); @@ -437,8 +430,8 @@ where let attributes = this.payload_attributes(id); let _ = tx.send(attributes); } - PayloadServiceCommand::Resolve(id, tx) => { - let _ = tx.send(this.resolve(id)); + PayloadServiceCommand::Resolve(id, strategy, tx) => { + let _ = tx.send(this.resolve(id, strategy)); } PayloadServiceCommand::Subscribe(tx) => { let new_rx = this.payload_events.subscribe(); @@ -469,7 +462,11 @@ pub enum PayloadServiceCommand { oneshot::Sender>>, ), /// Resolve the payload and return the payload - Resolve(PayloadId, oneshot::Sender>>), + Resolve( + PayloadId, + /* kind: */ PayloadKind, + oneshot::Sender>>, + ), /// Payload service events Subscribe(oneshot::Sender>>), } @@ -489,7 +486,7 @@ where Self::PayloadAttributes(f0, f1) => { f.debug_tuple("PayloadAttributes").field(&f0).field(&f1).finish() } - Self::Resolve(f0, _f1) => f.debug_tuple("Resolve").field(&f0).finish(), + Self::Resolve(f0, f1, _f2) => f.debug_tuple("Resolve").field(&f0).field(&f1).finish(), Self::Subscribe(f0) => f.debug_tuple("Subscribe").field(&f0).finish(), } } diff --git a/crates/payload/builder/src/test_utils.rs b/crates/payload/builder/src/test_utils.rs index 55b9b84f45ea..6990dc9b1744 100644 --- a/crates/payload/builder/src/test_utils.rs +++ b/crates/payload/builder/src/test_utils.rs @@ -7,7 +7,7 @@ use crate::{ use alloy_primitives::U256; use reth_chain_state::ExecutedBlock; -use reth_payload_primitives::{PayloadBuilderError, PayloadTypes}; +use reth_payload_primitives::{PayloadBuilderError, PayloadKind, PayloadTypes}; use reth_primitives::Block; use reth_provider::CanonStateNotification; use std::{ @@ -96,7 +96,10 @@ impl PayloadJob for TestPayloadJob { Ok(self.attr.clone()) } - fn resolve(&mut self) -> (Self::ResolvePayloadFuture, KeepPayloadJobAlive) { + fn resolve_kind( + &mut self, + _kind: PayloadKind, + ) -> (Self::ResolvePayloadFuture, KeepPayloadJobAlive) { let fut = futures_util::future::ready(self.best_payload()); (fut, KeepPayloadJobAlive::No) } diff --git a/crates/payload/builder/src/traits.rs b/crates/payload/builder/src/traits.rs index 8d448eeff5ad..62dadeb45d7c 100644 --- a/crates/payload/builder/src/traits.rs +++ b/crates/payload/builder/src/traits.rs @@ -1,6 +1,8 @@ //! Trait abstractions used by the payload crate. -use reth_payload_primitives::{BuiltPayload, PayloadBuilderAttributes, PayloadBuilderError}; +use reth_payload_primitives::{ + BuiltPayload, PayloadBuilderAttributes, PayloadBuilderError, PayloadKind, +}; use reth_provider::CanonStateNotification; use std::future::Future; @@ -53,7 +55,21 @@ pub trait PayloadJob: Future> + Send + /// If this returns [`KeepPayloadJobAlive::Yes`], then the [`PayloadJob`] will be polled /// once more. If this returns [`KeepPayloadJobAlive::No`] then the [`PayloadJob`] will be /// dropped after this call. - fn resolve(&mut self) -> (Self::ResolvePayloadFuture, KeepPayloadJobAlive); + /// + /// The [`PayloadKind`] determines how the payload should be resolved in the + /// `ResolvePayloadFuture`. [`PayloadKind::Earliest`] should return the earliest available + /// payload (as fast as possible), e.g. racing an empty payload job against a pending job if + /// there's no payload available yet. [`PayloadKind::WaitForPending`] is allowed to wait + /// until a built payload is available. + fn resolve_kind( + &mut self, + kind: PayloadKind, + ) -> (Self::ResolvePayloadFuture, KeepPayloadJobAlive); + + /// Resolves the payload as fast as possible. + fn resolve(&mut self) -> (Self::ResolvePayloadFuture, KeepPayloadJobAlive) { + self.resolve_kind(PayloadKind::Earliest) + } } /// Whether the payload job should be kept alive or terminated after the payload was requested by diff --git a/crates/payload/primitives/src/lib.rs b/crates/payload/primitives/src/lib.rs index 5d1004051350..08aa428000ed 100644 --- a/crates/payload/primitives/src/lib.rs +++ b/crates/payload/primitives/src/lib.rs @@ -26,7 +26,7 @@ pub use traits::{ mod payload; pub use payload::PayloadOrAttributes; -use reth_chainspec::{ChainSpec, EthereumHardforks}; +use reth_chainspec::EthereumHardforks; /// The types that are used by the engine API. pub trait PayloadTypes: Send + Sync + Unpin + core::fmt::Debug + Clone + 'static { /// The built payload type. @@ -125,8 +125,8 @@ pub fn validate_payload_timestamp( /// Validates the presence of the `withdrawals` field according to the payload timestamp. /// After Shanghai, withdrawals field must be [Some]. /// Before Shanghai, withdrawals field must be [None]; -pub fn validate_withdrawals_presence( - chain_spec: &ChainSpec, +pub fn validate_withdrawals_presence( + chain_spec: &T, version: EngineApiMessageVersion, message_validation_kind: MessageValidationKind, timestamp: u64, @@ -210,8 +210,8 @@ pub fn validate_withdrawals_presence( /// `MessageValidationKind::Payload`, then the error code will be `-32602: Invalid params`. If the /// parameter is `MessageValidationKind::PayloadAttributes`, then the error code will be `-38003: /// Invalid payload attributes`. -pub fn validate_parent_beacon_block_root_presence( - chain_spec: &ChainSpec, +pub fn validate_parent_beacon_block_root_presence( + chain_spec: &T, version: EngineApiMessageVersion, validation_kind: MessageValidationKind, timestamp: u64, @@ -298,13 +298,14 @@ impl MessageValidationKind { /// either an execution payload, or payload attributes. /// /// The version is provided by the [`EngineApiMessageVersion`] argument. -pub fn validate_version_specific_fields( - chain_spec: &ChainSpec, +pub fn validate_version_specific_fields( + chain_spec: &T, version: EngineApiMessageVersion, payload_or_attrs: PayloadOrAttributes<'_, Type>, ) -> Result<(), EngineObjectValidationError> where Type: PayloadAttributes, + T: EthereumHardforks, { validate_withdrawals_presence( chain_spec, @@ -341,6 +342,28 @@ pub enum EngineApiMessageVersion { V4, } +/// Determines how we should choose the payload to return. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub enum PayloadKind { + /// Returns the next best available payload (the earliest available payload). + /// This does not wait for a real for pending job to finish if there's no best payload yet and + /// is allowed to race various payload jobs (empty, pending best) against each other and + /// returns whichever job finishes faster. + /// + /// This should be used when it's more important to return a valid payload as fast as possible. + /// For example, the engine API timeout for `engine_getPayload` is 1s and clients should rather + /// return an empty payload than indefinitely waiting for the pending payload job to finish and + /// risk missing the deadline. + #[default] + Earliest, + /// Only returns once we have at least one built payload. + /// + /// Compared to [`PayloadKind::Earliest`] this does not race an empty payload job against the + /// already in progress one, and returns the best available built payload or awaits the job in + /// progress. + WaitForPending, +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/payload/primitives/src/payload.rs b/crates/payload/primitives/src/payload.rs index 41c2ef1efc23..fc685559e087 100644 --- a/crates/payload/primitives/src/payload.rs +++ b/crates/payload/primitives/src/payload.rs @@ -3,8 +3,10 @@ use alloy_primitives::B256; use alloy_rpc_types::engine::ExecutionPayload; /// Either an [`ExecutionPayload`] or a types that implements the [`PayloadAttributes`] trait. +/// +/// This is a helper type to unify pre-validation of version specific fields of the engine API. #[derive(Debug)] -pub enum PayloadOrAttributes<'a, AttributesType> { +pub enum PayloadOrAttributes<'a, Attributes> { /// An [`ExecutionPayload`] and optional parent beacon block root. ExecutionPayload { /// The inner execution payload @@ -13,13 +15,10 @@ pub enum PayloadOrAttributes<'a, AttributesType> { parent_beacon_block_root: Option, }, /// A payload attributes type. - PayloadAttributes(&'a AttributesType), + PayloadAttributes(&'a Attributes), } -impl<'a, AttributesType> PayloadOrAttributes<'a, AttributesType> -where - AttributesType: PayloadAttributes, -{ +impl<'a, Attributes> PayloadOrAttributes<'a, Attributes> { /// Construct a [`PayloadOrAttributes`] from an [`ExecutionPayload`] and optional parent beacon /// block root. pub const fn from_execution_payload( @@ -29,6 +28,16 @@ where Self::ExecutionPayload { payload, parent_beacon_block_root } } + /// Construct a [`PayloadOrAttributes::PayloadAttributes`] variant + pub const fn from_attributes(attributes: &'a Attributes) -> Self { + Self::PayloadAttributes(attributes) + } +} + +impl PayloadOrAttributes<'_, Attributes> +where + Attributes: PayloadAttributes, +{ /// Return the withdrawals for the payload or attributes. pub fn withdrawals(&self) -> Option<&Vec> { match self { diff --git a/crates/payload/primitives/src/traits.rs b/crates/payload/primitives/src/traits.rs index 494ed68aa4ee..ce98fcad32ea 100644 --- a/crates/payload/primitives/src/traits.rs +++ b/crates/payload/primitives/src/traits.rs @@ -1,4 +1,4 @@ -use crate::{PayloadBuilderError, PayloadEvents, PayloadTypes}; +use crate::{PayloadEvents, PayloadKind, PayloadTypes}; use alloy_primitives::{Address, B256, U256}; use alloy_rpc_types::{ engine::{PayloadAttributes as EthPayloadAttributes, PayloadId}, @@ -7,12 +7,8 @@ use alloy_rpc_types::{ use op_alloy_rpc_types_engine::OpPayloadAttributes; use reth_chain_state::ExecutedBlock; use reth_primitives::{SealedBlock, Withdrawals}; -use std::{future::Future, pin::Pin}; use tokio::sync::oneshot; -pub(crate) type PayloadFuture

= - Pin> + Send + Sync>>; - /// A type that can request, subscribe to and resolve payloads. #[async_trait::async_trait] pub trait PayloadBuilder: Send + Unpin { @@ -21,12 +17,13 @@ pub trait PayloadBuilder: Send + Unpin { /// The error type returned by the builder. type Error; - /// Sends a message to the service to start building a new payload for the given payload - /// attributes and returns a future that resolves to the payload. - async fn send_and_resolve_payload( + /// Sends a message to the service to start building a new payload for the given payload. + /// + /// Returns a receiver that will receive the payload id. + fn send_new_payload( &self, attr: ::PayloadBuilderAttributes, - ) -> Result::BuiltPayload>, Self::Error>; + ) -> oneshot::Receiver>; /// Returns the best payload for the given identifier. async fn best_payload( @@ -34,22 +31,21 @@ pub trait PayloadBuilder: Send + Unpin { id: PayloadId, ) -> Option::BuiltPayload, Self::Error>>; - /// Sends a message to the service to start building a new payload for the given payload. - /// - /// This is the same as [`PayloadBuilder::new_payload`] but does not wait for the result - /// and returns the receiver instead - fn send_new_payload( + /// Resolves the payload job and returns the best payload that has been built so far. + async fn resolve_kind( &self, - attr: ::PayloadBuilderAttributes, - ) -> oneshot::Receiver>; + id: PayloadId, + kind: PayloadKind, + ) -> Option::BuiltPayload, Self::Error>>; - /// Starts building a new payload for the given payload attributes. - /// - /// Returns the identifier of the payload. - async fn new_payload( + /// Resolves the payload job as fast and possible and returns the best payload that has been + /// built so far. + async fn resolve( &self, - attr: ::PayloadBuilderAttributes, - ) -> Result; + id: PayloadId, + ) -> Option::BuiltPayload, Self::Error>> { + self.resolve_kind(id, PayloadKind::Earliest).await + } /// Sends a message to the service to subscribe to payload events. /// Returns a receiver that will receive them. diff --git a/crates/payload/validator/Cargo.toml b/crates/payload/validator/Cargo.toml index 2662b987f889..619b99f28de2 100644 --- a/crates/payload/validator/Cargo.toml +++ b/crates/payload/validator/Cargo.toml @@ -18,4 +18,5 @@ reth-primitives.workspace = true reth-rpc-types-compat.workspace = true # alloy +alloy-eips.workspace = true alloy-rpc-types = { workspace = true, features = ["engine"] } diff --git a/crates/payload/validator/src/lib.rs b/crates/payload/validator/src/lib.rs index 55002b0a98be..3ec7b206a5b1 100644 --- a/crates/payload/validator/src/lib.rs +++ b/crates/payload/validator/src/lib.rs @@ -8,6 +8,7 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +use alloy_eips::eip7685::Requests; use alloy_rpc_types::engine::{ExecutionPayload, MaybeCancunPayloadFields, PayloadError}; use reth_chainspec::EthereumHardforks; use reth_primitives::SealedBlock; @@ -112,12 +113,14 @@ impl ExecutionPayloadValidator { &self, payload: ExecutionPayload, cancun_fields: MaybeCancunPayloadFields, + execution_requests: Option, ) -> Result { let expected_hash = payload.block_hash(); // First parse the block let sealed_block = - try_into_block(payload, cancun_fields.parent_beacon_block_root())?.seal_slow(); + try_into_block(payload, cancun_fields.parent_beacon_block_root(), execution_requests)? + .seal_slow(); // Ensure the hash included in the payload matches the block hash if expected_hash != sealed_block.hash() { @@ -162,7 +165,7 @@ impl ExecutionPayloadValidator { let shanghai_active = self.is_shanghai_active_at_timestamp(sealed_block.timestamp); if !shanghai_active && sealed_block.body.withdrawals.is_some() { // shanghai not active but withdrawals present - return Err(PayloadError::PreShanghaiBlockWithWitdrawals) + return Err(PayloadError::PreShanghaiBlockWithWithdrawals) } if !self.is_prague_active_at_timestamp(sealed_block.timestamp) && diff --git a/crates/primitives-traits/Cargo.toml b/crates/primitives-traits/Cargo.toml index b34987327ee8..9634da40f47c 100644 --- a/crates/primitives-traits/Cargo.toml +++ b/crates/primitives-traits/Cargo.toml @@ -54,14 +54,30 @@ test-fuzz.workspace = true [features] default = ["std"] -std = [] -test-utils = ["arbitrary"] +std = [ + "alloy-consensus/std", + "alloy-eips/std", + "alloy-genesis/std", + "alloy-primitives/std", + "revm-primitives/std", + "serde/std" +] +test-utils = [ + "arbitrary", + "reth-codecs/test-utils" +] arbitrary = [ - "std", - "alloy-consensus/arbitrary", - "alloy-primitives/arbitrary", - "dep:arbitrary", - "dep:proptest", - "dep:proptest-arbitrary-interop", + "std", + "alloy-consensus/arbitrary", + "alloy-primitives/arbitrary", + "dep:arbitrary", + "dep:proptest", + "dep:proptest-arbitrary-interop", + "alloy-eips/arbitrary", + "revm-primitives/arbitrary" +] +serde-bincode-compat = [ + "serde_with", + "alloy-consensus/serde-bincode-compat", + "alloy-eips/serde-bincode-compat" ] -serde-bincode-compat = ["serde_with", "alloy-consensus/serde-bincode-compat"] \ No newline at end of file diff --git a/crates/primitives/src/traits/block/body.rs b/crates/primitives-traits/src/block/body.rs similarity index 54% rename from crates/primitives/src/traits/block/body.rs rename to crates/primitives-traits/src/block/body.rs index ff8f71b76162..c9b673ec7241 100644 --- a/crates/primitives/src/traits/block/body.rs +++ b/crates/primitives-traits/src/block/body.rs @@ -1,12 +1,12 @@ //! Block body abstraction. -use alloc::fmt; -use core::ops; +use alloc::{fmt, vec::Vec}; use alloy_consensus::{BlockHeader, Transaction, TxType}; +use alloy_eips::{eip4895::Withdrawal, eip7685::Requests}; use alloy_primitives::{Address, B256}; -use crate::{proofs, traits::Block, Requests, Withdrawals}; +use crate::Block; /// Abstraction for block's body. pub trait BlockBody: @@ -27,17 +27,20 @@ pub trait BlockBody: /// Header type (uncle blocks). type Header: BlockHeader; + /// Withdrawals in block. + type Withdrawals: Iterator; + /// Returns reference to transactions in block. fn transactions(&self) -> &[Self::SignedTransaction]; - /// Returns [`Withdrawals`] in the block, if any. + /// Returns `Withdrawals` in the block, if any. // todo: branch out into extension trait - fn withdrawals(&self) -> Option<&Withdrawals>; + fn withdrawals(&self) -> Option<&Self::Withdrawals>; /// Returns reference to uncle block headers. fn ommers(&self) -> &[Self::Header]; - /// Returns [`Request`] in block, if any. + /// Returns [`Requests`] in block, if any. fn requests(&self) -> Option<&Requests>; /// Create a [`Block`] from the body and its header. @@ -53,32 +56,26 @@ pub trait BlockBody: /// 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()?)) - } + // todo: can be default impl if `calculate_withdrawals_root` made into a method on + // `Withdrawals` and `Withdrawals` moved to alloy + fn calculate_withdrawals_root(&self) -> Option; /// 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) + self.transactions().iter().any(|tx| tx.ty() == 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) + self.transactions().iter().any(|tx| tx.ty() == 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) + self.transactions().iter().filter(|tx| tx.ty() == TxType::Eip4844 as u8) } /// Returns only the blob transactions, if any, from the block body. @@ -97,56 +94,3 @@ pub trait BlockBody: /// 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-traits/src/block/mod.rs b/crates/primitives-traits/src/block/mod.rs new file mode 100644 index 000000000000..395cf61df145 --- /dev/null +++ b/crates/primitives-traits/src/block/mod.rs @@ -0,0 +1,106 @@ +//! Block abstraction. + +pub mod body; + +use alloc::{fmt, vec::Vec}; + +use alloy_consensus::BlockHeader; +use alloy_primitives::{Address, Sealable, B256}; + +use crate::BlockBody; + +/// Helper trait, unifies behaviour required of a block header. +pub trait Header: BlockHeader + Sealable {} + +impl Header for T where T: BlockHeader + Sealable {} + +/// Abstraction of block data type. +// todo: make sealable super-trait, depends on +// todo: make with senders extension trait, so block can be impl by block type already containing +// senders +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: Header; + + /// The block's body contains the transactions in the block. + type Body: BlockBody; + + /// A block and block hash. + type SealedBlock; + + /// A block and addresses of senders of transactions in it. + type BlockWithSenders; + + /// 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. + // todo: can be default impl if sealed block type is made generic over header and body and + // migrated to alloy + fn seal_slow(self) -> Self::SealedBlock; + + /// Seal the block with a known hash. + /// + /// WARNING: This method does not perform validation whether the hash is correct. + // todo: can be default impl if sealed block type is made generic over header and body and + // migrated to alloy + fn seal(self, hash: B256) -> Self::SealedBlock; + + /// Expensive operation that recovers transaction signer. See + /// `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

) -> Self::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 `SignedTransaction::recover_signer_unchecked`. + /// + /// Returns an error if a signature is invalid. + // todo: can be default impl if block with senders type is made generic over block and migrated + // to alloy + #[track_caller] + fn try_with_senders_unchecked( + self, + senders: Vec
, + ) -> Result, Self>; + + /// **Expensive**. Transform into a `BlockWithSenders` by recovering senders in the contained + /// transactions. + /// + /// Returns `None` if a transaction is invalid. + // todo: can be default impl if sealed block type is made generic over header and body and + // migrated to alloy + fn with_recovered_senders(self) -> Option>; + + /// Calculates a heuristic for the in-memory size of the [`Block`]. + fn size(&self) -> usize; +} diff --git a/crates/primitives-traits/src/constants/mod.rs b/crates/primitives-traits/src/constants/mod.rs index ed5c893dd6c2..482852bdccd4 100644 --- a/crates/primitives-traits/src/constants/mod.rs +++ b/crates/primitives-traits/src/constants/mod.rs @@ -13,9 +13,6 @@ pub const RETH_CLIENT_VERSION: &str = concat!("reth/v", env!("CARGO_PKG_VERSION" /// The first four bytes of the call data for a function call specifies the function to be called. pub const SELECTOR_LEN: usize = 4; -/// Maximum extra data size in a block after genesis -pub const MAXIMUM_EXTRA_DATA_SIZE: usize = 32; - /// An EPOCH is a series of 32 slots. pub const EPOCH_SLOTS: u64 = 32; @@ -97,11 +94,6 @@ pub const FINNEY_TO_WEI: u128 = (GWEI_TO_WEI as u128) * 1_000_000; /// Multiplier for converting ether to wei. pub const ETH_TO_WEI: u128 = FINNEY_TO_WEI * 1000; -/// The Ethereum mainnet genesis hash: -/// `0x0d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3` -pub const MAINNET_GENESIS_HASH: B256 = - b256!("d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"); - /// Sepolia genesis hash: `0x25a5cc106eea7138acab33231d7160d69cb777ee0c2c553fcddf5138993e6dd9` pub const SEPOLIA_GENESIS_HASH: B256 = b256!("25a5cc106eea7138acab33231d7160d69cb777ee0c2c553fcddf5138993e6dd9"); @@ -110,37 +102,12 @@ pub const SEPOLIA_GENESIS_HASH: B256 = pub const HOLESKY_GENESIS_HASH: B256 = b256!("b5f7f912443c940f21fd611f12828d75b534364ed9e95ca4e307729a4661bde4"); -/// Testnet genesis hash: `0x2f980576711e3617a5e4d83dd539548ec0f7792007d505a3d2e9674833af2d7c` -pub const DEV_GENESIS_HASH: B256 = - b256!("2f980576711e3617a5e4d83dd539548ec0f7792007d505a3d2e9674833af2d7c"); - -/// Keccak256 over empty array: `0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470` -pub const KECCAK_EMPTY: B256 = - b256!("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); - -/// Ommer root of empty list: `0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347` -pub const EMPTY_OMMER_ROOT_HASH: B256 = - b256!("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"); - -/// Root hash of an empty trie: `0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421` -pub const EMPTY_ROOT_HASH: B256 = - b256!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"); - /// From address from Optimism system txs: `0xdeaddeaddeaddeaddeaddeaddeaddeaddead0001` pub const OP_SYSTEM_TX_FROM_ADDR: Address = address!("deaddeaddeaddeaddeaddeaddeaddeaddead0001"); /// To address from Optimism system txs: `0x4200000000000000000000000000000000000015` pub const OP_SYSTEM_TX_TO_ADDR: Address = address!("4200000000000000000000000000000000000015"); -/// Transactions root of empty receipts set. -pub const EMPTY_RECEIPTS: B256 = EMPTY_ROOT_HASH; - -/// Transactions root of empty transactions set. -pub const EMPTY_TRANSACTIONS: B256 = EMPTY_ROOT_HASH; - -/// Withdrawals root of empty withdrawals set. -pub const EMPTY_WITHDRAWALS: B256 = EMPTY_ROOT_HASH; - /// The number of blocks to unwind during a reorg that already became a part of canonical chain. /// /// In reality, the node can end up in this particular situation very rarely. It would happen only diff --git a/crates/primitives-traits/src/header/test_utils.rs b/crates/primitives-traits/src/header/test_utils.rs index ef5c0d025360..c5f6e86b9db1 100644 --- a/crates/primitives-traits/src/header/test_utils.rs +++ b/crates/primitives-traits/src/header/test_utils.rs @@ -37,7 +37,7 @@ pub const fn generate_valid_header( } // Placeholder for future EIP adjustments - header.requests_root = None; + header.requests_hash = None; header } diff --git a/crates/primitives-traits/src/lib.rs b/crates/primitives-traits/src/lib.rs index dd68607f5911..a77669ec367e 100644 --- a/crates/primitives-traits/src/lib.rs +++ b/crates/primitives-traits/src/lib.rs @@ -24,13 +24,13 @@ pub mod receipt; pub use receipt::Receipt; pub mod transaction; -pub use transaction::Transaction; +pub use transaction::{signed::SignedTransaction, Transaction}; mod integer_list; pub use integer_list::{IntegerList, IntegerListError}; -pub mod request; -pub use request::{Request, Requests}; +pub mod block; +pub use block::{body::BlockBody, Block}; mod withdrawal; pub use withdrawal::{Withdrawal, Withdrawals}; diff --git a/crates/primitives-traits/src/request.rs b/crates/primitives-traits/src/request.rs deleted file mode 100644 index c08af3fd6225..000000000000 --- a/crates/primitives-traits/src/request.rs +++ /dev/null @@ -1,58 +0,0 @@ -//! EIP-7685 requests. - -use alloc::vec::Vec; -pub use alloy_consensus::Request; -use alloy_eips::eip7685::{Decodable7685, Encodable7685}; -use alloy_rlp::{Decodable, Encodable}; -use derive_more::{Deref, DerefMut, From, IntoIterator}; -use reth_codecs::{add_arbitrary_tests, Compact}; -use revm_primitives::Bytes; -use serde::{Deserialize, Serialize}; - -/// A list of EIP-7685 requests. -#[derive( - Debug, - Clone, - PartialEq, - Eq, - Default, - Hash, - Deref, - DerefMut, - From, - IntoIterator, - Serialize, - Deserialize, - Compact, -)] -#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))] -#[add_arbitrary_tests(compact)] -pub struct Requests(pub Vec); - -impl Encodable for Requests { - fn encode(&self, out: &mut dyn bytes::BufMut) { - let mut h = alloy_rlp::Header { list: true, payload_length: 0 }; - - let mut encoded = Vec::new(); - for req in &self.0 { - let encoded_req = req.encoded_7685(); - h.payload_length += encoded_req.len(); - encoded.push(Bytes::from(encoded_req)); - } - - h.encode(out); - for req in encoded { - req.encode(out); - } - } -} - -impl Decodable for Requests { - fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { - Ok( as Decodable>::decode(buf)? - .into_iter() - .map(|bytes| Request::decode_7685(&mut bytes.as_ref())) - .collect::, alloy_eips::eip7685::Eip7685Error>>() - .map(Self)?) - } -} diff --git a/crates/primitives-traits/src/transaction.rs b/crates/primitives-traits/src/transaction/mod.rs similarity index 97% rename from crates/primitives-traits/src/transaction.rs rename to crates/primitives-traits/src/transaction/mod.rs index 93645ead82e8..a306c5f76ed5 100644 --- a/crates/primitives-traits/src/transaction.rs +++ b/crates/primitives-traits/src/transaction/mod.rs @@ -1,5 +1,7 @@ //! Transaction abstraction +pub mod signed; + use alloc::fmt; use reth_codecs::Compact; diff --git a/crates/primitives-traits/src/transaction/signed.rs b/crates/primitives-traits/src/transaction/signed.rs new file mode 100644 index 000000000000..1bc8308b13f0 --- /dev/null +++ b/crates/primitives-traits/src/transaction/signed.rs @@ -0,0 +1,72 @@ +//! API of a signed transaction. + +use alloc::fmt; +use core::hash::Hash; + +use alloy_consensus::Transaction; +use alloy_eips::eip2718::{Decodable2718, Encodable2718}; +use alloy_primitives::{keccak256, Address, TxHash, B256}; + +/// A signed transaction. +pub trait SignedTransaction: + fmt::Debug + + Clone + + PartialEq + + Eq + + Hash + + Send + + Sync + + serde::Serialize + + for<'a> serde::Deserialize<'a> + + alloy_rlp::Encodable + + alloy_rlp::Decodable + + Encodable2718 + + Decodable2718 +{ + /// Transaction type that is signed. + type Transaction: Transaction; + + /// Signature type that results from signing transaction. + type Signature; + + /// Returns reference to transaction hash. + fn tx_hash(&self) -> &TxHash; + + /// Returns reference to transaction. + fn transaction(&self) -> &Self::Transaction; + + /// Returns reference to signature. + fn signature(&self) -> &Self::Signature; + + /// Recover signer from signature and hash. + /// + /// Returns `None` if the transaction's signature is invalid following [EIP-2](https://eips.ethereum.org/EIPS/eip-2), see also `reth_primitives::transaction::recover_signer`. + /// + /// Note: + /// + /// This can fail for some early ethereum mainnet transactions pre EIP-2, use + /// [`Self::recover_signer_unchecked`] if you want to recover the signer without ensuring that + /// the signature has a low `s` value. + fn recover_signer(&self) -> Option
; + + /// Recover signer from signature and hash _without ensuring that the signature has a low `s` + /// value_. + /// + /// Returns `None` if the transaction's signature is invalid, see also + /// `reth_primitives::transaction::recover_signer_unchecked`. + fn recover_signer_unchecked(&self) -> Option
; + + /// Create a new signed transaction from a transaction and its signature. + /// + /// This will also calculate the transaction hash using its encoding. + fn from_transaction_and_signature( + transaction: Self::Transaction, + signature: Self::Signature, + ) -> Self; + + /// Calculate transaction hash, eip2728 transaction does not contain rlp header and start with + /// tx type. + fn recalculate_hash(&self) -> B256 { + keccak256(self.encoded_2718()) + } +} diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index 8596f8d766c5..566a114bebf9 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -60,7 +60,6 @@ zstd = { workspace = true, features = ["experimental"], optional = true } # arbitrary utils arbitrary = { workspace = true, features = ["derive"], optional = true } -proptest = { workspace = true, optional = true } [dev-dependencies] # eth @@ -76,7 +75,6 @@ alloy-genesis.workspace = true arbitrary = { workspace = true, features = ["derive"] } assert_matches.workspace = true bincode.workspace = true -modular-bitfield.workspace = true proptest-arbitrary-interop.workspace = true proptest.workspace = true rand.workspace = true @@ -92,19 +90,41 @@ pprof = { workspace = true, features = [ [features] default = ["c-kzg", "alloy-compat", "std", "reth-codec", "secp256k1"] -std = ["reth-primitives-traits/std"] +std = [ + "reth-primitives-traits/std", + "alloy-consensus/std", + "alloy-eips/std", + "alloy-genesis/std", + "alloy-primitives/std", + "alloy-serde?/std", + "k256/std", + "once_cell/std", + "revm-primitives/std", + "secp256k1?/std", + "serde/std" +] reth-codec = ["dep:reth-codecs", "dep:zstd", "dep:modular-bitfield", "std"] -asm-keccak = ["alloy-primitives/asm-keccak"] +asm-keccak = [ + "alloy-primitives/asm-keccak", + "revm-primitives/asm-keccak" +] arbitrary = [ - "dep:arbitrary", - "dep:proptest", - "alloy-eips/arbitrary", - "rand", - "reth-codec", - "reth-ethereum-forks/arbitrary", - "reth-primitives-traits/arbitrary", - "revm-primitives/arbitrary", - "secp256k1", + "dep:arbitrary", + "alloy-eips/arbitrary", + "rand", + "reth-codec", + "reth-ethereum-forks/arbitrary", + "reth-primitives-traits/arbitrary", + "revm-primitives/arbitrary", + "secp256k1", + "reth-chainspec/arbitrary", + "reth-trie-common/arbitrary", + "alloy-consensus/arbitrary", + "alloy-primitives/arbitrary", + "alloy-rpc-types?/arbitrary", + "alloy-serde?/arbitrary", + "op-alloy-consensus?/arbitrary", + "op-alloy-rpc-types?/arbitrary" ] secp256k1 = ["dep:secp256k1"] c-kzg = [ @@ -124,12 +144,18 @@ alloy-compat = [ "dep:alloy-serde", "dep:op-alloy-rpc-types", ] -test-utils = ["reth-primitives-traits/test-utils"] +test-utils = [ + "reth-primitives-traits/test-utils", + "reth-chainspec/test-utils", + "reth-codecs?/test-utils", + "reth-trie-common/test-utils" +] serde-bincode-compat = [ - "alloy-consensus/serde-bincode-compat", - "op-alloy-consensus?/serde-bincode-compat", - "reth-primitives-traits/serde-bincode-compat", - "serde_with", + "alloy-consensus/serde-bincode-compat", + "op-alloy-consensus?/serde-bincode-compat", + "reth-primitives-traits/serde-bincode-compat", + "serde_with", + "alloy-eips/serde-bincode-compat" ] [[bench]] diff --git a/crates/primitives/src/alloy_compat.rs b/crates/primitives/src/alloy_compat.rs index 65099967e6e9..917baef66612 100644 --- a/crates/primitives/src/alloy_compat.rs +++ b/crates/primitives/src/alloy_compat.rs @@ -1,11 +1,13 @@ //! Common conversions from alloy types. use crate::{ - constants::EMPTY_TRANSACTIONS, transaction::extract_chain_id, Block, BlockBody, Signature, - Transaction, TransactionSigned, TransactionSignedEcRecovered, TransactionSignedNoHash, TxType, + transaction::extract_chain_id, Block, BlockBody, Signature, Transaction, TransactionSigned, + TransactionSignedEcRecovered, TransactionSignedNoHash, TxType, }; use alloc::{string::ToString, vec::Vec}; -use alloy_consensus::{TxEip1559, TxEip2930, TxEip4844, TxLegacy}; +use alloy_consensus::{ + constants::EMPTY_TRANSACTIONS, Transaction as _, TxEip1559, TxEip2930, TxEip4844, TxLegacy, +}; use alloy_primitives::{Parity, TxKind}; use alloy_rlp::Error as RlpError; use alloy_serde::WithOtherFields; @@ -46,9 +48,6 @@ impl TryFrom Option { - None - } -} - /// Ethereum full block. /// /// Withdrawals can be optionally included at the end of the RLP encoded message. @@ -122,7 +109,6 @@ mod block_rlp { transactions: Vec, ommers: Vec
, withdrawals: Option, - requests: Option, } #[derive(RlpEncodable)] @@ -132,50 +118,34 @@ mod block_rlp { transactions: &'a Vec, ommers: &'a Vec
, withdrawals: Option<&'a Withdrawals>, - requests: Option<&'a Requests>, } impl<'a> From<&'a Block> for HelperRef<'a, Header> { fn from(block: &'a Block) -> Self { - let Block { header, body: BlockBody { transactions, ommers, withdrawals, requests } } = - block; - Self { - header, - transactions, - ommers, - withdrawals: withdrawals.as_ref(), - requests: requests.as_ref(), - } + let Block { header, body: BlockBody { transactions, ommers, withdrawals } } = block; + Self { header, transactions, ommers, withdrawals: withdrawals.as_ref() } } } impl<'a> From<&'a SealedBlock> for HelperRef<'a, SealedHeader> { fn from(block: &'a SealedBlock) -> Self { - let SealedBlock { - header, - body: BlockBody { transactions, ommers, withdrawals, requests }, - } = block; - Self { - header, - transactions, - ommers, - withdrawals: withdrawals.as_ref(), - requests: requests.as_ref(), - } + let SealedBlock { header, body: BlockBody { transactions, ommers, withdrawals } } = + block; + Self { header, transactions, ommers, withdrawals: withdrawals.as_ref() } } } impl Decodable for Block { fn decode(b: &mut &[u8]) -> alloy_rlp::Result { - let Helper { header, transactions, ommers, withdrawals, requests } = Helper::decode(b)?; - Ok(Self { header, body: BlockBody { transactions, ommers, withdrawals, requests } }) + let Helper { header, transactions, ommers, withdrawals } = Helper::decode(b)?; + Ok(Self { header, body: BlockBody { transactions, ommers, withdrawals } }) } } impl Decodable for SealedBlock { fn decode(b: &mut &[u8]) -> alloy_rlp::Result { - let Helper { header, transactions, ommers, withdrawals, requests } = Helper::decode(b)?; - Ok(Self { header, body: BlockBody { transactions, ommers, withdrawals, requests } }) + let Helper { header, transactions, ommers, withdrawals } = Helper::decode(b)?; + Ok(Self { header, body: BlockBody { transactions, ommers, withdrawals } }) } } @@ -217,13 +187,7 @@ impl<'a> arbitrary::Arbitrary<'a> for Block { Ok(Self { header: u.arbitrary()?, - body: BlockBody { - transactions, - ommers, - // for now just generate empty requests, see HACK above - requests: u.arbitrary()?, - withdrawals: u.arbitrary()?, - }, + body: BlockBody { transactions, ommers, withdrawals: u.arbitrary()? }, }) } } @@ -572,8 +536,6 @@ pub struct BlockBody { pub ommers: Vec
, /// Withdrawals in the block. pub withdrawals: Option, - /// Requests in the block. - pub requests: Option, } impl BlockBody { @@ -598,12 +560,6 @@ impl BlockBody { self.withdrawals.as_ref().map(|w| crate::proofs::calculate_withdrawals_root(w)) } - /// Calculate the requests root for the block body, if requests exist. If there are no - /// requests, this will return `None`. - pub fn calculate_requests_root(&self) -> Option { - self.requests.as_ref().map(|r| crate::proofs::calculate_requests_root(&r.0)) - } - /// Recover signer addresses for all transactions in the block body. pub fn recover_signers(&self) -> Option> { TransactionSigned::recover_signers(&self.transactions, self.transactions.len()) @@ -672,7 +628,6 @@ impl From for BlockBody { transactions: block.body.transactions, ommers: block.body.ommers, withdrawals: block.body.withdrawals, - requests: block.body.requests, } } } @@ -694,8 +649,7 @@ impl<'a> arbitrary::Arbitrary<'a> for BlockBody { }) .collect::>>()?; - // for now just generate empty requests, see HACK above - Ok(Self { transactions, ommers, requests: None, withdrawals: u.arbitrary()? }) + Ok(Self { transactions, ommers, withdrawals: u.arbitrary()? }) } } @@ -705,7 +659,7 @@ pub(super) mod serde_bincode_compat { use alloc::{borrow::Cow, vec::Vec}; use alloy_consensus::serde_bincode_compat::Header; use alloy_primitives::Address; - use reth_primitives_traits::{serde_bincode_compat::SealedHeader, Requests, Withdrawals}; + use reth_primitives_traits::{serde_bincode_compat::SealedHeader, Withdrawals}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_with::{DeserializeAs, SerializeAs}; @@ -731,7 +685,6 @@ pub(super) mod serde_bincode_compat { transactions: Vec>, ommers: Vec>, withdrawals: Cow<'a, Option>, - requests: Cow<'a, Option>, } impl<'a> From<&'a super::BlockBody> for BlockBody<'a> { @@ -740,7 +693,6 @@ pub(super) mod serde_bincode_compat { transactions: value.transactions.iter().map(Into::into).collect(), ommers: value.ommers.iter().map(Into::into).collect(), withdrawals: Cow::Borrowed(&value.withdrawals), - requests: Cow::Borrowed(&value.requests), } } } @@ -751,7 +703,6 @@ pub(super) mod serde_bincode_compat { transactions: value.transactions.into_iter().map(Into::into).collect(), ommers: value.ommers.into_iter().map(Into::into).collect(), withdrawals: value.withdrawals.into_owned(), - requests: value.requests.into_owned(), } } } diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index ec65cbf20e52..7a9a6bf457fb 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -39,16 +39,13 @@ pub use block::{ }; #[cfg(feature = "reth-codec")] pub use compression::*; -pub use constants::{ - DEV_GENESIS_HASH, EMPTY_OMMER_ROOT_HASH, HOLESKY_GENESIS_HASH, KECCAK_EMPTY, - MAINNET_GENESIS_HASH, SEPOLIA_GENESIS_HASH, -}; +pub use constants::{HOLESKY_GENESIS_HASH, SEPOLIA_GENESIS_HASH}; pub use receipt::{ gas_spent_by_transactions, Receipt, ReceiptWithBloom, ReceiptWithBloomRef, Receipts, }; pub use reth_primitives_traits::{ logs_bloom, Account, Bytecode, GotExpected, GotExpectedBoxed, Header, HeaderError, Log, - LogData, Request, Requests, SealedHeader, StorageEntry, Withdrawal, Withdrawals, + LogData, SealedHeader, StorageEntry, Withdrawal, Withdrawals, }; pub use static_file::StaticFileSegment; @@ -64,8 +61,6 @@ pub use transaction::{ util::secp256k1::{public_key_to_address, recover_signer_unchecked, sign_message}, InvalidTransactionError, Signature, Transaction, TransactionMeta, TransactionSigned, TransactionSignedEcRecovered, TransactionSignedNoHash, TxHashOrNumber, TxType, - EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID, EIP4844_TX_TYPE_ID, EIP7702_TX_TYPE_ID, - LEGACY_TX_TYPE_ID, }; // Re-exports diff --git a/crates/primitives/src/proofs.rs b/crates/primitives/src/proofs.rs index a12a5d6be89f..1697246702a4 100644 --- a/crates/primitives/src/proofs.rs +++ b/crates/primitives/src/proofs.rs @@ -1,11 +1,11 @@ //! Helper function for calculating Merkle proofs and hashes. use crate::{ - constants::EMPTY_OMMER_ROOT_HASH, Header, Receipt, ReceiptWithBloom, ReceiptWithBloomRef, - Request, TransactionSigned, Withdrawal, + Header, Receipt, ReceiptWithBloom, ReceiptWithBloomRef, TransactionSigned, Withdrawal, }; use alloc::vec::Vec; -use alloy_eips::{eip2718::Encodable2718, eip7685::Encodable7685}; +use alloy_consensus::EMPTY_OMMER_ROOT_HASH; +use alloy_eips::eip2718::Encodable2718; use alloy_primitives::{keccak256, B256}; use reth_trie_common::root::{ordered_trie_root, ordered_trie_root_with_encoder}; @@ -29,13 +29,6 @@ pub fn calculate_receipt_root(receipts: &[ReceiptWithBloom]) -> B256 { ordered_trie_root_with_encoder(receipts, |r, buf| r.encode_inner(buf, false)) } -/// Calculate [EIP-7685](https://eips.ethereum.org/EIPS/eip-7685) requests root. -/// -/// NOTE: The requests are encoded as `id + request` -pub fn calculate_requests_root(requests: &[Request]) -> B256 { - ordered_trie_root_with_encoder(requests, |item, buf| item.encode_7685(buf)) -} - /// Calculates the receipt root for a header. pub fn calculate_receipt_root_ref(receipts: &[ReceiptWithBloomRef<'_>]) -> B256 { ordered_trie_root_with_encoder(receipts, |r, buf| r.encode_inner(buf, false)) @@ -65,7 +58,8 @@ pub fn calculate_ommers_root(ommers: &[Header]) -> B256 { #[cfg(test)] mod tests { use super::*; - use crate::{constants::EMPTY_ROOT_HASH, Block}; + use crate::Block; + use alloy_consensus::EMPTY_ROOT_HASH; use alloy_genesis::GenesisAccount; use alloy_primitives::{b256, hex_literal::hex, Address, U256}; use alloy_rlp::Decodable; diff --git a/crates/primitives/src/receipt.rs b/crates/primitives/src/receipt.rs index cfd831ed0f74..b117f8d96156 100644 --- a/crates/primitives/src/receipt.rs +++ b/crates/primitives/src/receipt.rs @@ -1,24 +1,24 @@ #[cfg(feature = "reth-codec")] use crate::compression::{RECEIPT_COMPRESSOR, RECEIPT_DECOMPRESSOR}; -use crate::{ - logs_bloom, TxType, EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID, EIP4844_TX_TYPE_ID, - EIP7702_TX_TYPE_ID, -}; +use crate::{logs_bloom, TxType}; use alloc::{vec, vec::Vec}; +use alloy_consensus::constants::{ + EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID, EIP4844_TX_TYPE_ID, EIP7702_TX_TYPE_ID, +}; use alloy_primitives::{Bloom, Bytes, Log, B256}; use alloy_rlp::{length_of_length, Decodable, Encodable, RlpDecodable, RlpEncodable}; use bytes::{Buf, BufMut}; use core::{cmp::Ordering, ops::Deref}; use derive_more::{DerefMut, From, IntoIterator}; #[cfg(feature = "reth-codec")] -use reth_codecs::{Compact, CompactZstd}; +use reth_codecs::Compact; use serde::{Deserialize, Serialize}; /// Receipt containing result of transaction execution. #[derive( Clone, Debug, PartialEq, Eq, Default, RlpEncodable, RlpDecodable, Serialize, Deserialize, )] -#[cfg_attr(any(test, feature = "reth-codec"), derive(CompactZstd))] +#[cfg_attr(any(test, feature = "reth-codec"), derive(reth_codecs::CompactZstd))] #[cfg_attr(any(test, feature = "reth-codec"), reth_codecs::add_arbitrary_tests)] #[rlp(trailing)] pub struct Receipt { @@ -130,7 +130,7 @@ impl From for ReceiptWithBloom { /// [`Receipt`] with calculated bloom filter. #[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)] #[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))] -#[cfg_attr(any(test, feature = "reth-codec"), derive(Compact))] +#[cfg_attr(any(test, feature = "reth-codec"), derive(reth_codecs::Compact))] #[cfg_attr(any(test, feature = "reth-codec"), reth_codecs::add_arbitrary_tests(compact))] pub struct ReceiptWithBloom { /// Bloom filter build from logs. diff --git a/crates/primitives/src/traits/block/mod.rs b/crates/primitives/src/traits/block/mod.rs deleted file mode 100644 index 451a54c3457c..000000000000 --- a/crates/primitives/src/traits/block/mod.rs +++ /dev/null @@ -1,137 +0,0 @@ -//! 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 index 8c84c6729753..49fb73ea5555 100644 --- a/crates/primitives/src/traits/mod.rs +++ b/crates/primitives/src/traits/mod.rs @@ -1,7 +1,9 @@ //! Abstractions of primitive data types pub mod block; +pub mod transaction; pub use block::{body::BlockBody, Block}; +pub use transaction::signed::SignedTransaction; pub use alloy_consensus::BlockHeader; diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index 0cb860ff6b86..1d410da1ea8f 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -1,15 +1,17 @@ //! Transaction types. use crate::BlockHashOrNumber; -use alloy_eips::eip7702::SignedAuthorization; -use alloy_primitives::{keccak256, Address, ChainId, TxKind, B256, U256}; - -use alloy_consensus::{SignableTransaction, TxEip1559, TxEip2930, TxEip4844, TxEip7702, TxLegacy}; +#[cfg(any(test, feature = "reth-codec"))] +use alloy_consensus::constants::{EIP4844_TX_TYPE_ID, EIP7702_TX_TYPE_ID}; +use alloy_consensus::{ + SignableTransaction, Transaction as _, TxEip1559, TxEip2930, TxEip4844, TxEip7702, TxLegacy, +}; use alloy_eips::{ eip2718::{Decodable2718, Eip2718Error, Eip2718Result, Encodable2718}, eip2930::AccessList, + eip7702::SignedAuthorization, }; -use alloy_primitives::{Bytes, TxHash}; +use alloy_primitives::{keccak256, Address, Bytes, ChainId, TxHash, TxKind, B256, U256}; use alloy_rlp::{Decodable, Encodable, Error as RlpError, Header}; use core::mem; use derive_more::{AsRef, Deref}; @@ -37,10 +39,7 @@ pub use compat::FillTxEnv; pub use signature::{ extract_chain_id, legacy_parity, recover_signer, recover_signer_unchecked, Signature, }; -pub use tx_type::{ - TxType, EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID, EIP4844_TX_TYPE_ID, EIP7702_TX_TYPE_ID, - LEGACY_TX_TYPE_ID, -}; +pub use tx_type::TxType; pub use variant::TransactionSignedVariant; pub(crate) mod access_list; @@ -197,19 +196,6 @@ impl Transaction { } } - /// Get `chain_id`. - pub const fn chain_id(&self) -> Option { - match self { - Self::Legacy(TxLegacy { chain_id, .. }) => *chain_id, - Self::Eip2930(TxEip2930 { chain_id, .. }) | - Self::Eip1559(TxEip1559 { chain_id, .. }) | - Self::Eip4844(TxEip4844 { chain_id, .. }) | - Self::Eip7702(TxEip7702 { chain_id, .. }) => Some(*chain_id), - #[cfg(feature = "optimism")] - Self::Deposit(_) => None, - } - } - /// Sets the transaction's chain id to the provided value. pub fn set_chain_id(&mut self, chain_id: u64) { match self { @@ -259,33 +245,6 @@ impl Transaction { } } - /// Gets the transaction's value field. - pub const fn value(&self) -> U256 { - *match self { - Self::Legacy(TxLegacy { value, .. }) | - Self::Eip2930(TxEip2930 { value, .. }) | - Self::Eip1559(TxEip1559 { value, .. }) | - Self::Eip4844(TxEip4844 { value, .. }) | - Self::Eip7702(TxEip7702 { value, .. }) => value, - #[cfg(feature = "optimism")] - Self::Deposit(TxDeposit { value, .. }) => value, - } - } - - /// Get the transaction's nonce. - pub const fn nonce(&self) -> u64 { - match self { - Self::Legacy(TxLegacy { nonce, .. }) | - Self::Eip2930(TxEip2930 { nonce, .. }) | - Self::Eip1559(TxEip1559 { nonce, .. }) | - Self::Eip4844(TxEip4844 { nonce, .. }) | - Self::Eip7702(TxEip7702 { nonce, .. }) => *nonce, - // Deposit transactions do not have nonces. - #[cfg(feature = "optimism")] - Self::Deposit(_) => 0, - } - } - /// Returns the [`AccessList`] of the transaction. /// /// Returns `None` for legacy transactions. @@ -311,19 +270,6 @@ impl Transaction { } } - /// Get the gas limit of the transaction. - pub const fn gas_limit(&self) -> u64 { - match self { - Self::Legacy(TxLegacy { gas_limit, .. }) | - Self::Eip1559(TxEip1559 { gas_limit, .. }) | - Self::Eip4844(TxEip4844 { gas_limit, .. }) | - Self::Eip7702(TxEip7702 { gas_limit, .. }) | - Self::Eip2930(TxEip2930 { gas_limit, .. }) => *gas_limit, - #[cfg(feature = "optimism")] - Self::Deposit(TxDeposit { gas_limit, .. }) => *gas_limit, - } - } - /// Returns true if the tx supports dynamic fees pub const fn is_dynamic_fee(&self) -> bool { match self { @@ -334,40 +280,6 @@ impl Transaction { } } - /// Max fee per gas for eip1559 transaction, for legacy transactions this is `gas_price`. - /// - /// This is also commonly referred to as the "Gas Fee Cap" (`GasFeeCap`). - pub const fn max_fee_per_gas(&self) -> u128 { - match self { - Self::Legacy(TxLegacy { gas_price, .. }) | - Self::Eip2930(TxEip2930 { gas_price, .. }) => *gas_price, - Self::Eip1559(TxEip1559 { max_fee_per_gas, .. }) | - Self::Eip4844(TxEip4844 { max_fee_per_gas, .. }) | - Self::Eip7702(TxEip7702 { max_fee_per_gas, .. }) => *max_fee_per_gas, - // Deposit transactions buy their L2 gas on L1 and, as such, the L2 gas is not - // refundable. - #[cfg(feature = "optimism")] - Self::Deposit(_) => 0, - } - } - - /// Max priority fee per gas for eip1559 transaction, for legacy and eip2930 transactions this - /// is `None` - /// - /// This is also commonly referred to as the "Gas Tip Cap" (`GasTipCap`). - pub const fn max_priority_fee_per_gas(&self) -> Option { - match self { - Self::Legacy(_) | Self::Eip2930(_) => None, - Self::Eip1559(TxEip1559 { max_priority_fee_per_gas, .. }) | - Self::Eip4844(TxEip4844 { max_priority_fee_per_gas, .. }) | - Self::Eip7702(TxEip7702 { max_priority_fee_per_gas, .. }) => { - Some(*max_priority_fee_per_gas) - } - #[cfg(feature = "optimism")] - Self::Deposit(_) => None, - } - } - /// Blob versioned hashes for eip4844 transaction, for legacy, eip1559, eip2930 and eip7702 /// transactions this is `None` /// @@ -383,18 +295,6 @@ impl Transaction { } } - /// Max fee per blob gas for eip4844 transaction [`TxEip4844`]. - /// - /// Returns `None` for non-eip4844 transactions. - /// - /// This is also commonly referred to as the "Blob Gas Fee Cap" (`BlobGasFeeCap`). - pub const fn max_fee_per_blob_gas(&self) -> Option { - match self { - Self::Eip4844(TxEip4844 { max_fee_per_blob_gas, .. }) => Some(*max_fee_per_blob_gas), - _ => None, - } - } - /// Returns the blob gas used for all blobs of the EIP-4844 transaction if it is an EIP-4844 /// transaction. /// @@ -404,25 +304,6 @@ impl Transaction { self.as_eip4844().map(TxEip4844::blob_gas) } - /// Return the max priority fee per gas if the transaction is an EIP-1559 transaction, and - /// otherwise return the gas price. - /// - /// # Warning - /// - /// This is different than the `max_priority_fee_per_gas` method, which returns `None` for - /// non-EIP-1559 transactions. - pub const fn priority_fee_or_price(&self) -> u128 { - match self { - Self::Legacy(TxLegacy { gas_price, .. }) | - Self::Eip2930(TxEip2930 { gas_price, .. }) => *gas_price, - Self::Eip1559(TxEip1559 { max_priority_fee_per_gas, .. }) | - Self::Eip4844(TxEip4844 { max_priority_fee_per_gas, .. }) | - Self::Eip7702(TxEip7702 { max_priority_fee_per_gas, .. }) => *max_priority_fee_per_gas, - #[cfg(feature = "optimism")] - Self::Deposit(_) => 0, - } - } - /// Returns the effective gas price for the given base fee. /// /// If the transaction is a legacy or EIP2930 transaction, the gas price is returned. @@ -897,18 +778,6 @@ impl alloy_consensus::Transaction for Transaction { } } - fn priority_fee_or_price(&self) -> u128 { - match self { - Self::Legacy(tx) => tx.priority_fee_or_price(), - Self::Eip2930(tx) => tx.priority_fee_or_price(), - Self::Eip1559(tx) => tx.priority_fee_or_price(), - Self::Eip4844(tx) => tx.priority_fee_or_price(), - Self::Eip7702(tx) => tx.priority_fee_or_price(), - #[cfg(feature = "optimism")] - Self::Deposit(tx) => tx.priority_fee_or_price(), - } - } - fn max_fee_per_blob_gas(&self) -> Option { match self { Self::Legacy(tx) => tx.max_fee_per_blob_gas(), @@ -921,19 +790,19 @@ impl alloy_consensus::Transaction for Transaction { } } - fn to(&self) -> TxKind { + fn priority_fee_or_price(&self) -> u128 { match self { - Self::Legacy(tx) => tx.to(), - Self::Eip2930(tx) => tx.to(), - Self::Eip1559(tx) => tx.to(), - Self::Eip4844(tx) => tx.to(), - Self::Eip7702(tx) => tx.to(), + Self::Legacy(tx) => tx.priority_fee_or_price(), + Self::Eip2930(tx) => tx.priority_fee_or_price(), + Self::Eip1559(tx) => tx.priority_fee_or_price(), + Self::Eip4844(tx) => tx.priority_fee_or_price(), + Self::Eip7702(tx) => tx.priority_fee_or_price(), #[cfg(feature = "optimism")] - Self::Deposit(tx) => tx.to(), + Self::Deposit(tx) => tx.priority_fee_or_price(), } } - fn value(&self) -> alloy_primitives::U256 { + fn value(&self) -> U256 { match self { Self::Legacy(tx) => tx.value(), Self::Eip2930(tx) => tx.value(), @@ -945,7 +814,7 @@ impl alloy_consensus::Transaction for Transaction { } } - fn input(&self) -> &[u8] { + fn input(&self) -> &Bytes { match self { Self::Legacy(tx) => tx.input(), Self::Eip2930(tx) => tx.input(), @@ -1004,6 +873,18 @@ impl alloy_consensus::Transaction for Transaction { Self::Deposit(tx) => tx.authorization_list(), } } + + fn kind(&self) -> TxKind { + match self { + Self::Legacy(tx) => tx.kind(), + Self::Eip2930(tx) => tx.kind(), + Self::Eip1559(tx) => tx.kind(), + Self::Eip4844(tx) => tx.kind(), + Self::Eip7702(tx) => tx.kind(), + #[cfg(feature = "optimism")] + Self::Deposit(tx) => tx.kind(), + } + } } /// Signed transaction without its Hash. Used type for inserting into the DB. @@ -1469,6 +1350,68 @@ impl TransactionSigned { } } +impl alloy_consensus::Transaction for TransactionSigned { + fn chain_id(&self) -> Option { + self.deref().chain_id() + } + + fn nonce(&self) -> u64 { + self.deref().nonce() + } + + fn gas_limit(&self) -> u64 { + self.deref().gas_limit() + } + + fn gas_price(&self) -> Option { + self.deref().gas_price() + } + + fn max_fee_per_gas(&self) -> u128 { + self.deref().max_fee_per_gas() + } + + fn max_priority_fee_per_gas(&self) -> Option { + self.deref().max_priority_fee_per_gas() + } + + fn max_fee_per_blob_gas(&self) -> Option { + self.deref().max_fee_per_blob_gas() + } + + fn priority_fee_or_price(&self) -> u128 { + self.deref().priority_fee_or_price() + } + + fn value(&self) -> U256 { + self.deref().value() + } + + fn input(&self) -> &Bytes { + self.deref().input() + } + + fn ty(&self) -> u8 { + self.deref().ty() + } + + fn access_list(&self) -> Option<&AccessList> { + self.deref().access_list() + } + + fn blob_versioned_hashes(&self) -> Option<&[B256]> { + alloy_consensus::Transaction::blob_versioned_hashes(self.deref()) + } + + fn authorization_list(&self) -> Option<&[SignedAuthorization]> { + self.deref().authorization_list() + } + + fn kind(&self) -> TxKind { + self.deref().kind() + } +} + impl From for TransactionSigned { fn from(recovered: TransactionSignedEcRecovered) -> Self { recovered.signed_transaction @@ -1974,6 +1917,7 @@ mod tests { transaction::{signature::Signature, TxEip1559, TxKind, TxLegacy}, Transaction, TransactionSigned, TransactionSignedEcRecovered, TransactionSignedNoHash, }; + use alloy_consensus::Transaction as _; use alloy_eips::eip2718::{Decodable2718, Encodable2718}; use alloy_primitives::{address, b256, bytes, hex, Address, Bytes, Parity, B256, U256}; use alloy_rlp::{Decodable, Encodable, Error as RlpError}; diff --git a/crates/primitives/src/transaction/pooled.rs b/crates/primitives/src/transaction/pooled.rs index ec49f44a680b..32d4da65980e 100644 --- a/crates/primitives/src/transaction/pooled.rs +++ b/crates/primitives/src/transaction/pooled.rs @@ -8,9 +8,10 @@ use super::{ }; use crate::{ BlobTransaction, BlobTransactionSidecar, Signature, Transaction, TransactionSigned, - TransactionSignedEcRecovered, EIP4844_TX_TYPE_ID, + TransactionSignedEcRecovered, }; use alloy_consensus::{ + constants::EIP4844_TX_TYPE_ID, transaction::{TxEip1559, TxEip2930, TxEip4844, TxLegacy}, SignableTransaction, TxEip4844WithSidecar, }; diff --git a/crates/primitives/src/transaction/sidecar.rs b/crates/primitives/src/transaction/sidecar.rs index 87b8c1fbf3e8..edc1427d1fe8 100644 --- a/crates/primitives/src/transaction/sidecar.rs +++ b/crates/primitives/src/transaction/sidecar.rs @@ -1,7 +1,9 @@ #![cfg_attr(docsrs, doc(cfg(feature = "c-kzg")))] -use crate::{Signature, Transaction, TransactionSigned, EIP4844_TX_TYPE_ID}; -use alloy_consensus::{transaction::TxEip4844, TxEip4844WithSidecar}; +use crate::{Signature, Transaction, TransactionSigned}; +use alloy_consensus::{ + constants::EIP4844_TX_TYPE_ID, transaction::TxEip4844, TxEip4844WithSidecar, +}; use alloy_primitives::{keccak256, TxHash}; use alloy_rlp::{Decodable, Error as RlpError, Header}; use serde::{Deserialize, Serialize}; diff --git a/crates/primitives/src/transaction/tx_type.rs b/crates/primitives/src/transaction/tx_type.rs index c55e0d3c6193..b6e1ecbf226a 100644 --- a/crates/primitives/src/transaction/tx_type.rs +++ b/crates/primitives/src/transaction/tx_type.rs @@ -1,3 +1,7 @@ +use alloy_consensus::constants::{ + EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID, EIP4844_TX_TYPE_ID, EIP7702_TX_TYPE_ID, + LEGACY_TX_TYPE_ID, +}; use alloy_primitives::{U64, U8}; use alloy_rlp::{Decodable, Encodable}; use serde::{Deserialize, Serialize}; @@ -23,22 +27,6 @@ pub(crate) const COMPACT_IDENTIFIER_EIP1559: usize = 2; #[cfg(any(test, feature = "reth-codec"))] pub(crate) const COMPACT_EXTENDED_IDENTIFIER_FLAG: usize = 3; -/// Identifier for legacy transaction, however [`TxLegacy`](alloy_consensus::TxLegacy) this is -/// technically not typed. -pub const LEGACY_TX_TYPE_ID: u8 = 0; - -/// Identifier for [`TxEip2930`](alloy_consensus::TxEip2930) transaction. -pub const EIP2930_TX_TYPE_ID: u8 = 1; - -/// Identifier for [`TxEip1559`](alloy_consensus::TxEip1559) transaction. -pub const EIP1559_TX_TYPE_ID: u8 = 2; - -/// Identifier for [`TxEip4844`](alloy_consensus::TxEip4844) transaction. -pub const EIP4844_TX_TYPE_ID: u8 = 3; - -/// Identifier for [`TxEip7702`](alloy_consensus::TxEip7702) transaction. -pub const EIP7702_TX_TYPE_ID: u8 = 4; - /// Identifier for [`TxDeposit`](op_alloy_consensus::TxDeposit) transaction. #[cfg(feature = "optimism")] pub const DEPOSIT_TX_TYPE_ID: u8 = 126; diff --git a/crates/prune/prune/src/segments/receipts.rs b/crates/prune/prune/src/segments/receipts.rs index 05482d65953d..c081bf88c7d2 100644 --- a/crates/prune/prune/src/segments/receipts.rs +++ b/crates/prune/prune/src/segments/receipts.rs @@ -109,12 +109,14 @@ mod tests { let mut receipts = Vec::new(); for block in &blocks { + receipts.reserve_exact(block.body.transactions.len()); for transaction in &block.body.transactions { receipts .push((receipts.len() as u64, random_receipt(&mut rng, transaction, Some(0)))); } } - db.insert_receipts(receipts.clone()).expect("insert receipts"); + let receipts_len = receipts.len(); + db.insert_receipts(receipts).expect("insert receipts"); assert_eq!( db.table::().unwrap().len(), @@ -194,7 +196,7 @@ mod tests { assert_eq!( db.table::().unwrap().len(), - receipts.len() - (last_pruned_tx_number + 1) + receipts_len - (last_pruned_tx_number + 1) ); assert_eq!( db.factory diff --git a/crates/prune/prune/src/segments/user/receipts_by_logs.rs b/crates/prune/prune/src/segments/user/receipts_by_logs.rs index 05bc40b6c7b0..ee2accee1b34 100644 --- a/crates/prune/prune/src/segments/user/receipts_by_logs.rs +++ b/crates/prune/prune/src/segments/user/receipts_by_logs.rs @@ -263,6 +263,7 @@ mod tests { let (deposit_contract_addr, _) = random_eoa_account(&mut rng); for block in &blocks { + receipts.reserve_exact(block.body.size()); for (txi, transaction) in block.body.transactions.iter().enumerate() { let mut receipt = random_receipt(&mut rng, transaction, Some(1)); receipt.logs.push(random_log( diff --git a/crates/prune/prune/src/segments/user/sender_recovery.rs b/crates/prune/prune/src/segments/user/sender_recovery.rs index bd86f3e65213..f189e6c36af4 100644 --- a/crates/prune/prune/src/segments/user/sender_recovery.rs +++ b/crates/prune/prune/src/segments/user/sender_recovery.rs @@ -110,6 +110,7 @@ mod tests { let mut transaction_senders = Vec::new(); for block in &blocks { + transaction_senders.reserve_exact(block.body.transactions.len()); for transaction in &block.body.transactions { transaction_senders.push(( transaction_senders.len() as u64, @@ -117,8 +118,8 @@ mod tests { )); } } - db.insert_transaction_senders(transaction_senders.clone()) - .expect("insert transaction senders"); + let transaction_senders_len = transaction_senders.len(); + db.insert_transaction_senders(transaction_senders).expect("insert transaction senders"); assert_eq!( db.table::().unwrap().len(), @@ -202,7 +203,7 @@ mod tests { assert_eq!( db.table::().unwrap().len(), - transaction_senders.len() - (last_pruned_tx_number + 1) + transaction_senders_len - (last_pruned_tx_number + 1) ); assert_eq!( db.factory diff --git a/crates/prune/prune/src/segments/user/transaction_lookup.rs b/crates/prune/prune/src/segments/user/transaction_lookup.rs index bb8196cdb03b..2df8cccf3056 100644 --- a/crates/prune/prune/src/segments/user/transaction_lookup.rs +++ b/crates/prune/prune/src/segments/user/transaction_lookup.rs @@ -140,11 +140,13 @@ mod tests { let mut tx_hash_numbers = Vec::new(); for block in &blocks { + tx_hash_numbers.reserve_exact(block.body.transactions.len()); for transaction in &block.body.transactions { tx_hash_numbers.push((transaction.hash, tx_hash_numbers.len() as u64)); } } - db.insert_tx_hash_numbers(tx_hash_numbers.clone()).expect("insert tx hash numbers"); + let tx_hash_numbers_len = tx_hash_numbers.len(); + db.insert_tx_hash_numbers(tx_hash_numbers).expect("insert tx hash numbers"); assert_eq!( db.table::().unwrap().len(), @@ -228,7 +230,7 @@ mod tests { assert_eq!( db.table::().unwrap().len(), - tx_hash_numbers.len() - (last_pruned_tx_number + 1) + tx_hash_numbers_len - (last_pruned_tx_number + 1) ); assert_eq!( db.factory diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index 9e4501f62770..8f670d364b8a 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -13,14 +13,15 @@ workspace = true [dependencies] # reth -reth-chainspec.workspace = true reth-primitives.workspace = true reth-storage-errors.workspace = true reth-execution-errors.workspace = true -reth-consensus-common.workspace = true reth-prune-types.workspace = true reth-storage-api.workspace = true reth-trie = { workspace = true, optional = true } + +# alloy +alloy-eips.workspace = true alloy-primitives.workspace = true # revm @@ -32,9 +33,22 @@ reth-ethereum-forks.workspace = true alloy-primitives.workspace = true [features] -default = ["std", "c-kzg"] -std = [] -c-kzg = ["revm/c-kzg"] -test-utils = ["dep:reth-trie"] -optimism = ["revm/optimism"] -serde = ["revm/serde"] +default = ["std"] +std = [ + "reth-primitives/std", + "alloy-primitives/std", + "revm/std", + "alloy-eips/std" +] +test-utils = [ + "dep:reth-trie", + "reth-primitives/test-utils", + "reth-trie?/test-utils", + "revm/test-utils" +] +serde = [ + "revm/serde", + "reth-trie?/serde", + "alloy-eips/serde", + "alloy-primitives/serde" +] diff --git a/crates/revm/src/batch.rs b/crates/revm/src/batch.rs index a63681aa1321..be3ef0a37821 100644 --- a/crates/revm/src/batch.rs +++ b/crates/revm/src/batch.rs @@ -1,9 +1,10 @@ //! Helper for handling execution of multiple blocks. use alloc::vec::Vec; +use alloy_eips::eip7685::Requests; use alloy_primitives::{map::HashSet, Address, BlockNumber}; use reth_execution_errors::{BlockExecutionError, InternalBlockExecutionError}; -use reth_primitives::{Receipt, Receipts, Request, Requests}; +use reth_primitives::{Receipt, Receipts}; use reth_prune_types::{PruneMode, PruneModes, PruneSegmentError, MINIMUM_PRUNING_DISTANCE}; use revm::db::states::bundle_state::BundleRetention; @@ -170,8 +171,8 @@ impl BlockBatchRecord { } /// Save EIP-7685 requests to the executor. - pub fn save_requests(&mut self, requests: Vec) { - self.requests.push(requests.into()); + pub fn save_requests(&mut self, requests: Requests) { + self.requests.push(requests); } } diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs index 5515357d0d23..02eb182ee119 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -16,9 +16,6 @@ pub mod database; pub mod batch; -/// State changes that are not related to transactions. -pub mod state_change; - /// Common test helpers #[cfg(any(test, feature = "test-utils"))] pub mod test_utils; diff --git a/crates/rpc/rpc-api/Cargo.toml b/crates/rpc/rpc-api/Cargo.toml index 6e9e469ec443..363e22955301 100644 --- a/crates/rpc/rpc-api/Cargo.toml +++ b/crates/rpc/rpc-api/Cargo.toml @@ -37,12 +37,9 @@ alloy-rpc-types-engine.workspace = true # misc jsonrpsee = { workspace = true, features = ["server", "macros"] } -[dev-dependencies] -serde_json.workspace = true - [features] client = [ "jsonrpsee/client", "jsonrpsee/async-client", - "reth-rpc-eth-api/client" + "reth-rpc-eth-api/client", ] diff --git a/crates/rpc/rpc-api/src/engine.rs b/crates/rpc/rpc-api/src/engine.rs index 50181d23a75c..ddf6d846119d 100644 --- a/crates/rpc/rpc-api/src/engine.rs +++ b/crates/rpc/rpc-api/src/engine.rs @@ -3,16 +3,16 @@ //! This contains the `engine_` namespace and the subset of the `eth_` namespace that is exposed to //! the consensus client. -use alloy_eips::{eip4844::BlobAndProofV1, BlockId, BlockNumberOrTag}; +use alloy_eips::{eip4844::BlobAndProofV1, eip7685::Requests, BlockId, BlockNumberOrTag}; use alloy_json_rpc::RpcObject; use alloy_primitives::{Address, BlockHash, Bytes, B256, U256, U64}; use alloy_rpc_types::{ state::StateOverride, BlockOverrides, EIP1186AccountProofResponse, Filter, Log, SyncStatus, }; use alloy_rpc_types_engine::{ - ClientVersionV1, ExecutionPayloadBodiesV1, ExecutionPayloadBodiesV2, ExecutionPayloadInputV2, - ExecutionPayloadV1, ExecutionPayloadV3, ExecutionPayloadV4, ForkchoiceState, ForkchoiceUpdated, - PayloadId, PayloadStatus, TransitionConfiguration, + ClientVersionV1, ExecutionPayloadBodiesV1, ExecutionPayloadInputV2, ExecutionPayloadV1, + ExecutionPayloadV3, ForkchoiceState, ForkchoiceUpdated, PayloadId, PayloadStatus, + TransitionConfiguration, }; use alloy_rpc_types_eth::transaction::TransactionRequest; use alloy_serde::JsonStorageKey; @@ -54,9 +54,10 @@ pub trait EngineApi { #[method(name = "newPayloadV4")] async fn new_payload_v4( &self, - payload: ExecutionPayloadV4, + payload: ExecutionPayloadV3, versioned_hashes: Vec, parent_beacon_block_root: B256, + execution_requests: Requests, ) -> RpcResult; /// See also @@ -117,7 +118,10 @@ pub trait EngineApi { /// payload build process at the time of receiving this call. Note: /// > Provider software MAY stop the corresponding build process after serving this call. #[method(name = "getPayloadV2")] - async fn get_payload_v2(&self, payload_id: PayloadId) -> RpcResult; + async fn get_payload_v2( + &self, + payload_id: PayloadId, + ) -> RpcResult; /// Post Cancun payload handler which also returns a blobs bundle. /// @@ -127,7 +131,10 @@ pub trait EngineApi { /// payload build process at the time of receiving this call. Note: /// > Provider software MAY stop the corresponding build process after serving this call. #[method(name = "getPayloadV3")] - async fn get_payload_v3(&self, payload_id: PayloadId) -> RpcResult; + async fn get_payload_v3( + &self, + payload_id: PayloadId, + ) -> RpcResult; /// Post Prague payload handler. /// @@ -137,7 +144,10 @@ pub trait EngineApi { /// payload build process at the time of receiving this call. Note: /// > Provider software MAY stop the corresponding build process after serving this call. #[method(name = "getPayloadV4")] - async fn get_payload_v4(&self, payload_id: PayloadId) -> RpcResult; + async fn get_payload_v4( + &self, + payload_id: PayloadId, + ) -> RpcResult; /// See also #[method(name = "getPayloadBodiesByHashV1")] @@ -146,13 +156,6 @@ pub trait EngineApi { block_hashes: Vec, ) -> RpcResult; - /// See also - #[method(name = "getPayloadBodiesByHashV2")] - async fn get_payload_bodies_by_hash_v2( - &self, - block_hashes: Vec, - ) -> RpcResult; - /// See also /// /// Returns the execution payload bodies by the range starting at `start`, containing `count` @@ -172,16 +175,6 @@ pub trait EngineApi { count: U64, ) -> RpcResult; - /// See also - /// - /// Similar to `getPayloadBodiesByRangeV1`, but returns [`ExecutionPayloadBodiesV2`] - #[method(name = "getPayloadBodiesByRangeV2")] - async fn get_payload_bodies_by_range_v2( - &self, - start: U64, - count: U64, - ) -> RpcResult; - /// See also /// /// Note: This method will be deprecated after the cancun hardfork: diff --git a/crates/rpc/rpc-builder/Cargo.toml b/crates/rpc/rpc-builder/Cargo.toml index 817d2a3d76b8..cc72c2ebf92e 100644 --- a/crates/rpc/rpc-builder/Cargo.toml +++ b/crates/rpc/rpc-builder/Cargo.toml @@ -64,8 +64,6 @@ reth-rpc-api = { workspace = true, features = ["client"] } reth-rpc-engine-api.workspace = true reth-tracing.workspace = true reth-transaction-pool = { workspace = true, features = ["test-utils"] } -reth-tokio-util.workspace = true -reth-node-api.workspace = true reth-rpc-types-compat.workspace = true alloy-primitives.workspace = true diff --git a/crates/rpc/rpc-builder/src/eth.rs b/crates/rpc/rpc-builder/src/eth.rs index 920268a98549..613652678a20 100644 --- a/crates/rpc/rpc-builder/src/eth.rs +++ b/crates/rpc/rpc-builder/src/eth.rs @@ -1,5 +1,3 @@ -use std::marker::PhantomData; - use reth_evm::ConfigureEvm; use reth_primitives::Header; use reth_provider::{BlockReader, CanonStateSubscriptions, EvmEnvProvider, StateProviderFactory}; @@ -11,9 +9,8 @@ use reth_rpc_eth_types::{ use reth_tasks::TaskSpawner; /// Alias for `eth` namespace API builder. -pub type DynEthApiBuilder = Box< - dyn Fn(&EthApiBuilderCtx) -> EthApi, ->; +pub type DynEthApiBuilder = + Box) -> EthApi>; /// Handlers for core, filter and pubsub `eth` namespace APIs. #[derive(Debug, Clone)] @@ -87,7 +84,6 @@ where executor, events, cache, - _rpc_ty_builders: PhantomData, }; let api = eth_api_builder(&ctx); diff --git a/crates/rpc/rpc-builder/src/lib.rs b/crates/rpc/rpc-builder/src/lib.rs index e49105ab7d7d..cd93aeb620e3 100644 --- a/crates/rpc/rpc-builder/src/lib.rs +++ b/crates/rpc/rpc-builder/src/lib.rs @@ -152,6 +152,7 @@ use std::{ collections::HashMap, + fmt::Debug, net::{Ipv4Addr, SocketAddr, SocketAddrV4}, time::{Duration, SystemTime, UNIX_EPOCH}, }; @@ -675,8 +676,8 @@ impl RpcModuleConfigBuilder { } /// Get a reference to the eth namespace config, if any - pub const fn get_eth(&self) -> &Option { - &self.eth + pub const fn get_eth(&self) -> Option<&EthConfig> { + self.eth.as_ref() } /// Get a mutable reference to the eth namespace config, if any diff --git a/crates/rpc/rpc-builder/tests/it/utils.rs b/crates/rpc/rpc-builder/tests/it/utils.rs index 847de99564ef..44614ea49a85 100644 --- a/crates/rpc/rpc-builder/tests/it/utils.rs +++ b/crates/rpc/rpc-builder/tests/it/utils.rs @@ -4,7 +4,8 @@ use alloy_rpc_types_engine::{ClientCode, ClientVersionV1}; use reth_beacon_consensus::BeaconConsensusEngineHandle; use reth_chainspec::MAINNET; use reth_ethereum_engine_primitives::{EthEngineTypes, EthereumEngineValidator}; -use reth_evm_ethereum::{execute::EthExecutorProvider, EthEvmConfig}; +use reth_evm::execute::BasicBlockExecutorProvider; +use reth_evm_ethereum::{execute::EthExecutionStrategyFactory, EthEvmConfig}; use reth_network_api::noop::NoopNetwork; use reth_payload_builder::test_utils::spawn_test_payload_service; use reth_provider::test_utils::{NoopProvider, TestCanonStateSubscriptions}; @@ -124,7 +125,7 @@ pub fn test_rpc_builder() -> RpcModuleBuilder< TokioTaskExecutor, TestCanonStateSubscriptions, EthEvmConfig, - EthExecutorProvider, + BasicBlockExecutorProvider, > { RpcModuleBuilder::default() .with_provider(NoopProvider::default()) @@ -133,5 +134,7 @@ pub fn test_rpc_builder() -> RpcModuleBuilder< .with_executor(TokioTaskExecutor::default()) .with_events(TestCanonStateSubscriptions::default()) .with_evm_config(EthEvmConfig::new(MAINNET.clone())) - .with_block_executor(EthExecutorProvider::ethereum(MAINNET.clone())) + .with_block_executor( + BasicBlockExecutorProvider::new(EthExecutionStrategyFactory::mainnet()), + ) } diff --git a/crates/rpc/rpc-engine-api/src/capabilities.rs b/crates/rpc/rpc-engine-api/src/capabilities.rs index de4d96231538..af0609b0d1f0 100644 --- a/crates/rpc/rpc-engine-api/src/capabilities.rs +++ b/crates/rpc/rpc-engine-api/src/capabilities.rs @@ -17,8 +17,6 @@ pub const CAPABILITIES: &[&str] = &[ "engine_newPayloadV4", "engine_getPayloadBodiesByHashV1", "engine_getPayloadBodiesByRangeV1", - "engine_getPayloadBodiesByHashV2", - "engine_getPayloadBodiesByRangeV2", "engine_getBlobsV1", ]; diff --git a/crates/rpc/rpc-engine-api/src/engine_api.rs b/crates/rpc/rpc-engine-api/src/engine_api.rs index 252808c14a77..ca055a77ea10 100644 --- a/crates/rpc/rpc-engine-api/src/engine_api.rs +++ b/crates/rpc/rpc-engine-api/src/engine_api.rs @@ -1,13 +1,12 @@ use crate::{ capabilities::EngineCapabilities, metrics::EngineApiMetrics, EngineApiError, EngineApiResult, }; -use alloy_eips::eip4844::BlobAndProofV1; +use alloy_eips::{eip4844::BlobAndProofV1, eip7685::Requests}; use alloy_primitives::{BlockHash, BlockNumber, B256, U64}; use alloy_rpc_types_engine::{ CancunPayloadFields, ClientVersionV1, ExecutionPayload, ExecutionPayloadBodiesV1, - ExecutionPayloadBodiesV2, ExecutionPayloadInputV2, ExecutionPayloadV1, ExecutionPayloadV3, - ExecutionPayloadV4, ForkchoiceState, ForkchoiceUpdated, PayloadId, PayloadStatus, - TransitionConfiguration, + ExecutionPayloadInputV2, ExecutionPayloadV1, ExecutionPayloadV3, ForkchoiceState, + ForkchoiceUpdated, PayloadId, PayloadStatus, TransitionConfiguration, }; use async_trait::async_trait; use jsonrpsee_core::RpcResult; @@ -23,7 +22,7 @@ use reth_payload_primitives::{ use reth_primitives::{Block, BlockHashOrNumber, EthereumHardfork}; use reth_rpc_api::EngineApiServer; use reth_rpc_types_compat::engine::payload::{ - convert_payload_input_v2_to_payload, convert_to_payload_body_v1, convert_to_payload_body_v2, + convert_payload_input_v2_to_payload, convert_to_payload_body_v1, }; use reth_storage_api::{BlockReader, HeaderProvider, StateProviderFactory}; use reth_tasks::TaskSpawner; @@ -141,7 +140,7 @@ where self.inner .validator .validate_version_specific_fields(EngineApiMessageVersion::V1, payload_or_attrs)?; - Ok(self.inner.beacon_consensus.new_payload(payload, None).await?) + Ok(self.inner.beacon_consensus.new_payload(payload, None, None).await?) } /// See also @@ -157,7 +156,7 @@ where self.inner .validator .validate_version_specific_fields(EngineApiMessageVersion::V2, payload_or_attrs)?; - Ok(self.inner.beacon_consensus.new_payload(payload, None).await?) + Ok(self.inner.beacon_consensus.new_payload(payload, None, None).await?) } /// See also @@ -179,15 +178,18 @@ where let cancun_fields = CancunPayloadFields { versioned_hashes, parent_beacon_block_root }; - Ok(self.inner.beacon_consensus.new_payload(payload, Some(cancun_fields)).await?) + Ok(self.inner.beacon_consensus.new_payload(payload, Some(cancun_fields), None).await?) } /// See also pub async fn new_payload_v4( &self, - payload: ExecutionPayloadV4, + payload: ExecutionPayloadV3, versioned_hashes: Vec, parent_beacon_block_root: B256, + // TODO(onbjerg): Figure out why we even get these here, since we'll check the requests + // from execution against the requests root in the header. + execution_requests: Requests, ) -> EngineApiResult { let payload = ExecutionPayload::from(payload); let payload_or_attrs = @@ -201,7 +203,13 @@ where let cancun_fields = CancunPayloadFields { versioned_hashes, parent_beacon_block_root }; - Ok(self.inner.beacon_consensus.new_payload(payload, Some(cancun_fields)).await?) + // HACK(onbjerg): We should have a pectra payload fields struct, this is just a temporary + // workaround. + Ok(self + .inner + .beacon_consensus + .new_payload(payload, Some(cancun_fields), Some(execution_requests)) + .await?) } /// Sends a message to the beacon consensus engine to update the fork choice _without_ @@ -281,7 +289,7 @@ where pub async fn get_payload_v2( &self, payload_id: PayloadId, - ) -> EngineApiResult { + ) -> EngineApiResult { // First we fetch the payload attributes to check the timestamp let attributes = self.get_payload_attributes(payload_id).await?; @@ -316,7 +324,7 @@ where pub async fn get_payload_v3( &self, payload_id: PayloadId, - ) -> EngineApiResult { + ) -> EngineApiResult { // First we fetch the payload attributes to check the timestamp let attributes = self.get_payload_attributes(payload_id).await?; @@ -351,7 +359,7 @@ where pub async fn get_payload_v4( &self, payload_id: PayloadId, - ) -> EngineApiResult { + ) -> EngineApiResult { // First we fetch the payload attributes to check the timestamp let attributes = self.get_payload_attributes(payload_id).await?; @@ -371,7 +379,7 @@ where .map_err(|_| EngineApiError::UnknownPayload)? .try_into() .map_err(|_| { - warn!("could not transform built payload into ExecutionPayloadV4"); + warn!("could not transform built payload into ExecutionPayloadV3"); EngineApiError::UnknownPayload }) } @@ -451,18 +459,6 @@ where self.get_payload_bodies_by_range_with(start, count, convert_to_payload_body_v1).await } - /// Returns the execution payload bodies by the range starting at `start`, containing `count` - /// blocks. - /// - /// Same as [`Self::get_payload_bodies_by_range_v1`] but as [`ExecutionPayloadBodiesV2`]. - pub async fn get_payload_bodies_by_range_v2( - &self, - start: BlockNumber, - count: u64, - ) -> EngineApiResult { - self.get_payload_bodies_by_range_with(start, count, convert_to_payload_body_v2).await - } - /// Called to retrieve execution payload bodies by hashes. async fn get_payload_bodies_by_hash_with( &self, @@ -509,16 +505,6 @@ where self.get_payload_bodies_by_hash_with(hashes, convert_to_payload_body_v1).await } - /// Called to retrieve execution payload bodies by hashes. - /// - /// Same as [`Self::get_payload_bodies_by_hash_v1`] but as [`ExecutionPayloadBodiesV2`]. - pub async fn get_payload_bodies_by_hash_v2( - &self, - hashes: Vec, - ) -> EngineApiResult { - self.get_payload_bodies_by_hash_with(hashes, convert_to_payload_body_v2).await - } - /// Called to verify network configuration parameters and ensure that Consensus and Execution /// layers are using the latest configuration. pub fn exchange_transition_configuration( @@ -688,15 +674,22 @@ where /// See also async fn new_payload_v4( &self, - payload: ExecutionPayloadV4, + payload: ExecutionPayloadV3, versioned_hashes: Vec, parent_beacon_block_root: B256, + execution_requests: Requests, ) -> RpcResult { trace!(target: "rpc::engine", "Serving engine_newPayloadV4"); let start = Instant::now(); - let gas_used = payload.payload_inner.payload_inner.payload_inner.gas_used; - let res = - Self::new_payload_v4(self, payload, versioned_hashes, parent_beacon_block_root).await; + let gas_used = payload.payload_inner.payload_inner.gas_used; + let res = Self::new_payload_v4( + self, + payload, + versioned_hashes, + parent_beacon_block_root, + execution_requests, + ) + .await; let elapsed = start.elapsed(); self.inner.metrics.latency.new_payload_v4.record(elapsed); self.inner.metrics.new_payload_response.update_response_metrics(&res, gas_used, elapsed); @@ -785,7 +778,7 @@ where async fn get_payload_v2( &self, payload_id: PayloadId, - ) -> RpcResult { + ) -> RpcResult { trace!(target: "rpc::engine", "Serving engine_getPayloadV2"); let start = Instant::now(); let res = Self::get_payload_v2(self, payload_id).await; @@ -805,7 +798,7 @@ where async fn get_payload_v3( &self, payload_id: PayloadId, - ) -> RpcResult { + ) -> RpcResult { trace!(target: "rpc::engine", "Serving engine_getPayloadV3"); let start = Instant::now(); let res = Self::get_payload_v3(self, payload_id).await; @@ -825,7 +818,7 @@ where async fn get_payload_v4( &self, payload_id: PayloadId, - ) -> RpcResult { + ) -> RpcResult { trace!(target: "rpc::engine", "Serving engine_getPayloadV4"); let start = Instant::now(); let res = Self::get_payload_v4(self, payload_id).await; @@ -846,17 +839,6 @@ where Ok(res.await?) } - async fn get_payload_bodies_by_hash_v2( - &self, - block_hashes: Vec, - ) -> RpcResult { - trace!(target: "rpc::engine", "Serving engine_getPayloadBodiesByHashV2"); - let start = Instant::now(); - let res = Self::get_payload_bodies_by_hash_v2(self, block_hashes); - self.inner.metrics.latency.get_payload_bodies_by_hash_v2.record(start.elapsed()); - Ok(res.await?) - } - /// Handler for `engine_getPayloadBodiesByRangeV1` /// /// See also @@ -885,18 +867,6 @@ where Ok(res?) } - async fn get_payload_bodies_by_range_v2( - &self, - start: U64, - count: U64, - ) -> RpcResult { - trace!(target: "rpc::engine", "Serving engine_getPayloadBodiesByRangeV2"); - let start_time = Instant::now(); - let res = Self::get_payload_bodies_by_range_v2(self, start.to(), count.to()).await; - self.inner.metrics.latency.get_payload_bodies_by_range_v2.record(start_time.elapsed()); - Ok(res?) - } - /// Handler for `engine_exchangeTransitionConfigurationV1` /// See also async fn exchange_transition_configuration( diff --git a/crates/rpc/rpc-engine-api/src/metrics.rs b/crates/rpc/rpc-engine-api/src/metrics.rs index 2c4216664ae2..8d0106f9dd93 100644 --- a/crates/rpc/rpc-engine-api/src/metrics.rs +++ b/crates/rpc/rpc-engine-api/src/metrics.rs @@ -44,12 +44,8 @@ pub(crate) struct EngineApiLatencyMetrics { pub(crate) get_payload_v4: Histogram, /// Latency for `engine_getPayloadBodiesByRangeV1` pub(crate) get_payload_bodies_by_range_v1: Histogram, - /// Latency for `engine_getPayloadBodiesByRangeV2` - pub(crate) get_payload_bodies_by_range_v2: Histogram, /// Latency for `engine_getPayloadBodiesByHashV1` pub(crate) get_payload_bodies_by_hash_v1: Histogram, - /// Latency for `engine_getPayloadBodiesByHashV2` - pub(crate) get_payload_bodies_by_hash_v2: Histogram, /// Latency for `engine_exchangeTransitionConfigurationV1` pub(crate) exchange_transition_configuration: Histogram, } diff --git a/crates/rpc/rpc-engine-api/tests/it/payload.rs b/crates/rpc/rpc-engine-api/tests/it/payload.rs index c08c30c1de09..007a62db045b 100644 --- a/crates/rpc/rpc-engine-api/tests/it/payload.rs +++ b/crates/rpc/rpc-engine-api/tests/it/payload.rs @@ -75,7 +75,7 @@ fn payload_validation() { b }); - assert_matches!(try_into_sealed_block(block_with_valid_extra_data, None), Ok(_)); + assert_matches!(try_into_sealed_block(block_with_valid_extra_data, None, None), Ok(_)); // Invalid extra data let block_with_invalid_extra_data = Bytes::from_static(&[0; 33]); @@ -84,7 +84,7 @@ fn payload_validation() { b }); assert_matches!( - try_into_sealed_block(invalid_extra_data_block,None), + try_into_sealed_block(invalid_extra_data_block, None, None), Err(PayloadError::ExtraData(data)) if data == block_with_invalid_extra_data ); @@ -94,8 +94,7 @@ fn payload_validation() { b }); assert_matches!( - - try_into_sealed_block(block_with_zero_base_fee,None), + try_into_sealed_block(block_with_zero_base_fee, None, None), Err(PayloadError::BaseFee(val)) if val.is_zero() ); @@ -114,8 +113,7 @@ fn payload_validation() { b }); assert_matches!( - try_into_sealed_block(block_with_ommers.clone(),None), - + try_into_sealed_block(block_with_ommers.clone(), None, None), Err(PayloadError::BlockHash { consensus, .. }) if consensus == block_with_ommers.block_hash() ); @@ -126,9 +124,8 @@ fn payload_validation() { b }); assert_matches!( - try_into_sealed_block(block_with_difficulty.clone(),None), + try_into_sealed_block(block_with_difficulty.clone(), None, None), Err(PayloadError::BlockHash { consensus, .. }) if consensus == block_with_difficulty.block_hash() - ); // None zero nonce @@ -137,7 +134,7 @@ fn payload_validation() { b }); assert_matches!( - try_into_sealed_block(block_with_nonce.clone(),None), + try_into_sealed_block(block_with_nonce.clone(), None, None), Err(PayloadError::BlockHash { consensus, .. }) if consensus == block_with_nonce.block_hash() ); diff --git a/crates/rpc/rpc-eth-api/Cargo.toml b/crates/rpc/rpc-eth-api/Cargo.toml index e59ee39a694b..9d0f6cfd83d6 100644 --- a/crates/rpc/rpc-eth-api/Cargo.toml +++ b/crates/rpc/rpc-eth-api/Cargo.toml @@ -40,6 +40,7 @@ alloy-primitives.workspace = true alloy-rpc-types-eth.workspace = true alloy-rpc-types.workspace = true alloy-rpc-types-mev.workspace = true +alloy-consensus.workspace = true # rpc jsonrpsee = { workspace = true, features = ["server", "macros"] } diff --git a/crates/rpc/rpc-eth-api/src/helpers/call.rs b/crates/rpc/rpc-eth-api/src/helpers/call.rs index cb5c4e79a732..b43b34305bd9 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/call.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/call.rs @@ -855,7 +855,7 @@ pub trait Call: LoadState + SpawnBlocking { // Update the gas used based on the new result. gas_used = res.result.gas_used(); // Update the gas limit estimates (highest and lowest) based on the execution result. - self.update_estimated_gas_range( + update_estimated_gas_range( res.result, optimistic_gas_limit, &mut highest_gas_limit, @@ -888,7 +888,11 @@ pub trait Call: LoadState + SpawnBlocking { // Execute transaction and handle potential gas errors, adjusting limits accordingly. match self.transact(&mut db, env.clone()) { Err(err) if err.is_gas_too_high() => { - // Increase the lowest gas limit if gas is too high + // Decrease the highest gas limit if gas is too high + highest_gas_limit = mid_gas_limit; + } + Err(err) if err.is_gas_too_low() => { + // Increase the lowest gas limit if gas is too low lowest_gas_limit = mid_gas_limit; } // Handle other cases, including successful transactions. @@ -896,7 +900,7 @@ pub trait Call: LoadState + SpawnBlocking { // Unpack the result and environment if the transaction was successful. (res, env) = ethres?; // Update the estimated gas range based on the transaction result. - self.update_estimated_gas_range( + update_estimated_gas_range( res.result, mid_gas_limit, &mut highest_gas_limit, @@ -912,55 +916,6 @@ pub trait Call: LoadState + SpawnBlocking { Ok(U256::from(highest_gas_limit)) } - /// Updates the highest and lowest gas limits for binary search based on the execution result. - /// - /// This function refines the gas limit estimates used in a binary search to find the optimal - /// gas limit for a transaction. It adjusts the highest or lowest gas limits depending on - /// whether the execution succeeded, reverted, or halted due to specific reasons. - #[inline] - fn update_estimated_gas_range( - &self, - result: ExecutionResult, - tx_gas_limit: u64, - highest_gas_limit: &mut u64, - lowest_gas_limit: &mut u64, - ) -> Result<(), Self::Error> { - match result { - ExecutionResult::Success { .. } => { - // Cap the highest gas limit with the succeeding gas limit. - *highest_gas_limit = tx_gas_limit; - } - ExecutionResult::Revert { .. } => { - // Increase the lowest gas limit. - *lowest_gas_limit = tx_gas_limit; - } - ExecutionResult::Halt { reason, .. } => { - match reason { - HaltReason::OutOfGas(_) | HaltReason::InvalidFEOpcode => { - // Both `OutOfGas` and `InvalidEFOpcode` can occur dynamically if the gas - // left is too low. Treat this as an out of gas - // condition, knowing that the call succeeds with a - // higher gas limit. - // - // Common usage of invalid opcode in OpenZeppelin: - // - - // Increase the lowest gas limit. - *lowest_gas_limit = tx_gas_limit; - } - err => { - // These cases should be unreachable because we know the transaction - // succeeds, but if they occur, treat them as an - // error. - return Err(RpcInvalidTransactionError::EvmHalt(err).into_eth_err()) - } - } - } - }; - - Ok(()) - } - /// Executes the requests again after an out of gas error to check if the error is gas related /// or not #[inline] @@ -1159,3 +1114,51 @@ pub trait Call: LoadState + SpawnBlocking { Ok(env) } } + +/// Updates the highest and lowest gas limits for binary search based on the execution result. +/// +/// This function refines the gas limit estimates used in a binary search to find the optimal +/// gas limit for a transaction. It adjusts the highest or lowest gas limits depending on +/// whether the execution succeeded, reverted, or halted due to specific reasons. +#[inline] +fn update_estimated_gas_range( + result: ExecutionResult, + tx_gas_limit: u64, + highest_gas_limit: &mut u64, + lowest_gas_limit: &mut u64, +) -> Result<(), EthApiError> { + match result { + ExecutionResult::Success { .. } => { + // Cap the highest gas limit with the succeeding gas limit. + *highest_gas_limit = tx_gas_limit; + } + ExecutionResult::Revert { .. } => { + // Increase the lowest gas limit. + *lowest_gas_limit = tx_gas_limit; + } + ExecutionResult::Halt { reason, .. } => { + match reason { + HaltReason::OutOfGas(_) | HaltReason::InvalidFEOpcode => { + // Both `OutOfGas` and `InvalidEFOpcode` can occur dynamically if the gas + // left is too low. Treat this as an out of gas + // condition, knowing that the call succeeds with a + // higher gas limit. + // + // Common usage of invalid opcode in OpenZeppelin: + // + + // Increase the lowest gas limit. + *lowest_gas_limit = tx_gas_limit; + } + err => { + // These cases should be unreachable because we know the transaction + // succeeds, but if they occur, treat them as an + // error. + return Err(RpcInvalidTransactionError::EvmHalt(err).into_eth_err()) + } + } + } + }; + + Ok(()) +} diff --git a/crates/rpc/rpc-eth-api/src/helpers/error.rs b/crates/rpc/rpc-eth-api/src/helpers/error.rs index 041a019052bd..1d991b8e65b6 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/error.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/error.rs @@ -59,6 +59,16 @@ pub trait AsEthApiError { false } + + /// Returns `true` if error is + /// [`RpcInvalidTransactionError::GasTooLow`](reth_rpc_eth_types::RpcInvalidTransactionError::GasTooLow). + fn is_gas_too_low(&self) -> bool { + if let Some(err) = self.as_err() { + return err.is_gas_too_low() + } + + false + } } impl AsEthApiError for EthApiError { diff --git a/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs b/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs index 26e0ffc7412a..407ddf1874ab 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs @@ -5,29 +5,32 @@ use std::time::{Duration, Instant}; use crate::{EthApiTypes, FromEthApiError, FromEvmError}; +use alloy_consensus::EMPTY_OMMER_ROOT_HASH; +use alloy_eips::eip7685::EMPTY_REQUESTS_HASH; use alloy_primitives::{BlockNumber, B256, U256}; use alloy_rpc_types::BlockNumberOrTag; use futures::Future; use reth_chainspec::{EthChainSpec, EthereumHardforks}; -use reth_evm::{system_calls::SystemCaller, ConfigureEvm, ConfigureEvmEnv}; +use reth_evm::{ + state_change::post_block_withdrawals_balance_increments, system_calls::SystemCaller, + ConfigureEvm, ConfigureEvmEnv, +}; use reth_execution_types::ExecutionOutcome; use reth_primitives::{ - constants::{eip4844::MAX_DATA_GAS_PER_BLOCK, BEACON_NONCE, EMPTY_ROOT_HASH}, + constants::{eip4844::MAX_DATA_GAS_PER_BLOCK, BEACON_NONCE}, proofs::calculate_transaction_root, revm_primitives::{ BlockEnv, CfgEnv, CfgEnvWithHandlerCfg, EVMError, Env, ExecutionResult, InvalidTransaction, ResultAndState, SpecId, }, - Block, BlockBody, Header, Receipt, Requests, SealedBlockWithSenders, SealedHeader, - TransactionSignedEcRecovered, EMPTY_OMMER_ROOT_HASH, + Block, BlockBody, Header, Receipt, SealedBlockWithSenders, SealedHeader, + TransactionSignedEcRecovered, }; use reth_provider::{ BlockReader, BlockReaderIdExt, ChainSpecProvider, EvmEnvProvider, ProviderError, ReceiptProvider, StateProviderFactory, }; -use reth_revm::{ - database::StateProviderDatabase, state_change::post_block_withdrawals_balance_increments, -}; +use reth_revm::database::StateProviderDatabase; use reth_rpc_eth_types::{EthApiError, PendingBlock, PendingBlockEnv, PendingBlockEnvOrigin}; use reth_transaction_pool::{BestTransactionsAttributes, TransactionPool}; use reth_trie::HashedPostState; @@ -155,7 +158,7 @@ pub trait LoadPendingBlock: EthApiTypes { pending.origin.header().hash() == pending_block.block.parent_hash && now <= pending_block.expires_at { - return Ok(Some((pending_block.block.clone(), pending_block.receipts.clone()))) + return Ok(Some((pending_block.block.clone(), pending_block.receipts.clone()))); } } @@ -416,14 +419,9 @@ pub trait LoadPendingBlock: EthApiTypes { let blob_gas_used = (cfg.handler_cfg.spec_id >= SpecId::CANCUN).then_some(sum_blob_gas_used); - // note(onbjerg): the rpc spec has not been changed to include requests, so for now we just - // set these to empty - let (requests, requests_root) = - if chain_spec.is_prague_active_at_timestamp(block_env.timestamp.to::()) { - (Some(Requests::default()), Some(EMPTY_ROOT_HASH)) - } else { - (None, None) - }; + let requests_hash = chain_spec + .is_prague_active_at_timestamp(block_env.timestamp.to::()) + .then_some(EMPTY_REQUESTS_HASH); let header = Header { parent_hash, @@ -446,7 +444,7 @@ pub trait LoadPendingBlock: EthApiTypes { excess_blob_gas: block_env.get_blob_excess_gas().map(Into::into), extra_data: Default::default(), parent_beacon_block_root, - requests_root, + requests_hash, }; // Convert Vec> to Vec @@ -455,7 +453,7 @@ pub trait LoadPendingBlock: EthApiTypes { // seal the block let block = Block { header, - body: BlockBody { transactions: executed_txs, ommers: vec![], withdrawals, requests }, + body: BlockBody { transactions: executed_txs, ommers: vec![], withdrawals }, }; Ok((SealedBlockWithSenders { block: block.seal_slow(), senders }, receipts)) } diff --git a/crates/rpc/rpc-eth-api/src/helpers/state.rs b/crates/rpc/rpc-eth-api/src/helpers/state.rs index 7b11ce6afe63..080d90dc3b00 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/state.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/state.rs @@ -1,13 +1,14 @@ //! Loads a pending block from database. Helper trait for `eth_` block, transaction, call and trace //! RPC methods. +use alloy_consensus::constants::KECCAK_EMPTY; use alloy_primitives::{Address, Bytes, B256, U256}; use alloy_rpc_types::{serde_helpers::JsonStorageKey, Account, EIP1186AccountProofResponse}; use futures::Future; use reth_chainspec::{EthChainSpec, EthereumHardforks}; use reth_errors::RethError; use reth_evm::ConfigureEvmEnv; -use reth_primitives::{BlockId, Header, KECCAK_EMPTY}; +use reth_primitives::{BlockId, Header}; use reth_provider::{ BlockIdReader, BlockNumReader, ChainSpecProvider, StateProvider, StateProviderBox, StateProviderFactory, @@ -271,6 +272,42 @@ pub trait LoadState: EthApiTypes { } } + /// Returns the next available nonce without gaps for the given address + /// Next available nonce is either the on chain nonce of the account or the highest consecutive + /// nonce in the pool + 1 + fn next_available_nonce( + &self, + address: Address, + ) -> impl Future> + Send + where + Self: SpawnBlocking, + { + self.spawn_blocking_io(move |this| { + // first fetch the on chain nonce of the account + let on_chain_account_nonce = this + .latest_state()? + .account_nonce(address) + .map_err(Self::Error::from_eth_err)? + .unwrap_or_default(); + + let mut next_nonce = on_chain_account_nonce; + // Retrieve the highest consecutive transaction for the sender from the transaction pool + if let Some(highest_tx) = this + .pool() + .get_highest_consecutive_transaction_by_sender(address, on_chain_account_nonce) + { + // Return the nonce of the highest consecutive transaction + 1 + next_nonce = highest_tx.nonce().checked_add(1).ok_or_else(|| { + Self::Error::from(EthApiError::InvalidTransaction( + RpcInvalidTransactionError::NonceMaxValue, + )) + })?; + } + + Ok(next_nonce) + }) + } + /// Returns the number of transactions sent from an address at the given block identifier. /// /// If this is [`BlockNumberOrTag::Pending`](reth_primitives::BlockNumberOrTag) then this will diff --git a/crates/rpc/rpc-eth-api/src/helpers/trace.rs b/crates/rpc/rpc-eth-api/src/helpers/trace.rs index 457cbb4811f3..981de8fa6c45 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/trace.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/trace.rs @@ -1,12 +1,14 @@ //! Loads a pending block from database. Helper trait for `eth_` call and trace RPC methods. +use std::sync::Arc; + use crate::FromEvmError; use alloy_primitives::B256; use alloy_rpc_types::{BlockId, TransactionInfo}; use futures::Future; use reth_chainspec::ChainSpecProvider; use reth_evm::{system_calls::SystemCaller, ConfigureEvm, ConfigureEvmEnv}; -use reth_primitives::Header; +use reth_primitives::{Header, SealedBlockWithSenders}; use reth_revm::database::StateProviderDatabase; use reth_rpc_eth_types::{ cache::db::{StateCacheDb, StateCacheDbRefMutWrapper, StateProviderTraitObjWrapper}, @@ -247,6 +249,7 @@ pub trait Trace: LoadState { fn trace_block_until( &self, block_id: BlockId, + block: Option>, highest_index: Option, config: TracingInspectorConfig, f: F, @@ -266,6 +269,7 @@ pub trait Trace: LoadState { { self.trace_block_until_with_inspector( block_id, + block, highest_index, move || TracingInspector::new(config), f, @@ -285,6 +289,7 @@ pub trait Trace: LoadState { fn trace_block_until_with_inspector( &self, block_id: BlockId, + block: Option>, highest_index: Option, mut inspector_setup: Setup, f: F, @@ -305,8 +310,15 @@ pub trait Trace: LoadState { R: Send + 'static, { async move { + let block = async { + if block.is_some() { + return Ok(block) + } + self.block_with_senders(block_id).await + }; + let ((cfg, block_env, _), block) = - futures::try_join!(self.evm_env_at(block_id), self.block_with_senders(block_id))?; + futures::try_join!(self.evm_env_at(block_id), block)?; let Some(block) = block else { return Ok(None) }; @@ -409,6 +421,7 @@ pub trait Trace: LoadState { fn trace_block_with( &self, block_id: BlockId, + block: Option>, config: TracingInspectorConfig, f: F, ) -> impl Future>, Self::Error>> + Send @@ -427,7 +440,7 @@ pub trait Trace: LoadState { + 'static, R: Send + 'static, { - self.trace_block_until(block_id, None, config, f) + self.trace_block_until(block_id, block, None, config, f) } /// Executes all transactions of a block and returns a list of callback results invoked for each @@ -447,6 +460,7 @@ pub trait Trace: LoadState { fn trace_block_inspector( &self, block_id: BlockId, + block: Option>, insp_setup: Setup, f: F, ) -> impl Future>, Self::Error>> + Send @@ -467,6 +481,6 @@ pub trait Trace: LoadState { Insp: for<'a, 'b> Inspector> + Send + 'static, R: Send + 'static, { - self.trace_block_until_with_inspector(block_id, None, insp_setup, f) + self.trace_block_until_with_inspector(block_id, block, None, insp_setup, f) } } diff --git a/crates/rpc/rpc-eth-api/src/helpers/transaction.rs b/crates/rpc/rpc-eth-api/src/helpers/transaction.rs index 4c2493717a04..d29787d7a23b 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/transaction.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/transaction.rs @@ -1,6 +1,7 @@ //! Database access for `eth_` transaction RPC methods. Loads transaction and receipt data w.r.t. //! network. +use alloy_consensus::Transaction; use alloy_dyn_abi::TypedData; use alloy_eips::eip2718::Encodable2718; use alloy_network::TransactionBuilder; @@ -364,9 +365,8 @@ pub trait EthTransactions: LoadTransaction { // set nonce if not already set before if request.nonce.is_none() { - let nonce = self.transaction_count(from, Some(BlockId::pending())).await?; - // note: `.to()` can't panic because the nonce is constructed from a `u64` - request.nonce = Some(nonce.to()); + let nonce = self.next_available_nonce(from).await?; + request.nonce = Some(nonce); } let chain_id = self.chain_id(); diff --git a/crates/rpc/rpc-eth-types/src/builder/ctx.rs b/crates/rpc/rpc-eth-types/src/builder/ctx.rs index cb2750b6e54f..2132dd0e22c4 100644 --- a/crates/rpc/rpc-eth-types/src/builder/ctx.rs +++ b/crates/rpc/rpc-eth-types/src/builder/ctx.rs @@ -1,7 +1,5 @@ //! Context required for building `eth` namespace APIs. -use std::marker::PhantomData; - use reth_chain_state::CanonStateSubscriptions; use reth_chainspec::ChainSpecProvider; use reth_storage_api::BlockReaderIdExt; @@ -14,7 +12,7 @@ use crate::{ /// Context for building the `eth` namespace API. #[derive(Debug, Clone)] -pub struct EthApiBuilderCtx { +pub struct EthApiBuilderCtx { /// Database handle. pub provider: Provider, /// Mempool handle. @@ -31,12 +29,10 @@ pub struct EthApiBuilderCtx, } -impl - EthApiBuilderCtx +impl + EthApiBuilderCtx where Provider: BlockReaderIdExt + Clone, { @@ -46,53 +42,14 @@ where Provider: ChainSpecProvider + 'static, Tasks: TaskSpawner, Events: CanonStateSubscriptions, - { - FeeHistoryCacheBuilder::build(self) - } - - /// Returns a new [`GasPriceOracle`] for the context. - pub fn new_gas_price_oracle(&self) -> GasPriceOracle { - GasPriceOracleBuilder::build(self) - } -} - -/// Builds `eth_` core api component [`GasPriceOracle`], for given context. -#[derive(Debug)] -pub struct GasPriceOracleBuilder; - -impl GasPriceOracleBuilder { - /// Builds a [`GasPriceOracle`], for given context. - pub fn build( - ctx: &EthApiBuilderCtx, - ) -> GasPriceOracle - where - Provider: BlockReaderIdExt + Clone, - { - GasPriceOracle::new(ctx.provider.clone(), ctx.config.gas_oracle, ctx.cache.clone()) - } -} - -/// Builds `eth_` core api component [`FeeHistoryCache`], for given context. -#[derive(Debug)] -pub struct FeeHistoryCacheBuilder; - -impl FeeHistoryCacheBuilder { - /// Builds a [`FeeHistoryCache`], for given context. - pub fn build( - ctx: &EthApiBuilderCtx, - ) -> FeeHistoryCache - where - Provider: ChainSpecProvider + BlockReaderIdExt + Clone + 'static, - Tasks: TaskSpawner, - Events: CanonStateSubscriptions, { let fee_history_cache = - FeeHistoryCache::new(ctx.cache.clone(), ctx.config.fee_history_cache); + FeeHistoryCache::new(self.cache.clone(), self.config.fee_history_cache); - let new_canonical_blocks = ctx.events.canonical_state_stream(); + let new_canonical_blocks = self.events.canonical_state_stream(); let fhc = fee_history_cache.clone(); - let provider = ctx.provider.clone(); - ctx.executor.spawn_critical( + let provider = self.provider.clone(); + self.executor.spawn_critical( "cache canonical blocks for fee history task", Box::pin(async move { fee_history_cache_new_blocks_task(fhc, new_canonical_blocks, provider).await; @@ -101,4 +58,9 @@ impl FeeHistoryCacheBuilder { fee_history_cache } + + /// Returns a new [`GasPriceOracle`] for the context. + pub fn new_gas_price_oracle(&self) -> GasPriceOracle { + GasPriceOracle::new(self.provider.clone(), self.config.gas_oracle, self.cache.clone()) + } } diff --git a/crates/rpc/rpc-eth-types/src/error.rs b/crates/rpc/rpc-eth-types/src/error.rs index 212fca36d9c9..b38b3122708b 100644 --- a/crates/rpc/rpc-eth-types/src/error.rs +++ b/crates/rpc/rpc-eth-types/src/error.rs @@ -151,6 +151,11 @@ impl EthApiError { pub const fn is_gas_too_high(&self) -> bool { matches!(self, Self::InvalidTransaction(RpcInvalidTransactionError::GasTooHigh)) } + + /// Returns `true` if error is [`RpcInvalidTransactionError::GasTooLow`] + pub const fn is_gas_too_low(&self) -> bool { + matches!(self, Self::InvalidTransaction(RpcInvalidTransactionError::GasTooLow)) + } } impl From for jsonrpsee_types::error::ErrorObject<'static> { diff --git a/crates/rpc/rpc-eth-types/src/fee_history.rs b/crates/rpc/rpc-eth-types/src/fee_history.rs index 57dd276e5cf4..c845d9683870 100644 --- a/crates/rpc/rpc-eth-types/src/fee_history.rs +++ b/crates/rpc/rpc-eth-types/src/fee_history.rs @@ -305,7 +305,7 @@ pub fn calculate_reward_percentiles_for_block( // the percentiles are monotonically increasing. let mut tx_index = 0; let mut cumulative_gas_used = transactions.first().map(|tx| tx.gas_used).unwrap_or_default(); - let mut rewards_in_block = Vec::new(); + let mut rewards_in_block = Vec::with_capacity(percentiles.len()); for percentile in percentiles { // Empty blocks should return in a zero row if transactions.is_empty() { diff --git a/crates/rpc/rpc-eth-types/src/receipt.rs b/crates/rpc/rpc-eth-types/src/receipt.rs index e95c92f24ae6..c3232f2383b8 100644 --- a/crates/rpc/rpc-eth-types/src/receipt.rs +++ b/crates/rpc/rpc-eth-types/src/receipt.rs @@ -1,5 +1,6 @@ //! RPC receipt response builder, extends a layer one receipt with layer two data. +use alloy_consensus::Transaction; use alloy_primitives::{Address, TxKind}; use alloy_rpc_types::{ AnyReceiptEnvelope, AnyTransactionReceipt, Log, ReceiptWithBloom, TransactionReceipt, @@ -101,8 +102,6 @@ impl ReceiptBuilder { gas_used: gas_used as u128, contract_address, effective_gas_price: transaction.effective_gas_price(meta.base_fee), - // TODO pre-byzantium receipts have a post-transaction state root - state_root: None, // EIP-4844 fields blob_gas_price, blob_gas_used: blob_gas_used.map(u128::from), diff --git a/crates/rpc/rpc-eth-types/src/simulate.rs b/crates/rpc/rpc-eth-types/src/simulate.rs index 561aa360d86f..77db511e6251 100644 --- a/crates/rpc/rpc-eth-types/src/simulate.rs +++ b/crates/rpc/rpc-eth-types/src/simulate.rs @@ -1,6 +1,6 @@ //! Utilities for serving `eth_simulateV1` -use alloy_consensus::{TxEip4844Variant, TxType, TypedTransaction}; +use alloy_consensus::{Transaction as _, TxEip4844Variant, TxType, TypedTransaction}; use alloy_primitives::Parity; use alloy_rpc_types::{ simulate::{SimCallResult, SimulateError, SimulatedBlock}, @@ -183,7 +183,7 @@ pub fn build_block( ) -> Result>, EthApiError> { let mut calls: Vec = Vec::with_capacity(results.len()); let mut senders = Vec::with_capacity(results.len()); - let mut receipts = Vec::new(); + let mut receipts = Vec::with_capacity(results.len()); let mut log_index = 0; for (transaction_index, ((sender, result), tx)) in diff --git a/crates/rpc/rpc-server-types/src/module.rs b/crates/rpc/rpc-server-types/src/module.rs index 72a5e7c85833..56417dda701c 100644 --- a/crates/rpc/rpc-server-types/src/module.rs +++ b/crates/rpc/rpc-server-types/src/module.rs @@ -199,9 +199,12 @@ impl FromStr for RpcModuleSelection { } let mut modules = s.split(',').map(str::trim).peekable(); let first = modules.peek().copied().ok_or(ParseError::VariantNotFound)?; - match first { - "all" | "All" => Ok(Self::All), - "none" | "None" => Ok(Self::Selection(Default::default())), + // We convert to lowercase to make the comparison case-insensitive + // + // This is a way to allow typing "all" and "ALL" and "All" and "aLl" etc. + match first.to_lowercase().as_str() { + "all" => Ok(Self::All), + "none" => Ok(Self::Selection(Default::default())), _ => Self::try_from_selection(modules), } } @@ -329,3 +332,229 @@ impl Serialize for RethRpcModule { s.serialize_str(self.as_ref()) } } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_all_modules() { + let all_modules = RpcModuleSelection::all_modules(); + assert_eq!(all_modules.len(), RethRpcModule::variant_count()); + } + + #[test] + fn test_standard_modules() { + let standard_modules = RpcModuleSelection::standard_modules(); + let expected_modules: HashSet = + HashSet::from([RethRpcModule::Eth, RethRpcModule::Net, RethRpcModule::Web3]); + assert_eq!(standard_modules, expected_modules); + } + + #[test] + fn test_default_ipc_modules() { + let default_ipc_modules = RpcModuleSelection::default_ipc_modules(); + assert_eq!(default_ipc_modules, RpcModuleSelection::all_modules()); + } + + #[test] + fn test_try_from_selection_success() { + let selection = vec!["eth", "admin"]; + let config = RpcModuleSelection::try_from_selection(selection).unwrap(); + assert_eq!(config, RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin])); + } + + #[test] + fn test_rpc_module_selection_len() { + let all_modules = RpcModuleSelection::All; + let standard = RpcModuleSelection::Standard; + let selection = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]); + + assert_eq!(all_modules.len(), RethRpcModule::variant_count()); + assert_eq!(standard.len(), 3); + assert_eq!(selection.len(), 2); + } + + #[test] + fn test_rpc_module_selection_is_empty() { + let empty_selection = RpcModuleSelection::from(HashSet::new()); + assert!(empty_selection.is_empty()); + + let non_empty_selection = RpcModuleSelection::from([RethRpcModule::Eth]); + assert!(!non_empty_selection.is_empty()); + } + + #[test] + fn test_rpc_module_selection_iter_selection() { + let all_modules = RpcModuleSelection::All; + let standard = RpcModuleSelection::Standard; + let selection = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]); + + assert_eq!(all_modules.iter_selection().count(), RethRpcModule::variant_count()); + assert_eq!(standard.iter_selection().count(), 3); + assert_eq!(selection.iter_selection().count(), 2); + } + + #[test] + fn test_rpc_module_selection_to_selection() { + let all_modules = RpcModuleSelection::All; + let standard = RpcModuleSelection::Standard; + let selection = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]); + + assert_eq!(all_modules.to_selection(), RpcModuleSelection::all_modules()); + assert_eq!(standard.to_selection(), RpcModuleSelection::standard_modules()); + assert_eq!( + selection.to_selection(), + HashSet::from([RethRpcModule::Eth, RethRpcModule::Admin]) + ); + } + + #[test] + fn test_rpc_module_selection_are_identical() { + // Test scenario: both selections are `All` + // + // Since both selections include all possible RPC modules, they should be considered + // identical. + let all_modules = RpcModuleSelection::All; + assert!(RpcModuleSelection::are_identical(Some(&all_modules), Some(&all_modules))); + + // Test scenario: both `http` and `ws` are `None` + // + // When both arguments are `None`, the function should return `true` because no modules are + // selected. + assert!(RpcModuleSelection::are_identical(None, None)); + + // Test scenario: both selections contain identical sets of specific modules + // + // In this case, both selections contain the same modules (`Eth` and `Admin`), + // so they should be considered identical. + let selection1 = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]); + let selection2 = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]); + assert!(RpcModuleSelection::are_identical(Some(&selection1), Some(&selection2))); + + // Test scenario: one selection is `All`, the other is `Standard` + // + // `All` includes all possible modules, while `Standard` includes a specific set of modules. + // Since `Standard` does not cover all modules, these two selections should not be + // considered identical. + let standard = RpcModuleSelection::Standard; + assert!(!RpcModuleSelection::are_identical(Some(&all_modules), Some(&standard))); + + // Test scenario: one is `None`, the other is an empty selection + // + // When one selection is `None` and the other is an empty selection (no modules), + // they should be considered identical because neither selects any modules. + let empty_selection = RpcModuleSelection::Selection(HashSet::new()); + assert!(RpcModuleSelection::are_identical(None, Some(&empty_selection))); + assert!(RpcModuleSelection::are_identical(Some(&empty_selection), None)); + + // Test scenario: one is `None`, the other is a non-empty selection + // + // If one selection is `None` and the other contains modules, they should not be considered + // identical because `None` represents no selection, while the other explicitly + // selects modules. + let non_empty_selection = RpcModuleSelection::from([RethRpcModule::Eth]); + assert!(!RpcModuleSelection::are_identical(None, Some(&non_empty_selection))); + assert!(!RpcModuleSelection::are_identical(Some(&non_empty_selection), None)); + + // Test scenario: `All` vs. non-full selection + // + // If one selection is `All` (which includes all modules) and the other contains only a + // subset of modules, they should not be considered identical. + let partial_selection = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Net]); + assert!(!RpcModuleSelection::are_identical(Some(&all_modules), Some(&partial_selection))); + + // Test scenario: full selection vs `All` + // + // If the other selection explicitly selects all available modules, it should be identical + // to `All`. + let full_selection = + RpcModuleSelection::from(RethRpcModule::modules().into_iter().collect::>()); + assert!(RpcModuleSelection::are_identical(Some(&all_modules), Some(&full_selection))); + + // Test scenario: different non-empty selections + // + // If the two selections contain different sets of modules, they should not be considered + // identical. + let selection3 = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Net]); + let selection4 = RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Web3]); + assert!(!RpcModuleSelection::are_identical(Some(&selection3), Some(&selection4))); + + // Test scenario: `Standard` vs an equivalent selection + // The `Standard` selection includes a predefined set of modules. If we explicitly create + // a selection with the same set of modules, they should be considered identical. + let matching_standard = + RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Net, RethRpcModule::Web3]); + assert!(RpcModuleSelection::are_identical(Some(&standard), Some(&matching_standard))); + + // Test scenario: `Standard` vs non-matching selection + // + // If the selection does not match the modules included in `Standard`, they should not be + // considered identical. + let non_matching_standard = + RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Net]); + assert!(!RpcModuleSelection::are_identical(Some(&standard), Some(&non_matching_standard))); + } + + #[test] + fn test_rpc_module_selection_from_str() { + // Test empty string returns default selection + let result = RpcModuleSelection::from_str(""); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), RpcModuleSelection::Selection(Default::default())); + + // Test "all" (case insensitive) returns All variant + let result = RpcModuleSelection::from_str("all"); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), RpcModuleSelection::All); + + let result = RpcModuleSelection::from_str("All"); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), RpcModuleSelection::All); + + let result = RpcModuleSelection::from_str("ALL"); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), RpcModuleSelection::All); + + // Test "none" (case insensitive) returns empty selection + let result = RpcModuleSelection::from_str("none"); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), RpcModuleSelection::Selection(Default::default())); + + let result = RpcModuleSelection::from_str("None"); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), RpcModuleSelection::Selection(Default::default())); + + let result = RpcModuleSelection::from_str("NONE"); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), RpcModuleSelection::Selection(Default::default())); + + // Test valid selections: "eth,admin" + let result = RpcModuleSelection::from_str("eth,admin"); + assert!(result.is_ok()); + let expected_selection = + RpcModuleSelection::from([RethRpcModule::Eth, RethRpcModule::Admin]); + assert_eq!(result.unwrap(), expected_selection); + + // Test valid selection with extra spaces: " eth , admin " + let result = RpcModuleSelection::from_str(" eth , admin "); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), expected_selection); + + // Test invalid selection should return error + let result = RpcModuleSelection::from_str("invalid,unknown"); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), ParseError::VariantNotFound); + + // Test single valid selection: "eth" + let result = RpcModuleSelection::from_str("eth"); + assert!(result.is_ok()); + let expected_selection = RpcModuleSelection::from([RethRpcModule::Eth]); + assert_eq!(result.unwrap(), expected_selection); + + // Test single invalid selection: "unknown" + let result = RpcModuleSelection::from_str("unknown"); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), ParseError::VariantNotFound); + } +} diff --git a/crates/rpc/rpc-types-compat/Cargo.toml b/crates/rpc/rpc-types-compat/Cargo.toml index 81b4def204f9..8e436f0d3934 100644 --- a/crates/rpc/rpc-types-compat/Cargo.toml +++ b/crates/rpc/rpc-types-compat/Cargo.toml @@ -24,6 +24,7 @@ alloy-rpc-types.workspace = true alloy-rpc-types-eth = { workspace = true, default-features = false, features = ["serde"] } alloy-serde.workspace = true alloy-rpc-types-engine.workspace = true +alloy-consensus.workspace = true [dev-dependencies] serde_json.workspace = true \ No newline at end of file diff --git a/crates/rpc/rpc-types-compat/src/block.rs b/crates/rpc/rpc-types-compat/src/block.rs index fc8ea9e1c487..a650a69c1c1f 100644 --- a/crates/rpc/rpc-types-compat/src/block.rs +++ b/crates/rpc/rpc-types-compat/src/block.rs @@ -124,7 +124,7 @@ pub fn from_primitive_with_hash(primitive_header: reth_primitives::SealedHeader) blob_gas_used, excess_blob_gas, parent_beacon_block_root, - requests_root, + requests_hash, } = header; Header { @@ -150,7 +150,7 @@ pub fn from_primitive_with_hash(primitive_header: reth_primitives::SealedHeader) excess_blob_gas, parent_beacon_block_root, total_difficulty: None, - requests_root, + requests_hash, } } diff --git a/crates/rpc/rpc-types-compat/src/engine/payload.rs b/crates/rpc/rpc-types-compat/src/engine/payload.rs index 84943b60e208..b63b7453aeb0 100644 --- a/crates/rpc/rpc-types-compat/src/engine/payload.rs +++ b/crates/rpc/rpc-types-compat/src/engine/payload.rs @@ -1,17 +1,19 @@ //! Standalone Conversion Functions for Handling Different Versions of Execution Payloads in //! Ethereum's Engine -use alloy_eips::eip2718::{Decodable2718, Encodable2718}; +use alloy_consensus::{constants::MAXIMUM_EXTRA_DATA_SIZE, EMPTY_OMMER_ROOT_HASH}; +use alloy_eips::{ + eip2718::{Decodable2718, Encodable2718}, + eip7685::Requests, +}; use alloy_primitives::{B256, U256}; use alloy_rpc_types_engine::{ payload::{ExecutionPayloadBodyV1, ExecutionPayloadFieldV2, ExecutionPayloadInputV2}, - ExecutionPayload, ExecutionPayloadBodyV2, ExecutionPayloadV1, ExecutionPayloadV2, - ExecutionPayloadV3, ExecutionPayloadV4, PayloadError, + ExecutionPayload, ExecutionPayloadV1, ExecutionPayloadV2, ExecutionPayloadV3, PayloadError, }; use reth_primitives::{ - constants::{EMPTY_OMMER_ROOT_HASH, MAXIMUM_EXTRA_DATA_SIZE}, proofs::{self}, - Block, BlockBody, Header, Request, SealedBlock, TransactionSigned, Withdrawals, + Block, BlockBody, Header, SealedBlock, TransactionSigned, Withdrawals, }; /// Converts [`ExecutionPayloadV1`] to [`Block`] @@ -67,7 +69,7 @@ pub fn try_payload_v1_to_block(payload: ExecutionPayloadV1) -> Result Result Result { - let ExecutionPayloadV4 { - payload_inner, - deposit_requests, - withdrawal_requests, - consolidation_requests, - } = payload; - let mut block = try_payload_v3_to_block(payload_inner)?; - - // attach requests with asc type identifiers - let requests = deposit_requests - .into_iter() - .map(Request::DepositRequest) - .chain(withdrawal_requests.into_iter().map(Request::WithdrawalRequest)) - .chain(consolidation_requests.into_iter().map(Request::ConsolidationRequest)) - .collect::>(); - - let requests_root = proofs::calculate_requests_root(&requests); - block.header.requests_root = Some(requests_root); - block.body.requests = Some(requests.into()); - - Ok(block) -} - /// Converts [`SealedBlock`] to [`ExecutionPayload`] pub fn block_to_payload(value: SealedBlock) -> ExecutionPayload { - if value.header.requests_root.is_some() { + if value.header.requests_hash.is_some() { // block with requests root: V3 - ExecutionPayload::V4(block_to_payload_v4(value)) + ExecutionPayload::V3(block_to_payload_v3(value)) } else if value.header.parent_beacon_block_root.is_some() { // block with parent beacon block root: V3 ExecutionPayload::V3(block_to_payload_v3(value)) @@ -217,37 +194,6 @@ pub fn block_to_payload_v3(value: SealedBlock) -> ExecutionPayloadV3 { } } -/// Converts [`SealedBlock`] to [`ExecutionPayloadV4`] -pub fn block_to_payload_v4(mut value: SealedBlock) -> ExecutionPayloadV4 { - let (deposit_requests, withdrawal_requests, consolidation_requests) = - value.body.requests.take().unwrap_or_default().into_iter().fold( - (Vec::new(), Vec::new(), Vec::new()), - |(mut deposits, mut withdrawals, mut consolidation_requests), request| { - match request { - Request::DepositRequest(r) => { - deposits.push(r); - } - Request::WithdrawalRequest(r) => { - withdrawals.push(r); - } - Request::ConsolidationRequest(r) => { - consolidation_requests.push(r); - } - _ => {} - }; - - (deposits, withdrawals, consolidation_requests) - }, - ); - - ExecutionPayloadV4 { - deposit_requests, - withdrawal_requests, - consolidation_requests, - payload_inner: block_to_payload_v3(value), - } -} - /// Converts [`SealedBlock`] to [`ExecutionPayloadFieldV2`] pub fn convert_block_to_payload_field_v2(value: SealedBlock) -> ExecutionPayloadFieldV2 { // if there are withdrawals, return V2 @@ -312,15 +258,16 @@ pub fn convert_block_to_payload_input_v2(value: SealedBlock) -> ExecutionPayload pub fn try_into_block( value: ExecutionPayload, parent_beacon_block_root: Option, + execution_requests: Option, ) -> Result { let mut base_payload = match value { ExecutionPayload::V1(payload) => try_payload_v1_to_block(payload)?, ExecutionPayload::V2(payload) => try_payload_v2_to_block(payload)?, ExecutionPayload::V3(payload) => try_payload_v3_to_block(payload)?, - ExecutionPayload::V4(payload) => try_payload_v4_to_block(payload)?, }; base_payload.header.parent_beacon_block_root = parent_beacon_block_root; + base_payload.header.requests_hash = execution_requests.map(|reqs| reqs.requests_hash()); Ok(base_payload) } @@ -338,9 +285,10 @@ pub fn try_into_block( pub fn try_into_sealed_block( payload: ExecutionPayload, parent_beacon_block_root: Option, + execution_requests: Option, ) -> Result { let block_hash = payload.block_hash(); - let base_payload = try_into_block(payload, parent_beacon_block_root)?; + let base_payload = try_into_block(payload, parent_beacon_block_root, execution_requests)?; // validate block hash and return validate_block_hash(block_hash, base_payload) @@ -380,52 +328,6 @@ pub fn convert_to_payload_body_v1(value: Block) -> ExecutionPayloadBodyV1 { } } -/// Converts [`Block`] to [`ExecutionPayloadBodyV2`] -pub fn convert_to_payload_body_v2(value: Block) -> ExecutionPayloadBodyV2 { - let transactions = value.body.transactions.into_iter().map(|tx| { - let mut out = Vec::new(); - tx.encode_2718(&mut out); - out.into() - }); - - let mut payload = ExecutionPayloadBodyV2 { - transactions: transactions.collect(), - withdrawals: value.body.withdrawals.map(Withdrawals::into_inner), - deposit_requests: None, - withdrawal_requests: None, - consolidation_requests: None, - }; - - if let Some(requests) = value.body.requests { - let (deposit_requests, withdrawal_requests, consolidation_requests) = - requests.into_iter().fold( - (Vec::new(), Vec::new(), Vec::new()), - |(mut deposits, mut withdrawals, mut consolidation_requests), request| { - match request { - Request::DepositRequest(r) => { - deposits.push(r); - } - Request::WithdrawalRequest(r) => { - withdrawals.push(r); - } - Request::ConsolidationRequest(r) => { - consolidation_requests.push(r); - } - _ => {} - }; - - (deposits, withdrawals, consolidation_requests) - }, - ); - - payload.deposit_requests = Some(deposit_requests); - payload.withdrawal_requests = Some(withdrawal_requests); - payload.consolidation_requests = Some(consolidation_requests); - } - - payload -} - /// Transforms a [`SealedBlock`] into a [`ExecutionPayloadV1`] pub fn execution_payload_from_sealed_block(value: SealedBlock) -> ExecutionPayloadV1 { let transactions = value.raw_transactions(); @@ -450,13 +352,12 @@ pub fn execution_payload_from_sealed_block(value: SealedBlock) -> ExecutionPaylo #[cfg(test)] mod tests { use super::{ - block_to_payload_v3, try_into_block, try_payload_v3_to_block, try_payload_v4_to_block, - validate_block_hash, + block_to_payload_v3, try_into_block, try_payload_v3_to_block, validate_block_hash, }; use alloy_primitives::{b256, hex, Bytes, U256}; use alloy_rpc_types_engine::{ CancunPayloadFields, ExecutionPayload, ExecutionPayloadV1, ExecutionPayloadV2, - ExecutionPayloadV3, ExecutionPayloadV4, + ExecutionPayloadV3, }; #[test] @@ -674,60 +575,10 @@ mod tests { let cancun_fields = CancunPayloadFields { parent_beacon_block_root, versioned_hashes }; // convert into block - let block = try_into_block(payload, Some(cancun_fields.parent_beacon_block_root)).unwrap(); + let block = + try_into_block(payload, Some(cancun_fields.parent_beacon_block_root), None).unwrap(); // Ensure the actual hash is calculated if we set the fields to what they should be validate_block_hash(block_hash_with_blob_fee_fields, block).unwrap(); } - - #[test] - fn parse_payload_v4() { - let s = r#"{ - "baseFeePerGas": "0x2ada43", - "blobGasUsed": "0x0", - "blockHash": "0x86eeb2a4b656499f313b601e1dcaedfeacccab27131b6d4ea99bc69a57607f7d", - "blockNumber": "0x2c", - "depositRequests": [ - { - "amount": "0xe8d4a51000", - "index": "0x0", - "pubkey": "0xaab5f2b3aad5c2075faf0c1d8937c7de51a53b765a21b4173eb2975878cea05d9ed3428b77f16a981716aa32af74c464", - "signature": "0xa889cd238be2dae44f2a3c24c04d686c548f6f82eb44d4604e1bc455b6960efb72b117e878068a8f2cfb91ad84b7ebce05b9254207aa51a1e8a3383d75b5a5bd2439f707636ea5b17b2b594b989c93b000b33e5dff6e4bed9d53a6d2d6889b0c", - "withdrawalCredentials": "0x00ab9364f8bf7561862ea0fc3b69c424c94ace406c4dc36ddfbf8a9d72051c80" - }, - { - "amount": "0xe8d4a51000", - "index": "0x1", - "pubkey": "0xb0b1b3b51cf688ead965a954c5cc206ba4e76f3f8efac60656ae708a9aad63487a2ca1fb30ccaf2ebe1028a2b2886b1b", - "signature": "0xb9759766e9bb191b1c457ae1da6cdf71a23fb9d8bc9f845eaa49ee4af280b3b9720ac4d81e64b1b50a65db7b8b4e76f1176a12e19d293d75574600e99fbdfecc1ab48edaeeffb3226cd47691d24473821dad0c6ff3973f03e4aa89f418933a56", - "withdrawalCredentials": "0x002d2b75f4a27f78e585a4735a40ab2437eceb12ec39938a94dc785a54d62513" - } - ], - "excessBlobGas": "0x0", - "extraData": "0x726574682f76302e322e302d626574612e372f6c696e7578", - "feeRecipient": "0x8943545177806ed17b9f23f0a21ee5948ecaa776", - "gasLimit": "0x1855e85", - "gasUsed": "0x25f98", - "logsBloom": "0x10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000", - "parentHash": "0xd753194ef19b5c566b7eca6e9ebcca03895b548e1e93a20a23d922ba0bc210d4", - "prevRandao": "0x8c52256fd491776dc32f531ad4c0dc1444684741bca15f54c9cd40c60142df90", - "receiptsRoot": "0x510e7fb94279897e5dcd6c1795f6137d8fe02e49e871bfea7999fd21a89f66aa", - "stateRoot": "0x59ae0706a2b47162666fc7af3e30ff7aa34154954b68cc6aed58c3af3d58c9c2", - "timestamp": "0x6643c5a9", - "transactions": [ - "0x02f9021e8330182480843b9aca0085174876e80083030d40944242424242424242424242424242424242424242893635c9adc5dea00000b901a422895118000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000012049f42823819771c6bbbd9cb6649850083fd3b6e5d0beb1069342c32d65a3b0990000000000000000000000000000000000000000000000000000000000000030aab5f2b3aad5c2075faf0c1d8937c7de51a53b765a21b4173eb2975878cea05d9ed3428b77f16a981716aa32af74c46400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000ab9364f8bf7561862ea0fc3b69c424c94ace406c4dc36ddfbf8a9d72051c800000000000000000000000000000000000000000000000000000000000000060a889cd238be2dae44f2a3c24c04d686c548f6f82eb44d4604e1bc455b6960efb72b117e878068a8f2cfb91ad84b7ebce05b9254207aa51a1e8a3383d75b5a5bd2439f707636ea5b17b2b594b989c93b000b33e5dff6e4bed9d53a6d2d6889b0cc080a0db786f0d89923949e533680524f003cebd66f32fbd30429a6b6bfbd3258dcf60a05241c54e05574765f7ddc1a742ae06b044edfe02bffb202bf172be97397eeca9", - "0x02f9021e8330182401843b9aca0085174876e80083030d40944242424242424242424242424242424242424242893635c9adc5dea00000b901a422895118000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120d694d6a0b0103651aafd87db6c88297175d7317c6e6da53ccf706c3c991c91fd0000000000000000000000000000000000000000000000000000000000000030b0b1b3b51cf688ead965a954c5cc206ba4e76f3f8efac60656ae708a9aad63487a2ca1fb30ccaf2ebe1028a2b2886b1b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020002d2b75f4a27f78e585a4735a40ab2437eceb12ec39938a94dc785a54d625130000000000000000000000000000000000000000000000000000000000000060b9759766e9bb191b1c457ae1da6cdf71a23fb9d8bc9f845eaa49ee4af280b3b9720ac4d81e64b1b50a65db7b8b4e76f1176a12e19d293d75574600e99fbdfecc1ab48edaeeffb3226cd47691d24473821dad0c6ff3973f03e4aa89f418933a56c080a099dc5b94a51e9b91a6425b1fed9792863006496ab71a4178524819d7db0c5e88a0119748e62700234079d91ae80f4676f9e0f71b260e9b46ef9b4aff331d3c2318" - ], - "withdrawalRequests": [], - "withdrawals": [], - "consolidationRequests": [] - }"#; - - let payload = serde_json::from_str::(s).unwrap(); - let mut block = try_payload_v4_to_block(payload).unwrap(); - block.header.parent_beacon_block_root = - Some(b256!("d9851db05fa63593f75e2b12c4bba9f47740613ca57da3b523a381b8c27f3297")); - let hash = block.seal_slow().hash(); - assert_eq!(hash, b256!("86eeb2a4b656499f313b601e1dcaedfeacccab27131b6d4ea99bc69a57607f7d")) - } } diff --git a/crates/rpc/rpc-types-compat/src/transaction/mod.rs b/crates/rpc/rpc-types-compat/src/transaction/mod.rs index 9bb2a8b5d92a..7ffd48cb1f72 100644 --- a/crates/rpc/rpc-types-compat/src/transaction/mod.rs +++ b/crates/rpc/rpc-types-compat/src/transaction/mod.rs @@ -4,6 +4,7 @@ mod signature; pub use signature::*; use std::fmt; +use alloy_consensus::Transaction as _; use alloy_rpc_types::{ request::{TransactionInput, TransactionRequest}, Transaction, TransactionInfo, @@ -47,9 +48,7 @@ pub trait TransactionCompat: Send + Sync + Unpin + Clone + fmt::Debug { // baseFee` let gas_price = base_fee .and_then(|base_fee| { - signed_tx - .effective_tip_per_gas(Some(base_fee)) - .map(|tip| tip + base_fee as u128) + signed_tx.effective_tip_per_gas(base_fee).map(|tip| tip + base_fee as u128) }) .unwrap_or_else(|| signed_tx.max_fee_per_gas()); diff --git a/crates/rpc/rpc/Cargo.toml b/crates/rpc/rpc/Cargo.toml index 5399e50ce28a..fe150e36eed5 100644 --- a/crates/rpc/rpc/Cargo.toml +++ b/crates/rpc/rpc/Cargo.toml @@ -98,8 +98,3 @@ jsonrpsee = { workspace = true, features = ["client"] } [features] js-tracer = ["revm-inspectors/js-tracer", "reth-rpc-eth-types/js-tracer"] -optimism = [ - "reth-primitives/optimism", - "reth-provider/optimism", - "reth-revm/optimism", -] diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index acf215b3b2cc..66465ef474aa 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -16,6 +16,7 @@ use jsonrpsee::core::RpcResult; use reth_chainspec::EthereumHardforks; use reth_evm::{ execute::{BlockExecutorProvider, Executor}, + system_calls::SystemCaller, ConfigureEvmEnv, }; use reth_primitives::{Block, BlockId, BlockNumberOrTag, TransactionSignedEcRecovered}; @@ -26,7 +27,7 @@ use reth_provider::{ use reth_revm::database::StateProviderDatabase; use reth_rpc_api::DebugApiServer; use reth_rpc_eth_api::{ - helpers::{Call, EthApiSpec, EthTransactions, TraceExt}, + helpers::{Call, EthApiSpec, EthTransactions, LoadState, TraceExt}, EthApiTypes, FromEthApiError, }; use reth_rpc_eth_types::{EthApiError, StateCacheDb}; @@ -245,6 +246,7 @@ where // block the transaction is included in let state_at: BlockId = block.parent_hash.into(); let block_hash = block.hash(); + let parent_beacon_block_root = block.parent_beacon_block_root; let this = self.clone(); self.eth_api() @@ -255,6 +257,26 @@ where let tx = transaction.into_recovered(); let mut db = CacheDB::new(StateProviderDatabase::new(state)); + + // apply relevant system calls + let mut system_caller = SystemCaller::new( + Call::evm_config(this.eth_api()).clone(), + LoadState::provider(this.eth_api()).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 let index = this.eth_api().replay_transactions_until( &mut db, @@ -664,7 +686,7 @@ where let state = state_provider.witness(Default::default(), hashed_state).map_err(Into::into)?; - Ok(ExecutionWitness { state: state.into_iter().collect(), codes, keys: Some(keys) }) + Ok(ExecutionWitness { state: state.into_iter().collect(), codes, keys }) }) .await } diff --git a/crates/rpc/rpc/src/eth/core.rs b/crates/rpc/rpc/src/eth/core.rs index 304266f6a8b3..5c7fbbd00239 100644 --- a/crates/rpc/rpc/src/eth/core.rs +++ b/crates/rpc/rpc/src/eth/core.rs @@ -6,7 +6,6 @@ use std::sync::Arc; use alloy_network::AnyNetwork; use alloy_primitives::U256; use derive_more::Deref; -use reth_node_api::{BuilderProvider, FullNodeComponents}; use reth_primitives::BlockNumberOrTag; use reth_provider::{BlockReaderIdExt, CanonStateSubscriptions, ChainSpecProvider}; use reth_rpc_eth_api::{ @@ -19,7 +18,7 @@ use reth_rpc_eth_types::{ }; use reth_tasks::{ pool::{BlockingTaskGuard, BlockingTaskPool}, - TaskExecutor, TaskSpawner, TokioTaskExecutor, + TaskSpawner, TokioTaskExecutor, }; use tokio::sync::Mutex; @@ -95,7 +94,7 @@ where { /// Creates a new, shareable instance. pub fn with_spawner( - ctx: &EthApiBuilderCtx, + ctx: &EthApiBuilderCtx, ) -> Self where Tasks: TaskSpawner + Clone + 'static, @@ -163,25 +162,6 @@ where } } -impl BuilderProvider for EthApi -where - N: FullNodeComponents, -{ - type Ctx<'a> = &'a EthApiBuilderCtx< - N::Provider, - N::Pool, - N::Evm, - N::Network, - TaskExecutor, - N::Provider, - Self, - >; - - fn builder() -> Box Fn(Self::Ctx<'a>) -> Self + Send> { - Box::new(Self::with_spawner) - } -} - /// Container type `EthApi` #[allow(missing_debug_implementations)] pub struct EthApiInner { @@ -441,8 +421,8 @@ mod tests { let mut rng = generators::rng(); // Build mock data - let mut gas_used_ratios = Vec::new(); - let mut base_fees_per_gas = Vec::new(); + let mut gas_used_ratios = Vec::with_capacity(block_count as usize); + let mut base_fees_per_gas = Vec::with_capacity(block_count as usize); let mut last_header = None; let mut parent_hash = B256::default(); @@ -464,8 +444,9 @@ mod tests { last_header = Some(header.clone()); parent_hash = hash; - let mut transactions = vec![]; - for _ in 0..100 { + const TOTAL_TRANSACTIONS: usize = 100; + let mut transactions = Vec::with_capacity(TOTAL_TRANSACTIONS); + for _ in 0..TOTAL_TRANSACTIONS { let random_fee: u128 = rng.gen(); if let Some(base_fee_per_gas) = header.base_fee_per_gas { diff --git a/crates/rpc/rpc/src/eth/helpers/signer.rs b/crates/rpc/rpc/src/eth/helpers/signer.rs index a5818aa494fd..e59be0ac2838 100644 --- a/crates/rpc/rpc/src/eth/helpers/signer.rs +++ b/crates/rpc/rpc/src/eth/helpers/signer.rs @@ -40,7 +40,7 @@ impl DevSigner { /// Generates provided number of random dev signers /// which satisfy [`EthSigner`] trait pub fn random_signers(num: u32) -> Vec> { - let mut signers = Vec::new(); + let mut signers = Vec::with_capacity(num as usize); for _ in 0..num { let sk = PrivateKeySigner::random_with(&mut rand::thread_rng()); diff --git a/crates/rpc/rpc/src/eth/helpers/types.rs b/crates/rpc/rpc/src/eth/helpers/types.rs index d2b9c268e240..ab7b1a268e06 100644 --- a/crates/rpc/rpc/src/eth/helpers/types.rs +++ b/crates/rpc/rpc/src/eth/helpers/types.rs @@ -1,5 +1,6 @@ //! L1 `eth` API types. +use alloy_consensus::Transaction as _; use alloy_network::{AnyNetwork, Network}; use alloy_primitives::{Address, TxKind}; use alloy_rpc_types::{Transaction, TransactionInfo}; @@ -36,8 +37,9 @@ where let GasPrice { gas_price, max_fee_per_gas } = Self::gas_price(&signed_tx, base_fee.map(|fee| fee as u64)); + let input = signed_tx.input().to_vec().into(); let chain_id = signed_tx.chain_id(); - let blob_versioned_hashes = signed_tx.blob_versioned_hashes(); + let blob_versioned_hashes = signed_tx.blob_versioned_hashes().map(|hs| hs.to_vec()); let access_list = signed_tx.access_list().cloned(); let authorization_list = signed_tx.authorization_list().map(|l| l.to_vec()); @@ -59,7 +61,7 @@ where max_priority_fee_per_gas: signed_tx.max_priority_fee_per_gas(), signature: Some(signature), gas: signed_tx.gas_limit(), - input: signed_tx.input().clone(), + input, chain_id, access_list, transaction_type: Some(signed_tx.tx_type() as u8), diff --git a/crates/rpc/rpc/src/otterscan.rs b/crates/rpc/rpc/src/otterscan.rs index 31db343a104f..024bdd172fb9 100644 --- a/crates/rpc/rpc/src/otterscan.rs +++ b/crates/rpc/rpc/src/otterscan.rs @@ -261,7 +261,6 @@ where from: receipt.from(), to: receipt.to(), contract_address: receipt.contract_address(), - state_root: receipt.state_root(), authorization_list: receipt .authorization_list() .map(<[SignedAuthorization]>::to_vec), @@ -334,6 +333,7 @@ where .eth .trace_block_with( num.into(), + None, TracingInspectorConfig::default_parity(), |tx_info, inspector, _, _, _| { Ok(inspector.into_parity_builder().into_localized_transaction_traces(tx_info)) diff --git a/crates/rpc/rpc/src/trace.rs b/crates/rpc/rpc/src/trace.rs index 0cd94ef15b89..b9b15b5366df 100644 --- a/crates/rpc/rpc/src/trace.rs +++ b/crates/rpc/rpc/src/trace.rs @@ -1,5 +1,3 @@ -use std::sync::Arc; - use alloy_primitives::{map::HashSet, Bytes, B256, U256}; use alloy_rpc_types::{ state::{EvmOverrides, StateOverride}, @@ -37,6 +35,7 @@ use revm_inspectors::{ opcode::OpcodeGasInspector, tracing::{parity::populate_state_diff, TracingInspector, TracingInspectorConfig}, }; +use std::sync::Arc; use tokio::sync::{AcquireError, OwnedSemaphorePermit}; /// `trace` API implementation. @@ -278,14 +277,21 @@ where } // fetch all blocks in that range - let blocks = self.provider().block_range(start..=end).map_err(Eth::Error::from_eth_err)?; + let blocks = self + .provider() + .sealed_block_with_senders_range(start..=end) + .map_err(Eth::Error::from_eth_err)? + .into_iter() + .map(Arc::new) + .collect::>(); // trace all blocks let mut block_traces = Vec::with_capacity(blocks.len()); for block in &blocks { let matcher = matcher.clone(); let traces = self.eth_api().trace_block_until( - block.number.into(), + block.hash().into(), + Some(block.clone()), None, TracingInspectorConfig::default_parity(), move |tx_info, inspector, _, _, _| { @@ -308,13 +314,15 @@ where // add reward traces for all blocks for block in &blocks { if let Some(base_block_reward) = self.calculate_base_block_reward(&block.header)? { - let mut traces = self.extract_reward_traces( - &block.header, - &block.body.ommers, - base_block_reward, + all_traces.extend( + self.extract_reward_traces( + &block.header, + &block.body.ommers, + base_block_reward, + ) + .into_iter() + .filter(|trace| matcher.matches(&trace.trace)), ); - traces.retain(|trace| matcher.matches(&trace.trace)); - all_traces.extend(traces); } else { // no block reward, means we're past the Paris hardfork and don't expect any rewards // because the blocks in ascending order @@ -369,6 +377,7 @@ where ) -> Result>, Eth::Error> { let traces = self.eth_api().trace_block_with( block_id, + None, TracingInspectorConfig::default_parity(), |tx_info, inspector, _, _, _| { let traces = @@ -405,6 +414,7 @@ where self.eth_api() .trace_block_with( block_id, + None, TracingInspectorConfig::from_parity_config(&trace_types), move |tx_info, inspector, res, state, db| { let mut full_trace = @@ -460,6 +470,7 @@ where .eth_api() .trace_block_inspector( block_id, + None, OpcodeGasInspector::default, move |tx_info, inspector, _res, _, _| { let trace = TransactionOpcodeGas { diff --git a/crates/rpc/rpc/src/txpool.rs b/crates/rpc/rpc/src/txpool.rs index 5e26935ca1ba..47aaac0bbfd5 100644 --- a/crates/rpc/rpc/src/txpool.rs +++ b/crates/rpc/rpc/src/txpool.rs @@ -1,5 +1,6 @@ use std::{collections::BTreeMap, marker::PhantomData}; +use alloy_consensus::Transaction; use alloy_primitives::Address; use alloy_rpc_types_txpool::{ TxpoolContent, TxpoolContentFrom, TxpoolInspect, TxpoolInspectSummary, TxpoolStatus, diff --git a/crates/stages/api/Cargo.toml b/crates/stages/api/Cargo.toml index 352d3e024765..cba569a2a431 100644 --- a/crates/stages/api/Cargo.toml +++ b/crates/stages/api/Cargo.toml @@ -46,4 +46,9 @@ tokio-stream.workspace = true reth-testing-utils.workspace = true [features] -test-utils = [] +test-utils = [ + "reth-consensus/test-utils", + "reth-network-p2p/test-utils", + "reth-primitives-traits/test-utils", + "reth-provider/test-utils" +] diff --git a/crates/stages/api/src/pipeline/builder.rs b/crates/stages/api/src/pipeline/builder.rs index 68ca887fe790..79a4c477ee6c 100644 --- a/crates/stages/api/src/pipeline/builder.rs +++ b/crates/stages/api/src/pipeline/builder.rs @@ -34,7 +34,9 @@ impl PipelineBuilder { /// [`builder`][StageSet::builder] on the set which will convert it to a /// [`StageSetBuilder`][crate::StageSetBuilder]. pub fn add_stages>(mut self, set: Set) -> Self { - for stage in set.builder().build() { + let states = set.builder().build(); + self.stages.reserve_exact(states.len()); + for stage in states { self.stages.push(stage); } self diff --git a/crates/stages/api/src/pipeline/mod.rs b/crates/stages/api/src/pipeline/mod.rs index 1f6d9341ad6d..14225a595285 100644 --- a/crates/stages/api/src/pipeline/mod.rs +++ b/crates/stages/api/src/pipeline/mod.rs @@ -276,6 +276,10 @@ impl Pipeline { // Unwind stages in reverse order of execution let unwind_pipeline = self.stages.iter_mut().rev(); + // Legacy Engine: This prevents a race condition in which the `StaticFileProducer` could + // attempt to proceed with a finalized block which has been unwinded + let _locked_sf_producer = self.static_file_producer.lock(); + let mut provider_rw = self.provider_factory.database_provider_rw()?; for stage in unwind_pipeline { diff --git a/crates/stages/stages/Cargo.toml b/crates/stages/stages/Cargo.toml index 3d4227d8a274..0b26cb6a1e79 100644 --- a/crates/stages/stages/Cargo.toml +++ b/crates/stages/stages/Cargo.toml @@ -24,7 +24,9 @@ reth-evm.workspace = true reth-exex.workspace = true reth-network-p2p.workspace = true reth-primitives = { workspace = true, features = ["secp256k1"] } -reth-primitives-traits = { workspace = true, features = ["serde-bincode-compat"] } +reth-primitives-traits = { workspace = true, features = [ + "serde-bincode-compat", +] } reth-provider.workspace = true reth-execution-types.workspace = true reth-prune.workspace = true @@ -82,9 +84,6 @@ tempfile.workspace = true # Stage benchmarks criterion = { workspace = true, features = ["async_tokio"] } -# io -serde_json.workspace = true - [target.'cfg(not(target_os = "windows"))'.dev-dependencies] pprof = { workspace = true, features = [ "flamegraph", @@ -94,13 +93,24 @@ pprof = { workspace = true, features = [ [features] test-utils = [ - "dep:reth-chainspec", - "reth-network-p2p/test-utils", - "reth-db/test-utils", - "reth-provider/test-utils", - "reth-stages-api/test-utils", - "dep:reth-testing-utils", - "dep:tempfile", + "dep:reth-chainspec", + "reth-network-p2p/test-utils", + "reth-db/test-utils", + "reth-provider/test-utils", + "reth-stages-api/test-utils", + "dep:reth-testing-utils", + "dep:tempfile", + "reth-chainspec?/test-utils", + "reth-consensus/test-utils", + "reth-evm/test-utils", + "reth-downloaders/test-utils", + "reth-primitives/test-utils", + "reth-primitives-traits/test-utils", + "reth-revm/test-utils", + "reth-codecs/test-utils", + "reth-db-api/test-utils", + "reth-trie-db/test-utils", + "reth-trie/test-utils" ] [[bench]] diff --git a/crates/stages/stages/src/stages/bodies.rs b/crates/stages/stages/src/stages/bodies.rs index 2d441dee292a..06a5250913ed 100644 --- a/crates/stages/stages/src/stages/bodies.rs +++ b/crates/stages/stages/src/stages/bodies.rs @@ -121,7 +121,6 @@ where let mut tx_block_cursor = tx.cursor_write::()?; let mut ommers_cursor = tx.cursor_write::()?; let mut withdrawals_cursor = tx.cursor_write::()?; - let mut requests_cursor = tx.cursor_write::()?; // Get id for the next tx_num of zero if there are no transactions. let mut next_tx_num = tx_block_cursor.last()?.map(|(id, _)| id + 1).unwrap_or_default(); @@ -234,13 +233,6 @@ where .append(block_number, StoredBlockWithdrawals { withdrawals })?; } } - - // Write requests if any - if let Some(requests) = block.body.requests { - if !requests.0.is_empty() { - requests_cursor.append(block_number, requests)?; - } - } } BlockResponse::Empty(_) => {} }; @@ -276,7 +268,6 @@ where let mut body_cursor = tx.cursor_write::()?; let mut ommers_cursor = tx.cursor_write::()?; let mut withdrawals_cursor = tx.cursor_write::()?; - let mut requests_cursor = tx.cursor_write::()?; // Cursors to unwind transitions let mut tx_block_cursor = tx.cursor_write::()?; @@ -296,11 +287,6 @@ where withdrawals_cursor.delete_current()?; } - // Delete the requests entry if any - if requests_cursor.seek_exact(number)?.is_some() { - requests_cursor.delete_current()?; - } - // Delete all transaction to block values. if !block_meta.is_empty() && tx_block_cursor.seek_exact(block_meta.last_tx_num())?.is_some() @@ -931,7 +917,8 @@ mod tests { return Poll::Ready(None) } - let mut response = Vec::default(); + let mut response = + Vec::with_capacity(std::cmp::min(this.headers.len(), this.batch_size as usize)); while let Some(header) = this.headers.pop_front() { if header.is_empty() { response.push(BlockResponse::Empty(header)) diff --git a/crates/stages/stages/src/stages/execution.rs b/crates/stages/stages/src/stages/execution.rs index 7bb6ebc59e09..47cd9d0445a2 100644 --- a/crates/stages/stages/src/stages/execution.rs +++ b/crates/stages/stages/src/stages/execution.rs @@ -667,7 +667,8 @@ mod tests { use assert_matches::assert_matches; use reth_chainspec::ChainSpecBuilder; use reth_db_api::{models::AccountBeforeTx, transaction::DbTxMut}; - use reth_evm_ethereum::execute::EthExecutorProvider; + use reth_evm::execute::BasicBlockExecutorProvider; + use reth_evm_ethereum::execute::EthExecutionStrategyFactory; use reth_execution_errors::BlockValidationError; use reth_primitives::{Account, Bytecode, SealedBlock, StorageEntry}; use reth_provider::{ @@ -678,10 +679,11 @@ mod tests { use reth_stages_api::StageUnitCheckpoint; use std::collections::BTreeMap; - fn stage() -> ExecutionStage { - let executor_provider = EthExecutorProvider::ethereum(Arc::new( + fn stage() -> ExecutionStage> { + let strategy_factory = EthExecutionStrategyFactory::ethereum(Arc::new( ChainSpecBuilder::mainnet().berlin_activated().build(), )); + let executor_provider = BasicBlockExecutorProvider::new(strategy_factory); ExecutionStage::new( executor_provider, ExecutionStageThresholds { diff --git a/crates/stages/stages/src/stages/mod.rs b/crates/stages/stages/src/stages/mod.rs index 17ffcf2e90eb..4b9f9295103e 100644 --- a/crates/stages/stages/src/stages/mod.rs +++ b/crates/stages/stages/src/stages/mod.rs @@ -263,7 +263,7 @@ mod tests { ); db.insert_blocks(blocks.iter(), StorageKind::Static)?; - let mut receipts = Vec::new(); + let mut receipts = Vec::with_capacity(blocks.len()); let mut tx_num = 0u64; for block in &blocks { let mut block_receipts = Vec::with_capacity(block.body.transactions.len()); diff --git a/crates/stages/stages/src/stages/sender_recovery.rs b/crates/stages/stages/src/stages/sender_recovery.rs index a85b0bc60ccb..a4eda6394c07 100644 --- a/crates/stages/stages/src/stages/sender_recovery.rs +++ b/crates/stages/stages/src/stages/sender_recovery.rs @@ -29,6 +29,9 @@ const BATCH_SIZE: usize = 100_000; /// Maximum number of senders to recover per rayon worker job. const WORKER_CHUNK_SIZE: usize = 100; +/// Type alias for a sender that transmits the result of sender recovery. +type RecoveryResultSender = mpsc::Sender>>; + /// The sender recovery stage iterates over existing transactions, /// recovers the transaction signer and stores them /// in [`TransactionSenders`][reth_db::tables::TransactionSenders] table. @@ -100,8 +103,10 @@ where .map(|start| start..std::cmp::min(start + BATCH_SIZE as u64, tx_range.end)) .collect::>>(); + let tx_batch_sender = setup_range_recovery(provider); + for range in batch { - recover_range(range, provider, &mut senders_cursor)?; + recover_range(range, provider, tx_batch_sender.clone(), &mut senders_cursor)?; } Ok(ExecOutput { @@ -136,15 +141,16 @@ where fn recover_range( tx_range: Range, provider: &Provider, + tx_batch_sender: mpsc::Sender, RecoveryResultSender)>>, senders_cursor: &mut CURSOR, ) -> Result<(), StageError> where Provider: DBProvider + HeaderProvider + StaticFileProviderFactory, CURSOR: DbCursorRW, { - debug!(target: "sync::stages::sender_recovery", ?tx_range, "Recovering senders batch"); + debug!(target: "sync::stages::sender_recovery", ?tx_range, "Sending batch for processing"); - // Preallocate channels + // Preallocate channels for each chunks in the batch let (chunks, receivers): (Vec<_>, Vec<_>) = tx_range .clone() .step_by(WORKER_CHUNK_SIZE) @@ -156,62 +162,9 @@ where }) .unzip(); - let static_file_provider = provider.static_file_provider(); - - // We do not use `tokio::task::spawn_blocking` because, during a shutdown, - // there will be a timeout grace period in which Tokio does not allow spawning - // additional blocking tasks. This would cause this function to return - // `SenderRecoveryStageError::RecoveredSendersMismatch` at the end. - // - // However, using `std::thread::spawn` allows us to utilize the timeout grace - // period to complete some work without throwing errors during the shutdown. - std::thread::spawn(move || { - for (chunk_range, recovered_senders_tx) in chunks { - // Read the raw value, and let the rayon worker to decompress & decode. - let chunk = match static_file_provider.fetch_range_with_predicate( - StaticFileSegment::Transactions, - chunk_range.clone(), - |cursor, number| { - Ok(cursor - .get_one::>>( - number.into(), - )? - .map(|tx| (number, tx))) - }, - |_| true, - ) { - Ok(chunk) => chunk, - Err(err) => { - // We exit early since we could not process this chunk. - let _ = recovered_senders_tx - .send(Err(Box::new(SenderRecoveryStageError::StageError(err.into())))); - break - } - }; - - // Spawn the task onto the global rayon pool - // This task will send the results through the channel after it has read the transaction - // and calculated the sender. - rayon::spawn(move || { - let mut rlp_buf = Vec::with_capacity(128); - for (number, tx) in chunk { - let res = tx - .value() - .map_err(|err| Box::new(SenderRecoveryStageError::StageError(err.into()))) - .and_then(|tx| recover_sender((number, tx), &mut rlp_buf)); - - let is_err = res.is_err(); - - let _ = recovered_senders_tx.send(res); - - // Finish early - if is_err { - break - } - } - }); - } - }); + if let Some(err) = tx_batch_sender.send(chunks).err() { + return Err(StageError::Fatal(err.into())); + } debug!(target: "sync::stages::sender_recovery", ?tx_range, "Appending recovered senders to the database"); @@ -235,6 +188,7 @@ where provider.sealed_header(block_number)?.ok_or_else(|| { ProviderError::HeaderNotFound(block_number.into()) })?; + Err(StageError::Block { block: Box::new(sealed_header), error: BlockErrorKind::Validation( @@ -269,10 +223,82 @@ where .into(), )); } - Ok(()) } +/// Spawns a thread to handle the recovery of transaction senders for +/// specified chunks of a given batch. It processes incoming ranges, fetching and recovering +/// transactions in parallel using global rayon pool +fn setup_range_recovery( + provider: &Provider, +) -> mpsc::Sender, RecoveryResultSender)>> +where + Provider: DBProvider + HeaderProvider + StaticFileProviderFactory, +{ + let (tx_sender, tx_receiver) = mpsc::channel::, RecoveryResultSender)>>(); + let static_file_provider = provider.static_file_provider(); + + // We do not use `tokio::task::spawn_blocking` because, during a shutdown, + // there will be a timeout grace period in which Tokio does not allow spawning + // additional blocking tasks. This would cause this function to return + // `SenderRecoveryStageError::RecoveredSendersMismatch` at the end. + // + // However, using `std::thread::spawn` allows us to utilize the timeout grace + // period to complete some work without throwing errors during the shutdown. + std::thread::spawn(move || { + while let Ok(chunks) = tx_receiver.recv() { + for (chunk_range, recovered_senders_tx) in chunks { + // Read the raw value, and let the rayon worker to decompress & decode. + let chunk = match static_file_provider.fetch_range_with_predicate( + StaticFileSegment::Transactions, + chunk_range.clone(), + |cursor, number| { + Ok(cursor + .get_one::>>( + number.into(), + )? + .map(|tx| (number, tx))) + }, + |_| true, + ) { + Ok(chunk) => chunk, + Err(err) => { + // We exit early since we could not process this chunk. + let _ = recovered_senders_tx + .send(Err(Box::new(SenderRecoveryStageError::StageError(err.into())))); + break + } + }; + + // Spawn the task onto the global rayon pool + // This task will send the results through the channel after it has read the + // transaction and calculated the sender. + rayon::spawn(move || { + let mut rlp_buf = Vec::with_capacity(128); + for (number, tx) in chunk { + let res = tx + .value() + .map_err(|err| { + Box::new(SenderRecoveryStageError::StageError(err.into())) + }) + .and_then(|tx| recover_sender((number, tx), &mut rlp_buf)); + + let is_err = res.is_err(); + + let _ = recovered_senders_tx.send(res); + + // Finish early + if is_err { + break + } + } + }); + } + } + }); + tx_sender +} + #[inline] fn recover_sender( (tx_id, tx): (TxNumber, TransactionSignedNoHash), diff --git a/crates/static-file/static-file/Cargo.toml b/crates/static-file/static-file/Cargo.toml index 8fa89e12e0ff..d22b116cdc59 100644 --- a/crates/static-file/static-file/Cargo.toml +++ b/crates/static-file/static-file/Cargo.toml @@ -13,17 +13,14 @@ workspace = true [dependencies] # reth -reth-chainspec.workspace = true reth-db.workspace = true reth-db-api.workspace = true reth-provider.workspace = true reth-storage-errors.workspace = true -reth-nippy-jar.workspace = true reth-tokio-util.workspace = true reth-prune-types.workspace = true reth-static-file-types.workspace = true reth-stages-types.workspace = true -reth-node-types.workspace = true alloy-primitives.workspace = true diff --git a/crates/static-file/static-file/src/static_file_producer.rs b/crates/static-file/static-file/src/static_file_producer.rs index 32565fd6dfa6..2c442aedfa34 100644 --- a/crates/static-file/static-file/src/static_file_producer.rs +++ b/crates/static-file/static-file/src/static_file_producer.rs @@ -5,8 +5,8 @@ use alloy_primitives::BlockNumber; use parking_lot::Mutex; use rayon::prelude::*; use reth_provider::{ - providers::StaticFileWriter, BlockReader, DBProvider, DatabaseProviderFactory, - StageCheckpointReader, StaticFileProviderFactory, + providers::StaticFileWriter, BlockReader, ChainStateBlockReader, DBProvider, + DatabaseProviderFactory, StageCheckpointReader, StaticFileProviderFactory, }; use reth_prune_types::PruneModes; use reth_stages_types::StageId; @@ -106,6 +106,16 @@ impl StaticFileProducerInner { } } +impl StaticFileProducerInner +where + Provider: StaticFileProviderFactory + DatabaseProviderFactory, +{ + /// Returns the last finalized block number on disk. + pub fn last_finalized_block(&self) -> ProviderResult> { + self.provider.database_provider_ro()?.last_finalized_block_number() + } +} + impl StaticFileProducerInner where Provider: StaticFileProviderFactory diff --git a/crates/static-file/types/src/lib.rs b/crates/static-file/types/src/lib.rs index 380931138869..6e954a781b71 100644 --- a/crates/static-file/types/src/lib.rs +++ b/crates/static-file/types/src/lib.rs @@ -71,3 +71,81 @@ pub const fn find_fixed_range( let start = (block / blocks_per_static_file) * blocks_per_static_file; SegmentRangeInclusive::new(start, start + blocks_per_static_file - 1) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_highest_static_files_highest() { + let files = + HighestStaticFiles { headers: Some(100), receipts: Some(200), transactions: None }; + + // Test for headers segment + assert_eq!(files.highest(StaticFileSegment::Headers), Some(100)); + + // Test for receipts segment + assert_eq!(files.highest(StaticFileSegment::Receipts), Some(200)); + + // Test for transactions segment + assert_eq!(files.highest(StaticFileSegment::Transactions), None); + } + + #[test] + fn test_highest_static_files_as_mut() { + let mut files = HighestStaticFiles::default(); + + // Modify headers value + *files.as_mut(StaticFileSegment::Headers) = Some(150); + assert_eq!(files.headers, Some(150)); + + // Modify receipts value + *files.as_mut(StaticFileSegment::Receipts) = Some(250); + assert_eq!(files.receipts, Some(250)); + + // Modify transactions value + *files.as_mut(StaticFileSegment::Transactions) = Some(350); + assert_eq!(files.transactions, Some(350)); + } + + #[test] + fn test_highest_static_files_min() { + let files = + HighestStaticFiles { headers: Some(300), receipts: Some(100), transactions: None }; + + // Minimum value among the available segments + assert_eq!(files.min(), Some(100)); + + let empty_files = HighestStaticFiles::default(); + // No values, should return None + assert_eq!(empty_files.min(), None); + } + + #[test] + fn test_highest_static_files_max() { + let files = + HighestStaticFiles { headers: Some(300), receipts: Some(100), transactions: Some(500) }; + + // Maximum value among the available segments + assert_eq!(files.max(), Some(500)); + + let empty_files = HighestStaticFiles::default(); + // No values, should return None + assert_eq!(empty_files.max(), None); + } + + #[test] + fn test_find_fixed_range() { + // Test with default block size + let block: BlockNumber = 600_000; + let range = find_fixed_range(block, DEFAULT_BLOCKS_PER_STATIC_FILE); + assert_eq!(range.start(), 500_000); + assert_eq!(range.end(), 999_999); + + // Test with a custom block size + let block: BlockNumber = 1_200_000; + let range = find_fixed_range(block, 1_000_000); + assert_eq!(range.start(), 1_000_000); + assert_eq!(range.end(), 1_999_999); + } +} diff --git a/crates/storage/codecs/Cargo.toml b/crates/storage/codecs/Cargo.toml index 640ec8c9561f..2525b4e8d7f8 100644 --- a/crates/storage/codecs/Cargo.toml +++ b/crates/storage/codecs/Cargo.toml @@ -39,8 +39,6 @@ alloy-primitives = { workspace = true, features = [ "rand", ] } alloy-consensus = { workspace = true, features = ["arbitrary"] } -alloy-rlp.workspace = true -rand.workspace = true test-fuzz.workspace = true serde_json.workspace = true @@ -51,7 +49,15 @@ serde.workspace = true [features] default = ["std", "alloy"] -std = ["alloy-primitives/std", "bytes/std"] +std = [ + "alloy-primitives/std", + "bytes/std", + "alloy-consensus?/std", + "alloy-eips?/std", + "alloy-genesis?/std", + "alloy-trie?/std", + "serde/std" +] alloy = [ "dep:alloy-consensus", "dep:alloy-eips", diff --git a/crates/storage/codecs/src/alloy/authorization_list.rs b/crates/storage/codecs/src/alloy/authorization_list.rs index 3efe13590622..2b013c0d3c52 100644 --- a/crates/storage/codecs/src/alloy/authorization_list.rs +++ b/crates/storage/codecs/src/alloy/authorization_list.rs @@ -13,7 +13,7 @@ use reth_codecs_derive::add_arbitrary_tests; #[cfg_attr(test, derive(arbitrary::Arbitrary, serde::Serialize, serde::Deserialize))] #[add_arbitrary_tests(compact)] pub(crate) struct Authorization { - chain_id: U256, + chain_id: u64, address: Address, nonce: u64, } @@ -78,7 +78,7 @@ mod tests { #[test] fn test_roundtrip_compact_authorization_list_item() { let authorization = AlloyAuthorization { - chain_id: U256::from(1), + chain_id: 1u64, address: address!("dac17f958d2ee523a2206206994597c13d831ec7"), nonce: 1, } diff --git a/crates/storage/codecs/src/alloy/header.rs b/crates/storage/codecs/src/alloy/header.rs index 3a17ed1fdcd2..90e67b1e312c 100644 --- a/crates/storage/codecs/src/alloy/header.rs +++ b/crates/storage/codecs/src/alloy/header.rs @@ -45,7 +45,7 @@ pub(crate) struct Header { #[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Compact)] pub(crate) struct HeaderExt { - requests_root: Option, + requests_hash: Option, } impl HeaderExt { @@ -53,7 +53,7 @@ impl HeaderExt { /// /// Required since [`Header`] uses `Option` as a field. const fn into_option(self) -> Option { - if self.requests_root.is_some() { + if self.requests_hash.is_some() { Some(self) } else { None @@ -66,7 +66,7 @@ impl Compact for AlloyHeader { where B: bytes::BufMut + AsMut<[u8]>, { - let extra_fields = HeaderExt { requests_root: self.requests_root }; + let extra_fields = HeaderExt { requests_hash: self.requests_hash }; let header = Header { parent_hash: self.parent_hash, @@ -116,7 +116,7 @@ impl Compact for AlloyHeader { blob_gas_used: header.blob_gas_used, excess_blob_gas: header.excess_blob_gas, parent_beacon_block_root: header.parent_beacon_block_root, - requests_root: header.extra_fields.and_then(|h| h.requests_root), + requests_hash: header.extra_fields.and_then(|h| h.requests_hash), extra_data: header.extra_data, }; (alloy_header, buf) @@ -126,12 +126,13 @@ impl Compact for AlloyHeader { #[cfg(test)] mod tests { use super::*; + use alloy_consensus::EMPTY_OMMER_ROOT_HASH; use alloy_primitives::{address, b256, bloom, bytes, hex}; /// Holesky block #1947953 const HOLESKY_BLOCK: Header = Header { parent_hash: b256!("8605e0c46689f66b3deed82598e43d5002b71a929023b665228728f0c6e62a95"), - ommers_hash: b256!("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"), + ommers_hash: EMPTY_OMMER_ROOT_HASH, beneficiary: address!("c6e2459991bfe27cca6d86722f35da23a1e4cb97"), state_root: b256!("edad188ca5647d62f4cca417c11a1afbadebce30d23260767f6f587e9b3b9993"), transactions_root: b256!("4daf25dc08a841aa22aa0d3cb3e1f159d4dcaf6a6063d4d36bfac11d3fdb63ee"), @@ -175,7 +176,7 @@ mod tests { #[test] fn test_extra_fields() { let mut header = HOLESKY_BLOCK; - header.extra_fields = Some(HeaderExt { requests_root: Some(B256::random()) }); + header.extra_fields = Some(HeaderExt { requests_hash: Some(B256::random()) }); let mut encoded_header = vec![]; let len = header.to_compact(&mut encoded_header); diff --git a/crates/storage/codecs/src/alloy/mod.rs b/crates/storage/codecs/src/alloy/mod.rs index 942258d0647e..ed77876c5ce1 100644 --- a/crates/storage/codecs/src/alloy/mod.rs +++ b/crates/storage/codecs/src/alloy/mod.rs @@ -3,7 +3,6 @@ mod authorization_list; mod genesis_account; mod header; mod log; -mod request; mod signature; mod transaction; mod trie; @@ -14,7 +13,6 @@ mod withdrawal; mod tests { use crate::{ alloy::{ - authorization_list::Authorization, genesis_account::{GenesisAccount, GenesisAccountRef, StorageEntries, StorageEntry}, header::{Header, HeaderExt}, transaction::{ @@ -38,7 +36,6 @@ mod tests { validate_bitflag_backwards_compat!(StorageEntries, UnusedBits::Zero); validate_bitflag_backwards_compat!(StorageEntry, UnusedBits::Zero); - validate_bitflag_backwards_compat!(Authorization, UnusedBits::NotZero); validate_bitflag_backwards_compat!(GenesisAccountRef<'_>, UnusedBits::NotZero); validate_bitflag_backwards_compat!(GenesisAccount, UnusedBits::NotZero); validate_bitflag_backwards_compat!(TxEip1559, UnusedBits::NotZero); diff --git a/crates/storage/codecs/src/alloy/request.rs b/crates/storage/codecs/src/alloy/request.rs deleted file mode 100644 index 2447160beb6d..000000000000 --- a/crates/storage/codecs/src/alloy/request.rs +++ /dev/null @@ -1,40 +0,0 @@ -//! Native Compact codec impl for EIP-7685 requests. - -use crate::Compact; -use alloy_consensus::Request; -use alloy_eips::eip7685::{Decodable7685, Encodable7685}; -use alloy_primitives::Bytes; -use bytes::BufMut; - -impl Compact for Request { - fn to_compact(&self, buf: &mut B) -> usize - where - B: BufMut + AsMut<[u8]>, - { - let encoded: Bytes = self.encoded_7685().into(); - encoded.to_compact(buf) - } - - fn from_compact(buf: &[u8], _: usize) -> (Self, &[u8]) { - let (raw, buf) = Bytes::from_compact(buf, buf.len()); - - (Self::decode_7685(&mut raw.as_ref()).expect("invalid eip-7685 request in db"), buf) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use proptest::proptest; - use proptest_arbitrary_interop::arb; - - proptest! { - #[test] - fn roundtrip(request in arb::()) { - let mut buf = Vec::::new(); - request.to_compact(&mut buf); - let (decoded, _) = Request::from_compact(&buf, buf.len()); - assert_eq!(request, decoded); - } - } -} diff --git a/crates/storage/codecs/src/alloy/transaction/mod.rs b/crates/storage/codecs/src/alloy/transaction/mod.rs index 86edfee5bcca..5b1d173a528e 100644 --- a/crates/storage/codecs/src/alloy/transaction/mod.rs +++ b/crates/storage/codecs/src/alloy/transaction/mod.rs @@ -15,8 +15,6 @@ mod tests { // this check is to ensure we do not inadvertently add too many fields to a struct which would // expand the flags field and break backwards compatibility - #[cfg(feature = "optimism")] - use crate::alloy::transaction::optimism::TxDeposit; use crate::alloy::transaction::{ eip1559::TxEip1559, eip2930::TxEip2930, eip4844::TxEip4844, eip7702::TxEip7702, legacy::TxLegacy, @@ -34,6 +32,6 @@ mod tests { #[cfg(feature = "optimism")] #[test] fn test_ensure_backwards_compatibility_optimism() { - assert_eq!(TxDeposit::bitflag_encoded_bytes(), 2); + assert_eq!(crate::alloy::transaction::optimism::TxDeposit::bitflag_encoded_bytes(), 2); } } diff --git a/crates/storage/codecs/src/lib.rs b/crates/storage/codecs/src/lib.rs index 8608c5eb8c19..c432400a5766 100644 --- a/crates/storage/codecs/src/lib.rs +++ b/crates/storage/codecs/src/lib.rs @@ -48,6 +48,12 @@ pub mod test_utils; /// Regarding the `specialized_to/from_compact` methods: Mainly used as a workaround for not being /// able to specialize an impl over certain types like `Vec`/`Option` where `T` is a fixed /// size array like `Vec`. +/// +/// ## Caution +/// +/// Due to the bitfields, every type change on the rust type (e.g. `U256` to `u64`) is a breaking +/// change and will lead to a new, incompatible [`Compact`] implementation. Implementers must take +/// special care when changing or rearranging fields. pub trait Compact: Sized { /// Takes a buffer which can be written to. *Ideally*, it returns the length written to. fn to_compact(&self, buf: &mut B) -> usize @@ -78,6 +84,21 @@ pub trait Compact: Sized { } } +impl Compact for alloc::string::String { + fn to_compact(&self, buf: &mut B) -> usize + where + B: bytes::BufMut + AsMut<[u8]>, + { + self.as_bytes().to_compact(buf) + } + + fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) { + let (vec, buf) = Vec::::from_compact(buf, len); + let string = Self::from_utf8(vec).unwrap(); // Safe conversion + (string, buf) + } +} + impl Compact for &T { fn to_compact(&self, buf: &mut B) -> usize where @@ -484,7 +505,7 @@ mod tests { #[test] fn compact_address() { - let mut buf = vec![]; + let mut buf = Vec::with_capacity(21); assert_eq!(Address::ZERO.to_compact(&mut buf), 20); assert_eq!(buf, vec![0; 20]); diff --git a/crates/storage/db-api/Cargo.toml b/crates/storage/db-api/Cargo.toml index d674f9d7b685..932a94b98ebc 100644 --- a/crates/storage/db-api/Cargo.toml +++ b/crates/storage/db-api/Cargo.toml @@ -56,11 +56,25 @@ proptest.workspace = true proptest-arbitrary-interop.workspace = true [features] -test-utils = ["arbitrary"] +test-utils = [ + "arbitrary", + "reth-primitives/test-utils", + "reth-primitives-traits/test-utils", + "reth-codecs/test-utils", + "reth-db-models/test-utils", + "reth-trie-common/test-utils" +] arbitrary = [ - "reth-primitives/arbitrary", - "reth-db-models/arbitrary", - "dep:arbitrary", - "dep:proptest", + "reth-primitives/arbitrary", + "reth-db-models/arbitrary", + "dep:arbitrary", + "dep:proptest", + "reth-primitives-traits/arbitrary", + "reth-trie-common/arbitrary", + "alloy-primitives/arbitrary", + "parity-scale-codec/arbitrary" +] +optimism = [ + "reth-primitives/optimism", + "reth-codecs/optimism" ] -optimism = ["reth-primitives/optimism"] diff --git a/crates/storage/db-api/src/models/mod.rs b/crates/storage/db-api/src/models/mod.rs index 0f35a558a352..b077027f2970 100644 --- a/crates/storage/db-api/src/models/mod.rs +++ b/crates/storage/db-api/src/models/mod.rs @@ -8,7 +8,7 @@ use alloy_genesis::GenesisAccount; use alloy_primitives::{Address, Bytes, Log, B256, U256}; use reth_codecs::{add_arbitrary_tests, Compact}; use reth_primitives::{ - Account, Bytecode, Header, Receipt, Requests, StorageEntry, TransactionSignedNoHash, TxType, + Account, Bytecode, Header, Receipt, StorageEntry, TransactionSignedNoHash, TxType, }; use reth_prune_types::{PruneCheckpoint, PruneSegment}; use reth_stages_types::StageCheckpoint; @@ -230,7 +230,6 @@ impl_compression_for_compact!( StageCheckpoint, PruneCheckpoint, ClientVersion, - Requests, // Non-DB GenesisAccount ); @@ -366,6 +365,5 @@ mod tests { validate_bitflag_backwards_compat!(StoredBlockWithdrawals, UnusedBits::Zero); validate_bitflag_backwards_compat!(StorageHashingCheckpoint, UnusedBits::NotZero); validate_bitflag_backwards_compat!(Withdrawals, UnusedBits::Zero); - validate_bitflag_backwards_compat!(Requests, UnusedBits::Zero); } } diff --git a/crates/storage/db-common/Cargo.toml b/crates/storage/db-common/Cargo.toml index 7fc487969861..9e4954357f84 100644 --- a/crates/storage/db-common/Cargo.toml +++ b/crates/storage/db-common/Cargo.toml @@ -42,6 +42,7 @@ tracing.workspace = true [dev-dependencies] reth-primitives-traits.workspace = true reth-provider = { workspace = true, features = ["test-utils"] } +alloy-consensus.workspace = true [lints] workspace = true diff --git a/crates/storage/db-common/src/init.rs b/crates/storage/db-common/src/init.rs index 3962dfd69809..f0695421ec5c 100644 --- a/crates/storage/db-common/src/init.rs +++ b/crates/storage/db-common/src/init.rs @@ -581,6 +581,7 @@ struct GenesisAccountWithAddress { #[cfg(test)] mod tests { use super::*; + use alloy_consensus::constants::MAINNET_GENESIS_HASH; use alloy_genesis::Genesis; use reth_chainspec::{Chain, ChainSpec, HOLESKY, MAINNET, SEPOLIA}; use reth_db::DatabaseEnv; @@ -591,7 +592,7 @@ mod tests { transaction::DbTx, Database, }; - use reth_primitives::{HOLESKY_GENESIS_HASH, MAINNET_GENESIS_HASH, SEPOLIA_GENESIS_HASH}; + use reth_primitives::{HOLESKY_GENESIS_HASH, SEPOLIA_GENESIS_HASH}; use reth_primitives_traits::IntegerList; use reth_provider::{ test_utils::{create_test_provider_factory_with_chain_spec, MockNodeTypesWithDB}, diff --git a/crates/storage/db-models/Cargo.toml b/crates/storage/db-models/Cargo.toml index 9bcd54f3860d..31741207cad2 100644 --- a/crates/storage/db-models/Cargo.toml +++ b/crates/storage/db-models/Cargo.toml @@ -35,15 +35,18 @@ proptest = { workspace = true, optional = true } reth-primitives = { workspace = true, features = ["arbitrary"] } reth-codecs.workspace = true -arbitrary = { workspace = true, features = ["derive"] } proptest-arbitrary-interop.workspace = true -proptest.workspace = true test-fuzz.workspace = true [features] -test-utils = ["arbitrary"] +test-utils = [ + "arbitrary", + "reth-primitives/test-utils", + "reth-codecs/test-utils" +] arbitrary = [ - "reth-primitives/arbitrary", - "dep:arbitrary", - "dep:proptest", + "reth-primitives/arbitrary", + "dep:arbitrary", + "dep:proptest", + "alloy-primitives/arbitrary" ] diff --git a/crates/storage/db-models/src/client_version.rs b/crates/storage/db-models/src/client_version.rs index de074ac88c6e..a28e7385f658 100644 --- a/crates/storage/db-models/src/client_version.rs +++ b/crates/storage/db-models/src/client_version.rs @@ -28,20 +28,16 @@ impl Compact for ClientVersion { where B: bytes::BufMut + AsMut<[u8]>, { - self.version.as_bytes().to_compact(buf); - self.git_sha.as_bytes().to_compact(buf); - self.build_timestamp.as_bytes().to_compact(buf) + self.version.to_compact(buf); + self.git_sha.to_compact(buf); + self.build_timestamp.to_compact(buf) } fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) { - let (version, buf) = Vec::::from_compact(buf, len); - let (git_sha, buf) = Vec::::from_compact(buf, len); - let (build_timestamp, buf) = Vec::::from_compact(buf, len); - let client_version = Self { - version: unsafe { String::from_utf8_unchecked(version) }, - git_sha: unsafe { String::from_utf8_unchecked(git_sha) }, - build_timestamp: unsafe { String::from_utf8_unchecked(build_timestamp) }, - }; + let (version, buf) = String::from_compact(buf, len); + let (git_sha, buf) = String::from_compact(buf, len); + let (build_timestamp, buf) = String::from_compact(buf, len); + let client_version = Self { version, git_sha, build_timestamp }; (client_version, buf) } } diff --git a/crates/storage/db/Cargo.toml b/crates/storage/db/Cargo.toml index a075f7724637..2f437e631095 100644 --- a/crates/storage/db/Cargo.toml +++ b/crates/storage/db/Cargo.toml @@ -58,7 +58,6 @@ strum = { workspace = true, features = ["derive"], optional = true } [dev-dependencies] # reth libs with arbitrary reth-primitives = { workspace = true, features = ["arbitrary"] } -rand.workspace = true serde_json.workspace = true tempfile.workspace = true test-fuzz.workspace = true @@ -90,10 +89,28 @@ mdbx = [ "dep:strum", "dep:rustc-hash", ] -test-utils = ["dep:tempfile", "arbitrary", "parking_lot"] +test-utils = [ + "dep:tempfile", + "arbitrary", + "parking_lot", + "reth-primitives/test-utils", + "reth-primitives-traits/test-utils", + "reth-db-api/test-utils", + "reth-nippy-jar/test-utils", + "reth-trie-common/test-utils" +] bench = [] -arbitrary = ["reth-primitives/arbitrary", "reth-db-api/arbitrary"] -optimism = [] +arbitrary = [ + "reth-primitives/arbitrary", + "reth-db-api/arbitrary", + "reth-primitives-traits/arbitrary", + "reth-trie-common/arbitrary", + "alloy-primitives/arbitrary" +] +optimism = [ + "reth-primitives/optimism", + "reth-db-api/optimism" +] disable-lock = [] [[bench]] diff --git a/crates/storage/db/src/tables/mod.rs b/crates/storage/db/src/tables/mod.rs index 83a063903e08..27f58f8a1f3d 100644 --- a/crates/storage/db/src/tables/mod.rs +++ b/crates/storage/db/src/tables/mod.rs @@ -30,9 +30,7 @@ use reth_db_api::{ }, table::{Decode, DupSort, Encode, Table}, }; -use reth_primitives::{ - Account, Bytecode, Header, Receipt, Requests, StorageEntry, TransactionSignedNoHash, -}; +use reth_primitives::{Account, Bytecode, Header, Receipt, StorageEntry, TransactionSignedNoHash}; use reth_primitives_traits::IntegerList; use reth_prune_types::{PruneCheckpoint, PruneSegment}; use reth_stages_types::StageCheckpoint; @@ -404,9 +402,6 @@ tables! { /// Stores the history of client versions that have accessed the database with write privileges by unix timestamp in seconds. table VersionHistory; - /// Stores EIP-7685 EL -> CL requests, indexed by block number. - table BlockRequests; - /// Stores generic chain state info, like the last finalized block. table ChainState; } diff --git a/crates/storage/errors/Cargo.toml b/crates/storage/errors/Cargo.toml index 52c93ae4ef06..ecefa5f6aca7 100644 --- a/crates/storage/errors/Cargo.toml +++ b/crates/storage/errors/Cargo.toml @@ -25,4 +25,8 @@ derive_more.workspace = true [features] default = ["std"] -std = [] +std = [ + "reth-primitives/std", + "alloy-eips/std", + "alloy-primitives/std" +] diff --git a/crates/storage/nippy-jar/src/compression/mod.rs b/crates/storage/nippy-jar/src/compression/mod.rs index 28a92fe909f2..f9bf8110eebb 100644 --- a/crates/storage/nippy-jar/src/compression/mod.rs +++ b/crates/storage/nippy-jar/src/compression/mod.rs @@ -44,7 +44,9 @@ pub trait Compression: Serialize + for<'a> Deserialize<'a> { #[derive(Debug, Serialize, Deserialize)] #[cfg_attr(test, derive(PartialEq))] pub enum Compressors { + /// Zstandard compression algorithm with custom settings. Zstd(Zstd), + /// LZ4 compression algorithm with custom settings. Lz4(Lz4), } diff --git a/crates/storage/nippy-jar/src/compression/zstd.rs b/crates/storage/nippy-jar/src/compression/zstd.rs index 500247d17677..896a65bd7080 100644 --- a/crates/storage/nippy-jar/src/compression/zstd.rs +++ b/crates/storage/nippy-jar/src/compression/zstd.rs @@ -12,10 +12,13 @@ pub use zstd::{bulk::Decompressor, dict::DecoderDictionary}; type RawDictionary = Vec; +/// Represents the state of a Zstandard compression operation. #[derive(Debug, Default, PartialEq, Eq, Serialize, Deserialize)] pub enum ZstdState { + /// The compressor is pending a dictionary. #[default] PendingDictionary, + /// The compressor is ready to perform compression. Ready, } @@ -51,6 +54,7 @@ impl Zstd { } } + /// Sets the compression level for the Zstd compression instance. pub const fn with_level(mut self, level: i32) -> Self { self.level = level; self @@ -209,7 +213,7 @@ impl Compression for Zstd { return Err(NippyJarError::ColumnLenMismatch(self.columns, columns.len())) } - let mut dictionaries = vec![]; + let mut dictionaries = Vec::with_capacity(columns.len()); for column in columns { // ZSTD requires all training data to be continuous in memory, alongside the size of // each entry diff --git a/crates/storage/nippy-jar/src/consistency.rs b/crates/storage/nippy-jar/src/consistency.rs index 1093fb5546ac..952980ef6eff 100644 --- a/crates/storage/nippy-jar/src/consistency.rs +++ b/crates/storage/nippy-jar/src/consistency.rs @@ -28,6 +28,11 @@ pub struct NippyJarChecker { } impl NippyJarChecker { + /// Creates a new instance of [`NippyJarChecker`] with the provided [`NippyJar`]. + /// + /// This method initializes the checker without any associated file handles for + /// the data or offsets files. The [`NippyJar`] passed in contains all necessary + /// configurations for handling data. pub const fn new(jar: NippyJar) -> Self { Self { jar, data_file: None, offsets_file: None } } diff --git a/crates/storage/nippy-jar/src/cursor.rs b/crates/storage/nippy-jar/src/cursor.rs index 267764827299..376411ac2656 100644 --- a/crates/storage/nippy-jar/src/cursor.rs +++ b/crates/storage/nippy-jar/src/cursor.rs @@ -25,9 +25,10 @@ impl std::fmt::Debug for NippyJarCursor<'_, H> { } impl<'a, H: NippyJarHeader> NippyJarCursor<'a, H> { + /// Creates a new instance of [`NippyJarCursor`] for the given [`NippyJar`]. pub fn new(jar: &'a NippyJar) -> Result { let max_row_size = jar.max_row_size; - Ok(NippyJarCursor { + Ok(Self { jar, reader: Arc::new(jar.open_data_reader()?), // Makes sure that we have enough buffer capacity to decompress any row of data. @@ -36,12 +37,14 @@ impl<'a, H: NippyJarHeader> NippyJarCursor<'a, H> { }) } + /// Creates a new instance of [`NippyJarCursor`] with the specified [`NippyJar`] and data + /// reader. pub fn with_reader( jar: &'a NippyJar, reader: Arc, ) -> Result { let max_row_size = jar.max_row_size; - Ok(NippyJarCursor { + Ok(Self { jar, reader, // Makes sure that we have enough buffer capacity to decompress any row of data. diff --git a/crates/storage/nippy-jar/src/error.rs b/crates/storage/nippy-jar/src/error.rs index fc096cf848c6..f69bb44a068b 100644 --- a/crates/storage/nippy-jar/src/error.rs +++ b/crates/storage/nippy-jar/src/error.rs @@ -4,53 +4,92 @@ use thiserror::Error; /// Errors associated with [`crate::NippyJar`]. #[derive(Error, Debug)] pub enum NippyJarError { + /// An internal error occurred, wrapping any type of error. #[error(transparent)] Internal(#[from] Box), + + /// An error occurred while disconnecting, wrapping a standard I/O error. #[error(transparent)] Disconnect(#[from] std::io::Error), + + /// An error related to the file system occurred, wrapping a file system path error. #[error(transparent)] FileSystem(#[from] reth_fs_util::FsPathError), + + /// A custom error message provided by the user. #[error("{0}")] Custom(String), + + /// An error occurred during serialization/deserialization with Bincode. #[error(transparent)] Bincode(#[from] Box), + + /// An error occurred with the Elias-Fano encoding/decoding process. #[error(transparent)] EliasFano(#[from] anyhow::Error), + + /// Compression was enabled, but the compressor is not ready yet. #[error("compression was enabled, but it's not ready yet")] CompressorNotReady, + + /// Decompression was enabled, but the decompressor is not ready yet. #[error("decompression was enabled, but it's not ready yet")] DecompressorNotReady, + + /// The number of columns does not match the expected length. #[error("number of columns does not match: {0} != {1}")] ColumnLenMismatch(usize, usize), + + /// An unexpected missing value was encountered at a specific row and column. #[error("unexpected missing value: row:col {0}:{1}")] UnexpectedMissingValue(u64, u64), + + /// The size of an offset exceeds the maximum allowed size of 8 bytes. #[error("the size of an offset must be at most 8 bytes, got {offset_size}")] OffsetSizeTooBig { /// The read offset size in number of bytes. offset_size: u8, }, + + /// The size of an offset is less than the minimum allowed size of 1 byte. #[error("the size of an offset must be at least 1 byte, got {offset_size}")] OffsetSizeTooSmall { /// The read offset size in number of bytes. offset_size: u8, }, + + /// An attempt was made to read an offset that is out of bounds. #[error("attempted to read an out of bounds offset: {index}")] OffsetOutOfBounds { /// The index of the offset that was being read. index: usize, }, + + /// The output buffer is too small for the compression or decompression operation. #[error("compression or decompression requires a bigger destination output")] OutputTooSmall, + + /// A dictionary is not loaded when it is required for operations. #[error("dictionary is not loaded.")] DictionaryNotLoaded, + + /// It's not possible to generate a compressor after loading a dictionary. #[error("it's not possible to generate a compressor after loading a dictionary.")] CompressorNotAllowed, + + /// The number of offsets is smaller than the requested prune size. #[error("number of offsets ({0}) is smaller than prune request ({1}).")] InvalidPruning(u64, u64), + + /// The jar has been frozen and cannot be modified. #[error("jar has been frozen and cannot be modified.")] FrozenJar, + + /// The file is in an inconsistent state. #[error("File is in an inconsistent state.")] InconsistentState, + + /// A specified file is missing. #[error("Missing file: {0}.")] MissingFile(PathBuf), } diff --git a/crates/storage/nippy-jar/src/lib.rs b/crates/storage/nippy-jar/src/lib.rs index bdc950aa38a7..b1d174feb2c3 100644 --- a/crates/storage/nippy-jar/src/lib.rs +++ b/crates/storage/nippy-jar/src/lib.rs @@ -10,7 +10,6 @@ issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] #![cfg_attr(not(test), warn(unused_crate_dependencies))] -#![allow(missing_docs)] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] use memmap2::Mmap; @@ -28,6 +27,7 @@ use std::os::windows::prelude::OpenOptionsExt; use tracing::*; +/// Compression algorithms supported by `NippyJar`. pub mod compression; #[cfg(test)] use compression::Compression; @@ -55,10 +55,13 @@ pub use writer::NippyJarWriter; mod consistency; pub use consistency::NippyJarChecker; +/// The version number of the Nippy Jar format. const NIPPY_JAR_VERSION: usize = 1; - +/// The file extension used for index files. const INDEX_FILE_EXTENSION: &str = "idx"; +/// The file extension used for offsets files. const OFFSETS_FILE_EXTENSION: &str = "off"; +/// The file extension used for configuration files. pub const CONFIG_FILE_EXTENSION: &str = "conf"; /// A [`RefRow`] is a list of column value slices pointing to either an internal buffer or a diff --git a/crates/storage/nippy-jar/src/writer.rs b/crates/storage/nippy-jar/src/writer.rs index 9bf9bf526448..3a1003bee764 100644 --- a/crates/storage/nippy-jar/src/writer.rs +++ b/crates/storage/nippy-jar/src/writer.rs @@ -354,6 +354,10 @@ impl NippyJarWriter { Ok(()) } + /// Commits changes to the data file and offsets without synchronizing all data to disk. + /// + /// This function flushes the buffered data to the data file and commits the offsets, + /// but it does not guarantee that all data is synchronized to persistent storage. #[cfg(feature = "test-utils")] pub fn commit_without_sync_all(&mut self) -> Result<(), NippyJarError> { self.data_file.flush()?; @@ -412,41 +416,49 @@ impl NippyJarWriter { Ok(()) } + /// Returns the maximum row size for the associated [`NippyJar`]. #[cfg(test)] pub const fn max_row_size(&self) -> usize { self.jar.max_row_size } + /// Returns the column index of the current checker instance. #[cfg(test)] pub const fn column(&self) -> usize { self.column } + /// Returns a reference to the offsets vector. #[cfg(test)] pub fn offsets(&self) -> &[u64] { &self.offsets } + /// Returns a mutable reference to the offsets vector. #[cfg(test)] pub fn offsets_mut(&mut self) -> &mut Vec { &mut self.offsets } + /// Returns the path to the offsets file for the associated [`NippyJar`]. #[cfg(test)] pub fn offsets_path(&self) -> std::path::PathBuf { self.jar.offsets_path() } + /// Returns the path to the data file for the associated [`NippyJar`]. #[cfg(test)] pub fn data_path(&self) -> &Path { self.jar.data_path() } + /// Returns a mutable reference to the buffered writer for the data file. #[cfg(any(test, feature = "test-utils"))] pub fn data_file(&mut self) -> &mut BufWriter { &mut self.data_file } + /// Returns a reference to the associated [`NippyJar`] instance. #[cfg(any(test, feature = "test-utils"))] pub const fn jar(&self) -> &NippyJar { &self.jar diff --git a/crates/storage/provider/Cargo.toml b/crates/storage/provider/Cargo.toml index 7ef827a37042..b93c22cdf671 100644 --- a/crates/storage/provider/Cargo.toml +++ b/crates/storage/provider/Cargo.toml @@ -73,8 +73,10 @@ rayon.workspace = true [dev-dependencies] reth-db = { workspace = true, features = ["test-utils"] } reth-primitives = { workspace = true, features = ["arbitrary", "test-utils"] } +reth-chain-state = { workspace = true, features = ["test-utils"] } reth-trie = { workspace = true, features = ["test-utils"] } reth-testing-utils.workspace = true +reth-ethereum-engine-primitives.workspace = true parking_lot.workspace = true tempfile.workspace = true @@ -86,16 +88,41 @@ alloy-consensus.workspace = true [features] optimism = [ - "reth-primitives/optimism", - "reth-execution-types/optimism", - "reth-optimism-primitives", + "reth-primitives/optimism", + "reth-execution-types/optimism", + "reth-optimism-primitives", + "reth-codecs/optimism", + "reth-db/optimism", + "reth-db-api/optimism", + "revm/optimism" +] +serde = [ + "reth-execution-types/serde", + "reth-trie-db/serde", + "reth-trie/serde", + "alloy-consensus?/serde", + "alloy-eips/serde", + "alloy-primitives/serde", + "alloy-rpc-types-engine/serde", + "dashmap/serde", + "notify/serde", + "parking_lot/serde", + "rand/serde", + "revm/serde" ] -serde = ["reth-execution-types/serde"] test-utils = [ - "reth-db/test-utils", - "reth-nippy-jar/test-utils", - "reth-trie/test-utils", - "reth-chain-state/test-utils", - "reth-ethereum-engine-primitives", - "alloy-consensus", + "reth-db/test-utils", + "reth-nippy-jar/test-utils", + "reth-trie/test-utils", + "reth-chain-state/test-utils", + "reth-ethereum-engine-primitives", + "alloy-consensus", + "reth-chainspec/test-utils", + "reth-evm/test-utils", + "reth-network-p2p/test-utils", + "reth-primitives/test-utils", + "reth-codecs/test-utils", + "reth-db-api/test-utils", + "reth-trie-db/test-utils", + "revm/test-utils" ] diff --git a/crates/storage/provider/src/providers/blockchain_provider.rs b/crates/storage/provider/src/providers/blockchain_provider.rs index 9b88cab136f5..1866610e3f28 100644 --- a/crates/storage/provider/src/providers/blockchain_provider.rs +++ b/crates/storage/provider/src/providers/blockchain_provider.rs @@ -4,7 +4,7 @@ use crate::{ CanonStateSubscriptions, ChainSpecProvider, ChainStateBlockReader, ChangeSetReader, DatabaseProviderFactory, DatabaseProviderRO, EvmEnvProvider, HeaderProvider, ProviderError, ProviderFactory, PruneCheckpointReader, ReceiptProvider, ReceiptProviderIdExt, - RequestsProvider, StageCheckpointReader, StateProviderBox, StateProviderFactory, StateReader, + StageCheckpointReader, StateProviderBox, StateProviderFactory, StateReader, StaticFileProviderFactory, TransactionVariant, TransactionsProvider, WithdrawalsProvider, }; use alloy_eips::{BlockHashOrNumber, BlockId, BlockNumHash, BlockNumberOrTag, HashOrNumber}; @@ -402,7 +402,7 @@ impl BlockchainProvider2 { ) -> ProviderResult { let anchor_hash = state.anchor().hash; let latest_historical = self.database.history_by_block_hash(anchor_hash)?; - Ok(self.canonical_in_memory_state.state_provider_from_state(state, latest_historical)) + Ok(state.state_provider(latest_historical)) } /// Fetches data from either in-memory state or persistent storage for a range of transactions. @@ -1200,24 +1200,6 @@ impl WithdrawalsProvider for BlockchainProvider2 { } } -impl RequestsProvider for BlockchainProvider2 { - fn requests_by_block( - &self, - id: BlockHashOrNumber, - timestamp: u64, - ) -> ProviderResult> { - if !self.chain_spec().is_prague_active_at_timestamp(timestamp) { - return Ok(None) - } - - 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()), - ) - } -} - impl StageCheckpointReader for BlockchainProvider2 { fn get_stage_checkpoint(&self, id: StageId) -> ProviderResult> { self.database.provider()?.get_stage_checkpoint(id) @@ -1747,8 +1729,8 @@ mod tests { use reth_storage_api::{ BlockHashReader, BlockIdReader, BlockNumReader, BlockReader, BlockReaderIdExt, BlockSource, ChangeSetReader, DatabaseProviderFactory, HeaderProvider, ReceiptProvider, - ReceiptProviderIdExt, RequestsProvider, StateProviderFactory, TransactionVariant, - TransactionsProvider, WithdrawalsProvider, + ReceiptProviderIdExt, StateProviderFactory, TransactionVariant, TransactionsProvider, + WithdrawalsProvider, }; use reth_testing_utils::generators::{ self, random_block, random_block_range, random_changeset_range, random_eoa_accounts, @@ -1933,7 +1915,7 @@ mod tests { /// This simulates a RPC method having a different view than when its database transaction was /// created. fn persist_block_after_db_tx_creation( - provider: Arc>, + provider: BlockchainProvider2, block_number: BlockNumber, ) { let hook_provider = provider.clone(); @@ -2849,37 +2831,6 @@ mod tests { Ok(()) } - #[test] - fn test_requests_provider() -> eyre::Result<()> { - let mut rng = generators::rng(); - let chain_spec = Arc::new(ChainSpecBuilder::mainnet().prague_activated().build()); - let (provider, database_blocks, in_memory_blocks, _) = - provider_with_chain_spec_and_random_blocks( - &mut rng, - chain_spec.clone(), - TEST_BLOCKS_COUNT, - TEST_BLOCKS_COUNT, - BlockRangeParams { requests_count: Some(1..2), ..Default::default() }, - )?; - - let database_block = database_blocks.first().unwrap().clone(); - let in_memory_block = in_memory_blocks.last().unwrap().clone(); - - let prague_timestamp = - chain_spec.hardforks.fork(EthereumHardfork::Prague).as_timestamp().unwrap(); - - assert_eq!( - provider.requests_by_block(database_block.number.into(), prague_timestamp,)?, - database_block.body.requests.clone() - ); - assert_eq!( - provider.requests_by_block(in_memory_block.number.into(), prague_timestamp,)?, - in_memory_block.body.requests.clone() - ); - - Ok(()) - } - #[test] fn test_state_provider_factory() -> eyre::Result<()> { let mut rng = generators::rng(); @@ -3142,7 +3093,6 @@ mod tests { ..Default::default() }, )?; - let provider = Arc::new(provider); $( // Since data moves for each tried method, need to recalculate everything @@ -3257,7 +3207,6 @@ mod tests { ..Default::default() }, )?; - let provider = Arc::new(provider); $( // Since data moves for each tried method, need to recalculate everything @@ -3383,7 +3332,6 @@ mod tests { ..Default::default() }, )?; - let provider = Arc::new(provider); let mut in_memory_blocks: std::collections::VecDeque<_> = in_memory_blocks.into(); @@ -3685,8 +3633,6 @@ mod tests { }, )?; - let provider = Arc::new(provider); - // Old implementation was querying the database first. This is problematic, if there are // changes AFTER the database transaction is created. let old_transaction_hash_fn = @@ -3739,7 +3685,7 @@ mod tests { correct_transaction_hash_fn( to_be_persisted_tx.hash(), provider.canonical_in_memory_state(), - provider.database.clone() + provider.database ), Ok(Some(to_be_persisted_tx)) ); diff --git a/crates/storage/provider/src/providers/bundle_state_provider.rs b/crates/storage/provider/src/providers/bundle_state_provider.rs index 296dae8c6ab7..be6549033cde 100644 --- a/crates/storage/provider/src/providers/bundle_state_provider.rs +++ b/crates/storage/provider/src/providers/bundle_state_provider.rs @@ -43,7 +43,7 @@ impl BundleStateProvider account.storage.iter().map(|(slot, value)| (slot, &value.present_value)), ) }) - .unwrap_or_else(|| HashedStorage::new(false)) + .unwrap_or_default() } } diff --git a/crates/storage/provider/src/providers/database/metrics.rs b/crates/storage/provider/src/providers/database/metrics.rs index ba43298c36b5..7e9ee7202c01 100644 --- a/crates/storage/provider/src/providers/database/metrics.rs +++ b/crates/storage/provider/src/providers/database/metrics.rs @@ -61,7 +61,6 @@ pub(crate) enum Action { InsertTransactions, InsertTransactionHashNumbers, InsertBlockWithdrawals, - InsertBlockRequests, InsertBlockBodyIndices, InsertTransactionBlocks, GetNextTxNum, @@ -106,8 +105,6 @@ struct DatabaseProviderMetrics { insert_tx_hash_numbers: Histogram, /// Duration of insert block withdrawals insert_block_withdrawals: Histogram, - /// Duration of insert block requests - insert_block_requests: Histogram, /// Duration of insert block body indices insert_block_body_indices: Histogram, /// Duration of insert transaction blocks @@ -139,7 +136,6 @@ impl DatabaseProviderMetrics { Action::InsertTransactions => self.insert_transactions.record(duration), Action::InsertTransactionHashNumbers => self.insert_tx_hash_numbers.record(duration), Action::InsertBlockWithdrawals => self.insert_block_withdrawals.record(duration), - Action::InsertBlockRequests => self.insert_block_requests.record(duration), Action::InsertBlockBodyIndices => self.insert_block_body_indices.record(duration), Action::InsertTransactionBlocks => self.insert_tx_blocks.record(duration), Action::GetNextTxNum => self.get_next_tx_num.record(duration), diff --git a/crates/storage/provider/src/providers/database/mod.rs b/crates/storage/provider/src/providers/database/mod.rs index 520b514527b2..54186dca6f69 100644 --- a/crates/storage/provider/src/providers/database/mod.rs +++ b/crates/storage/provider/src/providers/database/mod.rs @@ -4,8 +4,8 @@ use crate::{ traits::{BlockSource, ReceiptProvider}, BlockHashReader, BlockNumReader, BlockReader, ChainSpecProvider, DatabaseProviderFactory, EvmEnvProvider, HeaderProvider, HeaderSyncGap, HeaderSyncGapProvider, ProviderError, - PruneCheckpointReader, RequestsProvider, StageCheckpointReader, StateProviderBox, - StaticFileProviderFactory, TransactionVariant, TransactionsProvider, WithdrawalsProvider, + PruneCheckpointReader, StageCheckpointReader, StateProviderBox, StaticFileProviderFactory, + TransactionVariant, TransactionsProvider, WithdrawalsProvider, }; use alloy_eips::BlockHashOrNumber; use alloy_primitives::{Address, BlockHash, BlockNumber, TxHash, TxNumber, B256, U256}; @@ -519,16 +519,6 @@ impl WithdrawalsProvider for ProviderFactory { } } -impl RequestsProvider for ProviderFactory { - fn requests_by_block( - &self, - id: BlockHashOrNumber, - timestamp: u64, - ) -> ProviderResult> { - self.provider()?.requests_by_block(id, timestamp) - } -} - impl StageCheckpointReader for ProviderFactory { fn get_stage_checkpoint(&self, id: StageId) -> ProviderResult> { self.provider()?.get_stage_checkpoint(id) diff --git a/crates/storage/provider/src/providers/database/provider.rs b/crates/storage/provider/src/providers/database/provider.rs index 3b074a5afffa..308fa364a3d3 100644 --- a/crates/storage/provider/src/providers/database/provider.rs +++ b/crates/storage/provider/src/providers/database/provider.rs @@ -9,12 +9,12 @@ use crate::{ AccountReader, BlockExecutionReader, BlockExecutionWriter, BlockHashReader, BlockNumReader, BlockReader, BlockWriter, BundleStateInit, ChainStateBlockReader, ChainStateBlockWriter, DBProvider, EvmEnvProvider, HashingWriter, HeaderProvider, HeaderSyncGap, - HeaderSyncGapProvider, HistoricalStateProvider, HistoryWriter, LatestStateProvider, - OriginalValuesKnown, ProviderError, PruneCheckpointReader, PruneCheckpointWriter, - RequestsProvider, RevertsInit, StageCheckpointReader, StateChangeWriter, StateProviderBox, - StateReader, StateWriter, StaticFileProviderFactory, StatsReader, StorageReader, - StorageTrieWriter, TransactionVariant, TransactionsProvider, TransactionsProviderExt, - TrieWriter, WithdrawalsProvider, + HeaderSyncGapProvider, HistoricalStateProvider, HistoricalStateProviderRef, HistoryWriter, + LatestStateProvider, LatestStateProviderRef, OriginalValuesKnown, ProviderError, + PruneCheckpointReader, PruneCheckpointWriter, RevertsInit, StageCheckpointReader, + StateChangeWriter, StateProviderBox, StateReader, StateWriter, StaticFileProviderFactory, + StatsReader, StorageReader, StorageTrieWriter, TransactionVariant, TransactionsProvider, + TransactionsProviderExt, TrieWriter, WithdrawalsProvider, }; use alloy_eips::BlockHashOrNumber; use alloy_primitives::{keccak256, Address, BlockHash, BlockNumber, TxHash, TxNumber, B256, U256}; @@ -40,14 +40,14 @@ use reth_evm::ConfigureEvmEnv; use reth_execution_types::{Chain, ExecutionOutcome}; use reth_network_p2p::headers::downloader::SyncTarget; use reth_primitives::{ - Account, Block, BlockBody, BlockWithSenders, Bytecode, GotExpected, Header, Receipt, Requests, + Account, Block, BlockBody, BlockWithSenders, Bytecode, GotExpected, Header, Receipt, SealedBlock, SealedBlockWithSenders, SealedHeader, StaticFileSegment, StorageEntry, TransactionMeta, TransactionSigned, TransactionSignedEcRecovered, TransactionSignedNoHash, Withdrawal, Withdrawals, }; use reth_prune_types::{PruneCheckpoint, PruneModes, PruneSegment}; use reth_stages_types::{StageCheckpoint, StageId}; -use reth_storage_api::{StorageChangeSetReader, TryIntoHistoricalStateProvider}; +use reth_storage_api::{StateProvider, StorageChangeSetReader, TryIntoHistoricalStateProvider}; use reth_storage_errors::provider::{ProviderResult, RootMismatch}; use reth_trie::{ prefix_set::{PrefixSet, PrefixSetMut, TriePrefixSets}, @@ -68,7 +68,7 @@ use std::{ time::{Duration, Instant}, }; use tokio::sync::watch; -use tracing::{debug, error, warn}; +use tracing::{debug, error, trace, warn}; /// A [`DatabaseProvider`] that holds a read-only database transaction. pub type DatabaseProviderRO = DatabaseProvider<::TX, Spec>; @@ -145,6 +145,64 @@ impl DatabaseProvider { } } +impl DatabaseProvider { + /// State provider for latest block + pub fn latest<'a>(&'a self) -> ProviderResult> { + trace!(target: "providers::db", "Returning latest state provider"); + Ok(Box::new(LatestStateProviderRef::new(&self.tx, self.static_file_provider.clone()))) + } + + /// Storage provider for state at that given block hash + pub fn history_by_block_hash<'a>( + &'a self, + block_hash: BlockHash, + ) -> ProviderResult> { + let mut block_number = + self.block_number(block_hash)?.ok_or(ProviderError::BlockHashNotFound(block_hash))?; + if block_number == self.best_block_number().unwrap_or_default() && + block_number == self.last_block_number().unwrap_or_default() + { + return Ok(Box::new(LatestStateProviderRef::new( + &self.tx, + self.static_file_provider.clone(), + ))) + } + + // +1 as the changeset that we want is the one that was applied after this block. + block_number += 1; + + let account_history_prune_checkpoint = + self.get_prune_checkpoint(PruneSegment::AccountHistory)?; + let storage_history_prune_checkpoint = + self.get_prune_checkpoint(PruneSegment::StorageHistory)?; + + let mut state_provider = HistoricalStateProviderRef::new( + &self.tx, + block_number, + self.static_file_provider.clone(), + ); + + // If we pruned account or storage history, we can't return state on every historical block. + // Instead, we should cap it at the latest prune checkpoint for corresponding prune segment. + if let Some(prune_checkpoint_block_number) = + account_history_prune_checkpoint.and_then(|checkpoint| checkpoint.block_number) + { + state_provider = state_provider.with_lowest_available_account_history_block_number( + prune_checkpoint_block_number + 1, + ); + } + if let Some(prune_checkpoint_block_number) = + storage_history_prune_checkpoint.and_then(|checkpoint| checkpoint.block_number) + { + state_provider = state_provider.with_lowest_available_storage_history_block_number( + prune_checkpoint_block_number + 1, + ); + } + + Ok(Box::new(state_provider)) + } +} + impl StaticFileProviderFactory for DatabaseProvider { /// Returns a static file provider fn static_file_provider(&self) -> StaticFileProvider { @@ -442,7 +500,6 @@ impl DatabaseProvider { Vec
, Vec
, Option, - Option, ) -> ProviderResult>, { let Some(block_number) = self.convert_hash_or_number(id)? else { return Ok(None) }; @@ -451,7 +508,6 @@ impl DatabaseProvider { let ommers = self.ommers(block_number.into())?.unwrap_or_default(); let withdrawals = self.withdrawals_by_block(block_number.into(), header.as_ref().timestamp)?; - let requests = self.requests_by_block(block_number.into(), header.as_ref().timestamp)?; // Get the block body // @@ -482,7 +538,7 @@ impl DatabaseProvider { }) .collect(); - construct_block(header, body, senders, ommers, withdrawals, requests) + construct_block(header, body, senders, ommers, withdrawals) } /// Returns a range of blocks from the database. @@ -493,7 +549,6 @@ impl DatabaseProvider { /// - Range of transaction numbers /// – Ommers /// – Withdrawals - /// – Requests /// – Senders fn block_range( &self, @@ -505,13 +560,7 @@ impl DatabaseProvider { Spec: EthereumHardforks, H: AsRef
, HF: FnOnce(RangeInclusive) -> ProviderResult>, - F: FnMut( - H, - Range, - Vec
, - Option, - Option, - ) -> ProviderResult, + F: FnMut(H, Range, Vec
, Option) -> ProviderResult, { if range.is_empty() { return Ok(Vec::new()) @@ -523,7 +572,6 @@ impl DatabaseProvider { let headers = headers_range(range)?; let mut ommers_cursor = self.tx.cursor_read::()?; let mut withdrawals_cursor = self.tx.cursor_read::()?; - let mut requests_cursor = self.tx.cursor_read::()?; let mut block_body_cursor = self.tx.cursor_read::()?; for header in headers { @@ -550,13 +598,6 @@ impl DatabaseProvider { } else { None }; - let requests = - if self.chain_spec.is_prague_active_at_timestamp(header_ref.timestamp) { - (requests_cursor.seek_exact(header_ref.number)?.unwrap_or_default().1) - .into() - } else { - None - }; let ommers = if self.chain_spec.final_paris_total_difficulty(header_ref.number).is_some() { Vec::new() @@ -567,7 +608,7 @@ impl DatabaseProvider { .unwrap_or_default() }; - if let Ok(b) = assemble_block(header, tx_range, ommers, withdrawals, requests) { + if let Ok(b) = assemble_block(header, tx_range, ommers, withdrawals) { blocks.push(b); } } @@ -585,7 +626,6 @@ impl DatabaseProvider { /// - Transactions /// – Ommers /// – Withdrawals - /// – Requests /// – Senders fn block_with_senders_range( &self, @@ -602,14 +642,13 @@ impl DatabaseProvider { Vec, Vec
, Option, - Option, Vec
, ) -> ProviderResult, { let mut tx_cursor = self.tx.cursor_read::()?; let mut senders_cursor = self.tx.cursor_read::()?; - self.block_range(range, headers_range, |header, tx_range, ommers, withdrawals, requests| { + self.block_range(range, headers_range, |header, tx_range, ommers, withdrawals| { let (body, senders) = if tx_range.is_empty() { (Vec::new(), Vec::new()) } else { @@ -641,7 +680,7 @@ impl DatabaseProvider { (body, senders) }; - assemble_block(header, body, ommers, withdrawals, requests, senders) + assemble_block(header, body, ommers, withdrawals, senders) }) } @@ -723,7 +762,6 @@ impl DatabaseProvider { // - Bodies (transactions) // - Uncles/ommers // - Withdrawals - // - Requests // - Signers let block_headers = self.get::(range.clone())?; @@ -734,7 +772,6 @@ impl DatabaseProvider { let block_header_hashes = self.get::(range.clone())?; let block_ommers = self.get::(range.clone())?; let block_withdrawals = self.get::(range.clone())?; - let block_requests = self.get::(range.clone())?; let block_tx = self.get_block_transaction_range(range)?; let mut blocks = Vec::with_capacity(block_headers.len()); @@ -747,10 +784,8 @@ impl DatabaseProvider { // Ommers can be empty for some blocks let mut block_ommers_iter = block_ommers.into_iter(); let mut block_withdrawals_iter = block_withdrawals.into_iter(); - let mut block_requests_iter = block_requests.into_iter(); let mut block_ommers = block_ommers_iter.next(); let mut block_withdrawals = block_withdrawals_iter.next(); - let mut block_requests = block_requests_iter.next(); for ((main_block_number, header), (_, header_hash), (_, tx)) in izip!(block_header_iter, block_header_hashes_iter, block_tx_iter) @@ -783,24 +818,10 @@ impl DatabaseProvider { withdrawals = None } - // requests can be missing - let prague_is_active = self.chain_spec.is_prague_active_at_timestamp(header.timestamp); - let mut requests = Some(Requests::default()); - if prague_is_active { - if let Some((block_number, _)) = block_requests.as_ref() { - if *block_number == main_block_number { - requests = Some(block_requests.take().unwrap().1); - block_requests = block_requests_iter.next(); - } - } - } else { - requests = None; - } - blocks.push(SealedBlockWithSenders { block: SealedBlock { header, - body: BlockBody { transactions, ommers, withdrawals, requests }, + body: BlockBody { transactions, ommers, withdrawals }, }, senders, }) @@ -1164,7 +1185,6 @@ impl DatabaseProvider { /// * [`CanonicalHeaders`](tables::CanonicalHeaders) /// * [`BlockOmmers`](tables::BlockOmmers) /// * [`BlockWithdrawals`](tables::BlockWithdrawals) - /// * [`BlockRequests`](tables::BlockRequests) /// * [`HeaderTerminalDifficulties`](tables::HeaderTerminalDifficulties) /// /// This will also remove transaction data according to @@ -1184,7 +1204,6 @@ impl DatabaseProvider { self.remove::(range.clone())?; self.remove::(range.clone())?; self.remove::(range.clone())?; - self.remove::(range.clone())?; self.remove_block_transaction_range(range.clone())?; self.remove::(range)?; @@ -1198,7 +1217,6 @@ impl DatabaseProvider { /// * [`CanonicalHeaders`](tables::CanonicalHeaders) /// * [`BlockOmmers`](tables::BlockOmmers) /// * [`BlockWithdrawals`](tables::BlockWithdrawals) - /// * [`BlockRequests`](tables::BlockRequests) /// * [`HeaderTerminalDifficulties`](tables::HeaderTerminalDifficulties) /// /// This will also remove transaction data according to @@ -1216,7 +1234,6 @@ impl DatabaseProvider { // - Bodies (transactions) // - Uncles/ommers // - Withdrawals - // - Requests // - Signers let block_headers = self.take::(range.clone())?; @@ -1230,7 +1247,6 @@ impl DatabaseProvider { let block_header_hashes = self.take::(range.clone())?; let block_ommers = self.take::(range.clone())?; let block_withdrawals = self.take::(range.clone())?; - let block_requests = self.take::(range.clone())?; let block_tx = self.take_block_transaction_range(range.clone())?; let mut blocks = Vec::with_capacity(block_headers.len()); @@ -1246,10 +1262,8 @@ impl DatabaseProvider { // Ommers can be empty for some blocks let mut block_ommers_iter = block_ommers.into_iter(); let mut block_withdrawals_iter = block_withdrawals.into_iter(); - let mut block_requests_iter = block_requests.into_iter(); let mut block_ommers = block_ommers_iter.next(); let mut block_withdrawals = block_withdrawals_iter.next(); - let mut block_requests = block_requests_iter.next(); for ((main_block_number, header), (_, header_hash), (_, tx)) in izip!(block_header_iter, block_header_hashes_iter, block_tx_iter) @@ -1282,24 +1296,10 @@ impl DatabaseProvider { withdrawals = None } - // requests can be missing - let prague_is_active = self.chain_spec.is_prague_active_at_timestamp(header.timestamp); - let mut requests = Some(Requests::default()); - if prague_is_active { - if let Some((block_number, _)) = block_requests.as_ref() { - if *block_number == main_block_number { - requests = Some(block_requests.take().unwrap().1); - block_requests = block_requests_iter.next(); - } - } - } else { - requests = None; - } - blocks.push(SealedBlockWithSenders { block: SealedBlock { header, - body: BlockBody { transactions, ommers, withdrawals, requests }, + body: BlockBody { transactions, ommers, withdrawals }, }, senders, }) @@ -1668,7 +1668,6 @@ impl BlockReader for DatabasePr if let Some(header) = self.header_by_number(number)? { let withdrawals = self.withdrawals_by_block(number.into(), header.timestamp)?; let ommers = self.ommers(number.into())?.unwrap_or_default(); - let requests = self.requests_by_block(number.into(), header.timestamp)?; // If the body indices are not found, this means that the transactions either do not // exist in the database yet, or they do exit but are not indexed. // If they exist but are not indexed, we don't have enough @@ -1680,7 +1679,7 @@ impl BlockReader for DatabasePr return Ok(Some(Block { header, - body: BlockBody { transactions, ommers, withdrawals, requests }, + body: BlockBody { transactions, ommers, withdrawals }, })) } } @@ -1740,8 +1739,8 @@ impl BlockReader for DatabasePr id, transaction_kind, |block_number| self.header_by_number(block_number), - |header, transactions, senders, ommers, withdrawals, requests| { - Block { header, body: BlockBody { transactions, ommers, withdrawals, requests } } + |header, transactions, senders, ommers, withdrawals| { + Block { header, body: BlockBody { transactions, ommers, withdrawals } } // Note: we're using unchecked here because we know the block contains valid txs // wrt to its height and can ignore the s value check so pre // EIP-2 txs are allowed @@ -1761,17 +1760,14 @@ impl BlockReader for DatabasePr id, transaction_kind, |block_number| self.sealed_header(block_number), - |header, transactions, senders, ommers, withdrawals, requests| { - SealedBlock { - header, - body: BlockBody { transactions, ommers, withdrawals, requests }, - } - // Note: we're using unchecked here because we know the block contains valid txs - // wrt to its height and can ignore the s value check so pre - // EIP-2 txs are allowed - .try_with_senders_unchecked(senders) - .map(Some) - .map_err(|_| ProviderError::SenderRecoveryError) + |header, transactions, senders, ommers, withdrawals| { + SealedBlock { header, body: BlockBody { transactions, ommers, withdrawals } } + // Note: we're using unchecked here because we know the block contains valid txs + // wrt to its height and can ignore the s value check so pre + // EIP-2 txs are allowed + .try_with_senders_unchecked(senders) + .map(Some) + .map_err(|_| ProviderError::SenderRecoveryError) }, ) } @@ -1781,7 +1777,7 @@ impl BlockReader for DatabasePr self.block_range( range, |range| self.headers_range(range), - |header, tx_range, ommers, withdrawals, requests| { + |header, tx_range, ommers, withdrawals| { let transactions = if tx_range.is_empty() { Vec::new() } else { @@ -1790,10 +1786,7 @@ impl BlockReader for DatabasePr .map(Into::into) .collect() }; - Ok(Block { - header, - body: BlockBody { transactions, ommers, withdrawals, requests }, - }) + Ok(Block { header, body: BlockBody { transactions, ommers, withdrawals } }) }, ) } @@ -1805,8 +1798,8 @@ impl BlockReader for DatabasePr self.block_with_senders_range( range, |range| self.headers_range(range), - |header, transactions, ommers, withdrawals, requests, senders| { - Block { header, body: BlockBody { transactions, ommers, withdrawals, requests } } + |header, transactions, ommers, withdrawals, senders| { + Block { header, body: BlockBody { transactions, ommers, withdrawals } } .try_with_senders_unchecked(senders) .map_err(|_| ProviderError::SenderRecoveryError) }, @@ -1820,12 +1813,9 @@ impl BlockReader for DatabasePr self.block_with_senders_range( range, |range| self.sealed_headers_range(range), - |header, transactions, ommers, withdrawals, requests, senders| { + |header, transactions, ommers, withdrawals, senders| { SealedBlockWithSenders::new( - SealedBlock { - header, - body: BlockBody { transactions, ommers, withdrawals, requests }, - }, + SealedBlock { header, body: BlockBody { transactions, ommers, withdrawals } }, senders, ) .ok_or(ProviderError::SenderRecoveryError) @@ -2142,24 +2132,6 @@ impl WithdrawalsProvider } } -impl RequestsProvider - for DatabaseProvider -{ - fn requests_by_block( - &self, - id: BlockHashOrNumber, - timestamp: u64, - ) -> ProviderResult> { - if self.chain_spec.is_prague_active_at_timestamp(timestamp) { - if let Some(number) = self.convert_hash_or_number(id)? { - let requests = self.tx.get::(number)?; - return Ok(requests) - } - } - Ok(None) - } -} - impl EvmEnvProvider for DatabaseProvider { @@ -3355,7 +3327,6 @@ impl(block_number, requests)?; - durations_recorder.record_relative(metrics::Action::InsertBlockRequests); - } - let block_indices = StoredBlockBodyIndices { first_tx_num, tx_count }; self.tx.put::(block_number, block_indices.clone())?; durations_recorder.record_relative(metrics::Action::InsertBlockBodyIndices); diff --git a/crates/storage/provider/src/providers/mod.rs b/crates/storage/provider/src/providers/mod.rs index 561e1d974362..b98bbf5be472 100644 --- a/crates/storage/provider/src/providers/mod.rs +++ b/crates/storage/provider/src/providers/mod.rs @@ -3,7 +3,7 @@ use crate::{ BlockSource, BlockchainTreePendingStateProvider, CanonChainTracker, CanonStateNotifications, CanonStateSubscriptions, ChainSpecProvider, ChainStateBlockReader, ChangeSetReader, DatabaseProviderFactory, EvmEnvProvider, FullExecutionDataProvider, HeaderProvider, - ProviderError, PruneCheckpointReader, ReceiptProvider, ReceiptProviderIdExt, RequestsProvider, + ProviderError, PruneCheckpointReader, ReceiptProvider, ReceiptProviderIdExt, StageCheckpointReader, StateProviderBox, StateProviderFactory, StaticFileProviderFactory, TransactionVariant, TransactionsProvider, TreeViewer, WithdrawalsProvider, }; @@ -504,16 +504,6 @@ impl WithdrawalsProvider for BlockchainProvider { } } -impl RequestsProvider for BlockchainProvider { - fn requests_by_block( - &self, - id: BlockHashOrNumber, - timestamp: u64, - ) -> ProviderResult> { - self.database.requests_by_block(id, timestamp) - } -} - impl StageCheckpointReader for BlockchainProvider { fn get_stage_checkpoint(&self, id: StageId) -> ProviderResult> { self.database.provider()?.get_stage_checkpoint(id) diff --git a/crates/storage/provider/src/providers/state/historical.rs b/crates/storage/provider/src/providers/state/historical.rs index 781a11f6deca..640041e0801f 100644 --- a/crates/storage/provider/src/providers/state/historical.rs +++ b/crates/storage/provider/src/providers/state/historical.rs @@ -227,6 +227,24 @@ impl<'b, TX: DbTx> HistoricalStateProviderRef<'b, TX> { Ok(HistoryInfo::NotYetWritten) } } + + /// Set the lowest block number at which the account history is available. + pub const fn with_lowest_available_account_history_block_number( + mut self, + block_number: BlockNumber, + ) -> Self { + self.lowest_available_blocks.account_history_block_number = Some(block_number); + self + } + + /// Set the lowest block number at which the storage history is available. + pub const fn with_lowest_available_storage_history_block_number( + mut self, + block_number: BlockNumber, + ) -> Self { + self.lowest_available_blocks.storage_history_block_number = Some(block_number); + self + } } impl AccountReader for HistoricalStateProviderRef<'_, TX> { diff --git a/crates/storage/provider/src/providers/static_file/manager.rs b/crates/storage/provider/src/providers/static_file/manager.rs index e233332a0e95..e81dc01f722c 100644 --- a/crates/storage/provider/src/providers/static_file/manager.rs +++ b/crates/storage/provider/src/providers/static_file/manager.rs @@ -4,8 +4,8 @@ use super::{ }; use crate::{ to_range, BlockHashReader, BlockNumReader, BlockReader, BlockSource, HeaderProvider, - ReceiptProvider, RequestsProvider, StageCheckpointReader, StatsReader, TransactionVariant, - TransactionsProvider, TransactionsProviderExt, WithdrawalsProvider, + ReceiptProvider, StageCheckpointReader, StatsReader, TransactionVariant, TransactionsProvider, + TransactionsProviderExt, WithdrawalsProvider, }; use alloy_eips::BlockHashOrNumber; use alloy_primitives::{keccak256, Address, BlockHash, BlockNumber, TxHash, TxNumber, B256, U256}; @@ -143,6 +143,7 @@ impl StaticFileProvider { // appending/truncating rows for segment in event.paths { // Ensure it's a file with the .conf extension + #[allow(clippy::nonminimal_bool)] if !segment .extension() .is_some_and(|s| s.to_str() == Some(CONFIG_FILE_EXTENSION)) @@ -1363,13 +1364,13 @@ impl TransactionsProviderExt for StaticFileProvider { // chunks are too big, there will be idle threads waiting for work. Choosing an // arbitrary smaller value to make sure it doesn't happen. let chunk_size = 100; - let mut channels = Vec::new(); // iterator over the chunks let chunks = tx_range .clone() .step_by(chunk_size) .map(|start| start..std::cmp::min(start + chunk_size as u64, tx_range.end)); + let mut channels = Vec::with_capacity(tx_range_size.div_ceil(chunk_size)); for chunk_range in chunks { let (channel_tx, channel_rx) = mpsc::channel(); @@ -1641,17 +1642,6 @@ impl WithdrawalsProvider for StaticFileProvider { } } -impl RequestsProvider for StaticFileProvider { - fn requests_by_block( - &self, - _id: BlockHashOrNumber, - _timestamp: u64, - ) -> ProviderResult> { - // Required data not present in static_files - Err(ProviderError::UnsupportedProvider) - } -} - impl StatsReader for StaticFileProvider { fn count_entries(&self) -> ProviderResult { match T::NAME { diff --git a/crates/storage/provider/src/providers/static_file/mod.rs b/crates/storage/provider/src/providers/static_file/mod.rs index 45b7816af02a..52eb6ed666ea 100644 --- a/crates/storage/provider/src/providers/static_file/mod.rs +++ b/crates/storage/provider/src/providers/static_file/mod.rs @@ -56,6 +56,7 @@ impl Deref for LoadedJar { mod tests { use super::*; use crate::{test_utils::create_test_provider_factory, HeaderProvider}; + use alloy_consensus::Transaction; use alloy_primitives::{BlockHash, TxNumber, B256, U256}; use rand::seq::SliceRandom; use reth_db::{ diff --git a/crates/storage/provider/src/test_utils/blocks.rs b/crates/storage/provider/src/test_utils/blocks.rs index e6a88618792d..07486f5557cb 100644 --- a/crates/storage/provider/src/test_utils/blocks.rs +++ b/crates/storage/provider/src/test_utils/blocks.rs @@ -1,6 +1,6 @@ //! Dummy blocks and data for tests use crate::{DatabaseProviderRW, ExecutionOutcome}; -use alloy_consensus::TxLegacy; +use alloy_consensus::{TxLegacy, EMPTY_OMMER_ROOT_HASH}; use alloy_primitives::{ b256, hex_literal::hex, map::HashMap, Address, BlockNumber, Bytes, Log, Parity, Sealable, TxKind, B256, U256, @@ -40,7 +40,6 @@ pub fn assert_genesis_block( ); assert_eq!(tx.table::().unwrap(), vec![]); assert_eq!(tx.table::().unwrap(), vec![]); - assert_eq!(tx.table::().unwrap(), vec![]); assert_eq!(tx.table::().unwrap(), vec![]); assert_eq!(tx.table::().unwrap(), vec![]); assert_eq!(tx.table::().unwrap(), vec![]); @@ -66,8 +65,7 @@ pub(crate) static TEST_BLOCK: LazyLock = LazyLock::new(|| SealedBlo Header { parent_hash: hex!("c86e8cc0310ae7c531c758678ddbfd16fc51c8cef8cec650b032de9869e8b94f") .into(), - ommers_hash: hex!("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347") - .into(), + ommers_hash: EMPTY_OMMER_ROOT_HASH, beneficiary: hex!("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba").into(), state_root: hex!("50554882fbbda2c2fd93fdc466db9946ea262a67f7a76cc169e714f105ab583d") .into(), @@ -201,20 +199,20 @@ fn block1(number: BlockNumber) -> (SealedBlockWithSenders, ExecutionOutcome) { .revert_account_info(number, account2, Some(None)) .state_storage(account1, HashMap::from_iter([(slot, (U256::ZERO, U256::from(10)))])) .build(), - vec![vec![Some(Receipt { - tx_type: TxType::Eip2930, - success: true, - cumulative_gas_used: 300, - logs: vec![Log::new_unchecked( - Address::new([0x60; 20]), - vec![B256::with_last_byte(1), B256::with_last_byte(2)], - Bytes::default(), - )], - #[cfg(feature = "optimism")] - deposit_nonce: None, - #[cfg(feature = "optimism")] - deposit_receipt_version: None, - })]] + vec![vec![Some( + #[allow(clippy::needless_update)] // side-effect of optimism fields + Receipt { + tx_type: TxType::Eip2930, + success: true, + cumulative_gas_used: 300, + logs: vec![Log::new_unchecked( + Address::new([0x60; 20]), + vec![B256::with_last_byte(1), B256::with_last_byte(2)], + Bytes::default(), + )], + ..Default::default() + }, + )]] .into(), number, Vec::new(), @@ -263,20 +261,20 @@ fn block2( ) .revert_storage(number, account, Vec::from([(slot, U256::from(10))])) .build(), - vec![vec![Some(Receipt { - tx_type: TxType::Eip1559, - success: false, - cumulative_gas_used: 400, - logs: vec![Log::new_unchecked( - Address::new([0x61; 20]), - vec![B256::with_last_byte(3), B256::with_last_byte(4)], - Bytes::default(), - )], - #[cfg(feature = "optimism")] - deposit_nonce: None, - #[cfg(feature = "optimism")] - deposit_receipt_version: None, - })]] + vec![vec![Some( + #[allow(clippy::needless_update)] // side-effect of optimism fields + Receipt { + tx_type: TxType::Eip1559, + success: false, + cumulative_gas_used: 400, + logs: vec![Log::new_unchecked( + Address::new([0x61; 20]), + vec![B256::with_last_byte(3), B256::with_last_byte(4)], + Bytes::default(), + )], + ..Default::default() + }, + )]] .into(), number, Vec::new(), @@ -334,20 +332,20 @@ fn block3( } let execution_outcome = ExecutionOutcome::new( bundle_state_builder.build(), - vec![vec![Some(Receipt { - tx_type: TxType::Eip1559, - success: true, - cumulative_gas_used: 400, - logs: vec![Log::new_unchecked( - Address::new([0x61; 20]), - vec![B256::with_last_byte(3), B256::with_last_byte(4)], - Bytes::default(), - )], - #[cfg(feature = "optimism")] - deposit_nonce: None, - #[cfg(feature = "optimism")] - deposit_receipt_version: None, - })]] + vec![vec![Some( + #[allow(clippy::needless_update)] // side-effect of optimism fields + Receipt { + tx_type: TxType::Eip1559, + success: true, + cumulative_gas_used: 400, + logs: vec![Log::new_unchecked( + Address::new([0x61; 20]), + vec![B256::with_last_byte(3), B256::with_last_byte(4)], + Bytes::default(), + )], + ..Default::default() + }, + )]] .into(), number, Vec::new(), @@ -425,20 +423,20 @@ fn block4( } let execution_outcome = ExecutionOutcome::new( bundle_state_builder.build(), - vec![vec![Some(Receipt { - tx_type: TxType::Eip1559, - success: true, - cumulative_gas_used: 400, - logs: vec![Log::new_unchecked( - Address::new([0x61; 20]), - vec![B256::with_last_byte(3), B256::with_last_byte(4)], - Bytes::default(), - )], - #[cfg(feature = "optimism")] - deposit_nonce: None, - #[cfg(feature = "optimism")] - deposit_receipt_version: None, - })]] + vec![vec![Some( + #[allow(clippy::needless_update)] // side-effect of optimism fields + Receipt { + tx_type: TxType::Eip1559, + success: true, + cumulative_gas_used: 400, + logs: vec![Log::new_unchecked( + Address::new([0x61; 20]), + vec![B256::with_last_byte(3), B256::with_last_byte(4)], + Bytes::default(), + )], + ..Default::default() + }, + )]] .into(), number, Vec::new(), @@ -513,20 +511,20 @@ fn block5( } let execution_outcome = ExecutionOutcome::new( bundle_state_builder.build(), - vec![vec![Some(Receipt { - tx_type: TxType::Eip1559, - success: true, - cumulative_gas_used: 400, - logs: vec![Log::new_unchecked( - Address::new([0x61; 20]), - vec![B256::with_last_byte(3), B256::with_last_byte(4)], - Bytes::default(), - )], - #[cfg(feature = "optimism")] - deposit_nonce: None, - #[cfg(feature = "optimism")] - deposit_receipt_version: None, - })]] + vec![vec![Some( + #[allow(clippy::needless_update)] // side-effect of optimism fields + Receipt { + tx_type: TxType::Eip1559, + success: true, + cumulative_gas_used: 400, + logs: vec![Log::new_unchecked( + Address::new([0x61; 20]), + vec![B256::with_last_byte(3), B256::with_last_byte(4)], + Bytes::default(), + )], + ..Default::default() + }, + )]] .into(), number, Vec::new(), diff --git a/crates/storage/provider/src/test_utils/mock.rs b/crates/storage/provider/src/test_utils/mock.rs index c7c94b939ac3..08530acf0a7f 100644 --- a/crates/storage/provider/src/test_utils/mock.rs +++ b/crates/storage/provider/src/test_utils/mock.rs @@ -2,9 +2,9 @@ use crate::{ traits::{BlockSource, ReceiptProvider}, AccountReader, BlockExecutionReader, BlockHashReader, BlockIdReader, BlockNumReader, BlockReader, BlockReaderIdExt, ChainSpecProvider, ChangeSetReader, DatabaseProvider, - EvmEnvProvider, HeaderProvider, ReceiptProviderIdExt, RequestsProvider, StateProvider, - StateProviderBox, StateProviderFactory, StateReader, StateRootProvider, TransactionVariant, - TransactionsProvider, WithdrawalsProvider, + EvmEnvProvider, HeaderProvider, ReceiptProviderIdExt, StateProvider, StateProviderBox, + StateProviderFactory, StateReader, StateRootProvider, TransactionVariant, TransactionsProvider, + WithdrawalsProvider, }; use alloy_consensus::constants::EMPTY_ROOT_HASH; use alloy_eips::{BlockHashOrNumber, BlockId, BlockNumberOrTag}; @@ -809,16 +809,6 @@ impl WithdrawalsProvider for MockEthProvider { } } -impl RequestsProvider for MockEthProvider { - fn requests_by_block( - &self, - _id: BlockHashOrNumber, - _timestamp: u64, - ) -> ProviderResult> { - Ok(None) - } -} - impl ChangeSetReader for MockEthProvider { fn account_block_changeset( &self, diff --git a/crates/storage/provider/src/test_utils/noop.rs b/crates/storage/provider/src/test_utils/noop.rs index 0a205389c9b6..f6f7e185de63 100644 --- a/crates/storage/provider/src/test_utils/noop.rs +++ b/crates/storage/provider/src/test_utils/noop.rs @@ -37,7 +37,7 @@ use crate::{ traits::{BlockSource, ReceiptProvider}, AccountReader, BlockHashReader, BlockIdReader, BlockNumReader, BlockReader, BlockReaderIdExt, ChainSpecProvider, ChangeSetReader, EvmEnvProvider, HeaderProvider, PruneCheckpointReader, - ReceiptProviderIdExt, RequestsProvider, StageCheckpointReader, StateProvider, StateProviderBox, + ReceiptProviderIdExt, StageCheckpointReader, StateProvider, StateProviderBox, StateProviderFactory, StateRootProvider, StaticFileProviderFactory, TransactionVariant, TransactionsProvider, WithdrawalsProvider, }; @@ -539,16 +539,6 @@ impl WithdrawalsProvider for NoopProvider { } } -impl RequestsProvider for NoopProvider { - fn requests_by_block( - &self, - _id: BlockHashOrNumber, - _timestamp: u64, - ) -> ProviderResult> { - Ok(None) - } -} - impl PruneCheckpointReader for NoopProvider { fn get_prune_checkpoint( &self, diff --git a/crates/storage/storage-api/Cargo.toml b/crates/storage/storage-api/Cargo.toml index 51d8eabfc40f..0ae8b284588e 100644 --- a/crates/storage/storage-api/Cargo.toml +++ b/crates/storage/storage-api/Cargo.toml @@ -26,5 +26,6 @@ reth-trie.workspace = true # ethereum alloy-eips.workspace = true alloy-primitives.workspace = true +alloy-consensus.workspace = true auto_impl.workspace = true diff --git a/crates/storage/storage-api/src/block.rs b/crates/storage/storage-api/src/block.rs index a3b0cc7438f3..01238be745e2 100644 --- a/crates/storage/storage-api/src/block.rs +++ b/crates/storage/storage-api/src/block.rs @@ -1,6 +1,6 @@ use crate::{ - BlockNumReader, HeaderProvider, ReceiptProvider, ReceiptProviderIdExt, RequestsProvider, - TransactionVariant, TransactionsProvider, WithdrawalsProvider, + BlockNumReader, HeaderProvider, ReceiptProvider, ReceiptProviderIdExt, TransactionVariant, + TransactionsProvider, WithdrawalsProvider, }; use alloy_eips::{BlockHashOrNumber, BlockId, BlockNumberOrTag}; use alloy_primitives::{BlockNumber, Sealable, B256}; @@ -52,7 +52,6 @@ pub trait BlockReader: + HeaderProvider + TransactionsProvider + ReceiptProvider - + RequestsProvider + WithdrawalsProvider + Send + Sync diff --git a/crates/storage/storage-api/src/block_id.rs b/crates/storage/storage-api/src/block_id.rs index 3d9df2e329f0..55cd6ab1c763 100644 --- a/crates/storage/storage-api/src/block_id.rs +++ b/crates/storage/storage-api/src/block_id.rs @@ -82,11 +82,10 @@ pub trait BlockIdReader: BlockNumReader + Send + Sync { BlockNumberOrTag::Pending => self .pending_block_num_hash() .map(|res_opt| res_opt.map(|num_hash| num_hash.hash)), - _ => self - .convert_block_number(num)? - .map(|num| self.block_hash(num)) - .transpose() - .map(|maybe_hash| maybe_hash.flatten()), + BlockNumberOrTag::Finalized => self.finalized_block_hash(), + BlockNumberOrTag::Safe => self.safe_block_hash(), + BlockNumberOrTag::Earliest => self.block_hash(0), + BlockNumberOrTag::Number(num) => self.block_hash(num), }, } } diff --git a/crates/storage/storage-api/src/lib.rs b/crates/storage/storage-api/src/lib.rs index 3f93bbbde2f1..4e589242a91e 100644 --- a/crates/storage/storage-api/src/lib.rs +++ b/crates/storage/storage-api/src/lib.rs @@ -31,9 +31,6 @@ pub use prune_checkpoint::*; mod receipts; pub use receipts::*; -mod requests; -pub use requests::*; - mod stage_checkpoint; pub use stage_checkpoint::*; diff --git a/crates/storage/storage-api/src/requests.rs b/crates/storage/storage-api/src/requests.rs deleted file mode 100644 index 02818c429b62..000000000000 --- a/crates/storage/storage-api/src/requests.rs +++ /dev/null @@ -1,14 +0,0 @@ -use alloy_eips::BlockHashOrNumber; -use reth_primitives::Requests; -use reth_storage_errors::provider::ProviderResult; - -/// Client trait for fetching EIP-7685 [Requests] for blocks. -#[auto_impl::auto_impl(&, Arc)] -pub trait RequestsProvider: Send + Sync { - /// Get withdrawals by block id. - fn requests_by_block( - &self, - id: BlockHashOrNumber, - timestamp: u64, - ) -> ProviderResult>; -} diff --git a/crates/storage/storage-api/src/state.rs b/crates/storage/storage-api/src/state.rs index 9a3b855ff14d..d37940f04787 100644 --- a/crates/storage/storage-api/src/state.rs +++ b/crates/storage/storage-api/src/state.rs @@ -2,11 +2,12 @@ use super::{ AccountReader, BlockHashReader, BlockIdReader, StateProofProvider, StateRootProvider, StorageRootProvider, }; +use alloy_consensus::constants::KECCAK_EMPTY; use alloy_eips::{BlockId, BlockNumHash, BlockNumberOrTag}; use alloy_primitives::{Address, BlockHash, BlockNumber, StorageKey, StorageValue, B256, U256}; use auto_impl::auto_impl; use reth_execution_types::ExecutionOutcome; -use reth_primitives::{Bytecode, KECCAK_EMPTY}; +use reth_primitives::Bytecode; use reth_storage_errors::provider::{ProviderError, ProviderResult}; /// Type alias of boxed [`StateProvider`]. diff --git a/crates/tasks/src/lib.rs b/crates/tasks/src/lib.rs index a0070698fcff..28b5eaba9ffb 100644 --- a/crates/tasks/src/lib.rs +++ b/crates/tasks/src/lib.rs @@ -288,7 +288,7 @@ pub struct TaskExecutor { on_shutdown: Shutdown, /// Sender half for sending panic signals to this type panicked_tasks_tx: UnboundedSender, - // Task Executor Metrics + /// Task Executor Metrics metrics: TaskExecutorMetrics, /// How many [`GracefulShutdown`] tasks are currently active graceful_tasks: Arc, diff --git a/crates/transaction-pool/Cargo.toml b/crates/transaction-pool/Cargo.toml index 41abbb4b6b7e..1bfb10d86d77 100644 --- a/crates/transaction-pool/Cargo.toml +++ b/crates/transaction-pool/Cargo.toml @@ -27,6 +27,7 @@ revm.workspace = true alloy-eips.workspace = true alloy-primitives.workspace = true alloy-rlp.workspace = true +alloy-consensus.workspace = true # async/futures futures-util.workspace = true @@ -54,7 +55,6 @@ rand = { workspace = true, optional = true } paste = { workspace = true, optional = true } proptest = { workspace = true, optional = true } proptest-arbitrary-interop = { workspace = true, optional = true } -alloy-consensus = { workspace = true, optional = true } [dev-dependencies] reth-primitives = { workspace = true, features = ["arbitrary"] } @@ -69,13 +69,46 @@ pprof = { workspace = true, features = ["criterion", "flamegraph"] } assert_matches.workspace = true tempfile.workspace = true serde_json.workspace = true -alloy-consensus.workspace = true [features] default = ["serde"] -serde = ["dep:serde"] -test-utils = ["rand", "paste", "serde", "alloy-consensus"] -arbitrary = ["proptest", "reth-primitives/arbitrary", "proptest-arbitrary-interop"] +serde = [ + "dep:serde", + "reth-execution-types/serde", + "reth-eth-wire-types/serde", + "reth-provider/serde", + "alloy-consensus/serde", + "alloy-eips/serde", + "alloy-primitives/serde", + "bitflags/serde", + "parking_lot/serde", + "rand?/serde", + "revm/serde", + "smallvec/serde" +] +test-utils = [ + "rand", + "paste", + "serde", + "reth-chain-state/test-utils", + "reth-chainspec/test-utils", + "reth-primitives/test-utils", + "reth-provider/test-utils", + "revm/test-utils" +] +arbitrary = [ + "proptest", + "reth-primitives/arbitrary", + "proptest-arbitrary-interop", + "reth-chainspec/arbitrary", + "reth-eth-wire-types/arbitrary", + "alloy-consensus/arbitrary", + "alloy-eips/arbitrary", + "alloy-primitives/arbitrary", + "bitflags/arbitrary", + "revm/arbitrary", + "smallvec/arbitrary" +] [[bench]] name = "truncate" diff --git a/crates/transaction-pool/benches/truncate.rs b/crates/transaction-pool/benches/truncate.rs index 22e457630540..1ca6f98499cd 100644 --- a/crates/transaction-pool/benches/truncate.rs +++ b/crates/transaction-pool/benches/truncate.rs @@ -66,7 +66,7 @@ fn generate_many_transactions(senders: usize, max_depth: usize) -> Vec().new_tree(&mut runner).unwrap().current() % max_depth + 1; diff --git a/crates/transaction-pool/src/blobstore/disk.rs b/crates/transaction-pool/src/blobstore/disk.rs index 96119a0f8170..e168a1c11116 100644 --- a/crates/transaction-pool/src/blobstore/disk.rs +++ b/crates/transaction-pool/src/blobstore/disk.rs @@ -409,13 +409,9 @@ impl DiskFileBlobStoreInner { /// Returns an error if there are any missing blobs. #[inline] fn get_exact(&self, txs: Vec) -> Result, BlobStoreError> { - let mut res = Vec::with_capacity(txs.len()); - for tx in txs { - let blob = self.get_one(tx)?.ok_or_else(|| BlobStoreError::MissingSidecar(tx))?; - res.push(blob) - } - - Ok(res) + txs.into_iter() + .map(|tx| self.get_one(tx)?.ok_or(BlobStoreError::MissingSidecar(tx))) + .collect() } } diff --git a/crates/transaction-pool/src/blobstore/mem.rs b/crates/transaction-pool/src/blobstore/mem.rs index 15160c2c3fad..c98a01b88c18 100644 --- a/crates/transaction-pool/src/blobstore/mem.rs +++ b/crates/transaction-pool/src/blobstore/mem.rs @@ -76,42 +76,26 @@ impl BlobStore for InMemoryBlobStore { // Retrieves the decoded blob data for the given transaction hash. fn get(&self, tx: B256) -> Result, BlobStoreError> { - let store = self.inner.store.read(); - Ok(store.get(&tx).cloned()) + Ok(self.inner.store.read().get(&tx).cloned()) } fn contains(&self, tx: B256) -> Result { - let store = self.inner.store.read(); - Ok(store.contains_key(&tx)) + Ok(self.inner.store.read().contains_key(&tx)) } fn get_all( &self, txs: Vec, ) -> Result, BlobStoreError> { - let mut items = Vec::with_capacity(txs.len()); let store = self.inner.store.read(); - for tx in txs { - if let Some(item) = store.get(&tx) { - items.push((tx, item.clone())); - } - } - - Ok(items) + Ok(txs.into_iter().filter_map(|tx| store.get(&tx).map(|item| (tx, item.clone()))).collect()) } fn get_exact(&self, txs: Vec) -> Result, BlobStoreError> { - let mut items = Vec::with_capacity(txs.len()); let store = self.inner.store.read(); - for tx in txs { - if let Some(item) = store.get(&tx) { - items.push(item.clone()); - } else { - return Err(BlobStoreError::MissingSidecar(tx)) - } - } - - Ok(items) + txs.into_iter() + .map(|tx| store.get(&tx).cloned().ok_or_else(|| BlobStoreError::MissingSidecar(tx))) + .collect() } fn get_by_versioned_hashes( diff --git a/crates/transaction-pool/src/blobstore/tracker.rs b/crates/transaction-pool/src/blobstore/tracker.rs index e6041fa12e19..f22dcf5706e5 100644 --- a/crates/transaction-pool/src/blobstore/tracker.rs +++ b/crates/transaction-pool/src/blobstore/tracker.rs @@ -81,6 +81,13 @@ pub enum BlobStoreUpdates { #[cfg(test)] mod tests { + use alloy_consensus::Header; + use reth_execution_types::Chain; + use reth_primitives::{ + BlockBody, SealedBlock, SealedBlockWithSenders, SealedHeader, Transaction, + TransactionSigned, + }; + use super::*; #[test] @@ -101,4 +108,85 @@ mod tests { BlobStoreUpdates::Finalized(block2.into_iter().chain(block3).collect::>()) ); } + + #[test] + fn test_add_new_chain_blocks() { + let mut tracker = BlobStoreCanonTracker::default(); + + // Create sample transactions + let tx1_hash = B256::random(); // EIP-4844 transaction + let tx2_hash = B256::random(); // EIP-4844 transaction + let tx3_hash = B256::random(); // Non-EIP-4844 transaction + + // Creating a first block with EIP-4844 transactions + let block1 = SealedBlockWithSenders { + block: SealedBlock { + header: SealedHeader::new( + Header { number: 10, ..Default::default() }, + B256::random(), + ), + body: BlockBody { + transactions: vec![ + TransactionSigned { + hash: tx1_hash, + transaction: Transaction::Eip4844(Default::default()), + ..Default::default() + }, + TransactionSigned { + hash: tx2_hash, + transaction: Transaction::Eip4844(Default::default()), + ..Default::default() + }, + // Another transaction that is not EIP-4844 + TransactionSigned { + hash: B256::random(), + transaction: Transaction::Eip7702(Default::default()), + ..Default::default() + }, + ], + ..Default::default() + }, + }, + ..Default::default() + }; + + // Creating a second block with EIP-1559 and EIP-2930 transactions + // Note: This block does not contain any EIP-4844 transactions + let block2 = SealedBlockWithSenders { + block: SealedBlock { + header: SealedHeader::new( + Header { number: 11, ..Default::default() }, + B256::random(), + ), + body: BlockBody { + transactions: vec![ + TransactionSigned { + hash: tx3_hash, + transaction: Transaction::Eip1559(Default::default()), + ..Default::default() + }, + TransactionSigned { + hash: tx2_hash, + transaction: Transaction::Eip2930(Default::default()), + ..Default::default() + }, + ], + ..Default::default() + }, + }, + ..Default::default() + }; + + // Extract blocks from the chain + let chain = Chain::new(vec![block1, block2], Default::default(), None); + let blocks = chain.into_inner().0; + + // Add new chain blocks to the tracker + tracker.add_new_chain_blocks(&blocks); + + // Tx1 and tx2 should be in the block containing EIP-4844 transactions + assert_eq!(tracker.blob_txs_in_blocks.get(&10).unwrap(), &vec![tx1_hash, tx2_hash]); + // No transactions should be in the block containing non-EIP-4844 transactions + assert!(tracker.blob_txs_in_blocks.get(&11).unwrap().is_empty()); + } } diff --git a/crates/transaction-pool/src/config.rs b/crates/transaction-pool/src/config.rs index 1b4b010a8e14..30703f888c38 100644 --- a/crates/transaction-pool/src/config.rs +++ b/crates/transaction-pool/src/config.rs @@ -2,11 +2,9 @@ use crate::{ pool::{NEW_TX_LISTENER_BUFFER_SIZE, PENDING_TX_LISTENER_BUFFER_SIZE}, PoolSize, TransactionOrigin, }; +use alloy_consensus::constants::EIP4844_TX_TYPE_ID; use alloy_primitives::Address; -use reth_primitives::{ - constants::{ETHEREUM_BLOCK_GAS_LIMIT, MIN_PROTOCOL_BASE_FEE}, - EIP4844_TX_TYPE_ID, -}; +use reth_primitives::constants::{ETHEREUM_BLOCK_GAS_LIMIT, MIN_PROTOCOL_BASE_FEE}; use std::{collections::HashSet, ops::Mul}; /// Guarantees max transactions for one sender, compatible with geth/erigon diff --git a/crates/transaction-pool/src/identifier.rs b/crates/transaction-pool/src/identifier.rs index 97d4bda8d03b..2e5312a210fe 100644 --- a/crates/transaction-pool/src/identifier.rs +++ b/crates/transaction-pool/src/identifier.rs @@ -59,6 +59,11 @@ impl SenderId { pub const fn start_bound(self) -> std::ops::Bound { std::ops::Bound::Included(TransactionId::new(self, 0)) } + + /// Converts the sender to a [`TransactionId`] with the given nonce. + pub const fn into_transaction_id(self, nonce: u64) -> TransactionId { + TransactionId::new(self, nonce) + } } impl From for SenderId { diff --git a/crates/transaction-pool/src/lib.rs b/crates/transaction-pool/src/lib.rs index 75465afac1a1..2cffcd33fa8b 100644 --- a/crates/transaction-pool/src/lib.rs +++ b/crates/transaction-pool/src/lib.rs @@ -510,6 +510,14 @@ where self.pool.get_highest_transaction_by_sender(sender) } + fn get_highest_consecutive_transaction_by_sender( + &self, + sender: Address, + on_chain_nonce: u64, + ) -> Option>> { + self.pool.get_highest_consecutive_transaction_by_sender(sender, on_chain_nonce) + } + fn get_transaction_by_sender_and_nonce( &self, sender: Address, diff --git a/crates/transaction-pool/src/noop.rs b/crates/transaction-pool/src/noop.rs index 681aa9896abb..4464ae1fc8a5 100644 --- a/crates/transaction-pool/src/noop.rs +++ b/crates/transaction-pool/src/noop.rs @@ -227,6 +227,14 @@ impl TransactionPool for NoopTransactionPool { None } + fn get_highest_consecutive_transaction_by_sender( + &self, + _sender: Address, + _on_chain_nonce: u64, + ) -> Option>> { + None + } + fn get_transaction_by_sender_and_nonce( &self, _sender: Address, diff --git a/crates/transaction-pool/src/pool/mod.rs b/crates/transaction-pool/src/pool/mod.rs index 44dd5a8d525f..600a8da934ec 100644 --- a/crates/transaction-pool/src/pool/mod.rs +++ b/crates/transaction-pool/src/pool/mod.rs @@ -785,6 +785,18 @@ where self.get_pool_data().get_highest_transaction_by_sender(sender_id) } + /// Returns the transaction with the highest nonce that is executable given the on chain nonce. + pub(crate) fn get_highest_consecutive_transaction_by_sender( + &self, + sender: Address, + on_chain_nonce: u64, + ) -> Option>> { + let sender_id = self.get_sender_id(sender); + self.get_pool_data().get_highest_consecutive_transaction_by_sender( + sender_id.into_transaction_id(on_chain_nonce), + ) + } + /// Returns all transactions that where submitted with the given [`TransactionOrigin`] pub(crate) fn get_transactions_by_origin( &self, diff --git a/crates/transaction-pool/src/pool/pending.rs b/crates/transaction-pool/src/pool/pending.rs index ff3ecf65a494..ff5269014c4c 100644 --- a/crates/transaction-pool/src/pool/pending.rs +++ b/crates/transaction-pool/src/pool/pending.rs @@ -197,7 +197,7 @@ impl PendingPool { } } else { self.size_of += tx.transaction.size(); - self.update_independents_and_highest_nonces(&tx, &id); + self.update_independents_and_highest_nonces(&tx); self.all.insert(tx.clone()); self.by_id.insert(id, tx); } @@ -243,7 +243,7 @@ impl PendingPool { tx.priority = self.ordering.priority(&tx.transaction.transaction, base_fee); self.size_of += tx.transaction.size(); - self.update_independents_and_highest_nonces(&tx, &id); + self.update_independents_and_highest_nonces(&tx); self.all.insert(tx.clone()); self.by_id.insert(id, tx); } @@ -254,12 +254,8 @@ impl PendingPool { /// Updates the independent transaction and highest nonces set, assuming the given transaction /// is being _added_ to the pool. - fn update_independents_and_highest_nonces( - &mut self, - tx: &PendingTransaction, - tx_id: &TransactionId, - ) { - let ancestor_id = tx_id.unchecked_ancestor(); + fn update_independents_and_highest_nonces(&mut self, tx: &PendingTransaction) { + let ancestor_id = tx.transaction.id().unchecked_ancestor(); if let Some(ancestor) = ancestor_id.and_then(|id| self.by_id.get(&id)) { // the transaction already has an ancestor, so we only need to ensure that the // highest nonces set actually contains the highest nonce for that sender @@ -305,7 +301,7 @@ impl PendingPool { let priority = self.ordering.priority(&tx.transaction, base_fee); let tx = PendingTransaction { submission_id, transaction: tx, priority }; - self.update_independents_and_highest_nonces(&tx, &tx_id); + self.update_independents_and_highest_nonces(&tx); self.all.insert(tx.clone()); // send the new transaction to any existing pendingpool static file iterators diff --git a/crates/transaction-pool/src/pool/txpool.rs b/crates/transaction-pool/src/pool/txpool.rs index 7aaa77df49f4..a85a9a1856b9 100644 --- a/crates/transaction-pool/src/pool/txpool.rs +++ b/crates/transaction-pool/src/pool/txpool.rs @@ -18,14 +18,14 @@ use crate::{ PoolConfig, PoolResult, PoolTransaction, PriceBumpConfig, TransactionOrdering, ValidPoolTransaction, U256, }; -use alloy_primitives::{Address, TxHash, B256}; -use reth_primitives::{ - constants::{ - eip4844::BLOB_TX_MIN_BLOB_GASPRICE, ETHEREUM_BLOCK_GAS_LIMIT, MIN_PROTOCOL_BASE_FEE, - }, +use alloy_consensus::constants::{ EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID, EIP4844_TX_TYPE_ID, EIP7702_TX_TYPE_ID, LEGACY_TX_TYPE_ID, }; +use alloy_primitives::{Address, TxHash, B256}; +use reth_primitives::constants::{ + eip4844::BLOB_TX_MIN_BLOB_GASPRICE, ETHEREUM_BLOCK_GAS_LIMIT, MIN_PROTOCOL_BASE_FEE, +}; use rustc_hash::FxHashMap; use smallvec::SmallVec; use std::{ @@ -108,6 +108,35 @@ impl TxPool { self.all().txs_iter(sender).last().map(|(_, tx)| Arc::clone(&tx.transaction)) } + /// Returns the transaction with the highest nonce that is executable given the on chain nonce. + /// + /// If the pool already tracks a higher nonce for the given sender, then this nonce is used + /// instead. + /// + /// Note: The next pending pooled transaction must have the on chain nonce. + pub(crate) fn get_highest_consecutive_transaction_by_sender( + &self, + mut on_chain: TransactionId, + ) -> Option>> { + let mut last_consecutive_tx = None; + + // ensure this operates on the most recent + if let Some(current) = self.sender_info.get(&on_chain.sender) { + on_chain.nonce = on_chain.nonce.max(current.state_nonce); + } + + let mut next_expected_nonce = on_chain.nonce; + for (id, tx) in self.all().descendant_txs_inclusive(&on_chain) { + if next_expected_nonce != id.nonce { + break + } + next_expected_nonce = id.next_nonce(); + last_consecutive_tx = Some(tx); + } + + last_consecutive_tx.map(|tx| Arc::clone(&tx.transaction)) + } + /// Returns access to the [`AllTransactions`] container. pub(crate) const fn all(&self) -> &AllTransactions { &self.all_transactions @@ -2755,6 +2784,47 @@ mod tests { assert_eq!(highest_tx.as_ref().transaction, tx1); } + #[test] + fn get_highest_consecutive_transaction_by_sender() { + // Set up a mock transaction factory and a new transaction pool. + let mut pool = TxPool::new(MockOrdering::default(), PoolConfig::default()); + let mut f = MockTransactionFactory::default(); + + // Create transactions with nonces 0, 1, 2, 4, 5. + let sender = Address::random(); + let txs: Vec<_> = vec![0, 1, 2, 4, 5, 8, 9]; + for nonce in txs { + let mut mock_tx = MockTransaction::eip1559(); + mock_tx.set_sender(sender); + mock_tx.set_nonce(nonce); + + let validated_tx = f.validated(mock_tx); + pool.add_transaction(validated_tx, U256::from(1000), 0).unwrap(); + } + + // Get last consecutive transaction + let sender_id = f.ids.sender_id(&sender).unwrap(); + let next_tx = + pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(0)); + assert_eq!(next_tx.map(|tx| tx.nonce()), Some(2), "Expected nonce 2 for on-chain nonce 0"); + + let next_tx = + pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(4)); + assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 4"); + + let next_tx = + pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(5)); + assert_eq!(next_tx.map(|tx| tx.nonce()), Some(5), "Expected nonce 5 for on-chain nonce 5"); + + // update the tracked nonce + let mut info = SenderInfo::default(); + info.update(8, U256::ZERO); + pool.sender_info.insert(sender_id, info); + let next_tx = + pool.get_highest_consecutive_transaction_by_sender(sender_id.into_transaction_id(5)); + assert_eq!(next_tx.map(|tx| tx.nonce()), Some(9), "Expected nonce 9 for on-chain nonce 8"); + } + #[test] fn discard_nonce_too_low() { let mut f = MockTransactionFactory::default(); diff --git a/crates/transaction-pool/src/test_utils/mock.rs b/crates/transaction-pool/src/test_utils/mock.rs index e2b5f373e446..474cf5cc8f83 100644 --- a/crates/transaction-pool/src/test_utils/mock.rs +++ b/crates/transaction-pool/src/test_utils/mock.rs @@ -7,7 +7,10 @@ use crate::{ CoinbaseTipOrdering, EthBlobTransactionSidecar, EthPoolTransaction, PoolTransaction, ValidPoolTransaction, }; -use alloy_consensus::{TxEip1559, TxEip2930, TxEip4844, TxLegacy}; +use alloy_consensus::{ + constants::{EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID, EIP4844_TX_TYPE_ID, LEGACY_TX_TYPE_ID}, + TxEip1559, TxEip2930, TxEip4844, TxLegacy, +}; use alloy_eips::eip2930::AccessList; use alloy_primitives::{Address, Bytes, ChainId, TxHash, TxKind, B256, U256}; use paste::paste; @@ -20,7 +23,6 @@ use reth_primitives::{ transaction::TryFromRecoveredTransactionError, BlobTransactionSidecar, BlobTransactionValidationError, PooledTransactionsElementEcRecovered, Signature, Transaction, TransactionSigned, TransactionSignedEcRecovered, TxType, - EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID, EIP4844_TX_TYPE_ID, LEGACY_TX_TYPE_ID, }; use std::{ops::Range, sync::Arc, time::Instant, vec::IntoIter}; diff --git a/crates/transaction-pool/src/traits.rs b/crates/transaction-pool/src/traits.rs index 07fe9b9c206e..fcfdae4ed1b3 100644 --- a/crates/transaction-pool/src/traits.rs +++ b/crates/transaction-pool/src/traits.rs @@ -7,6 +7,10 @@ use crate::{ validate::ValidPoolTransaction, AllTransactionsEvents, }; +use alloy_consensus::{ + constants::{EIP1559_TX_TYPE_ID, EIP4844_TX_TYPE_ID, EIP7702_TX_TYPE_ID}, + Transaction as _, +}; use alloy_eips::{eip2718::Encodable2718, eip2930::AccessList, eip4844::BlobAndProofV1}; use alloy_primitives::{Address, TxHash, TxKind, B256, U256}; use futures_util::{ready, Stream}; @@ -16,7 +20,6 @@ use reth_primitives::{ kzg::KzgSettings, transaction::TryFromRecoveredTransactionError, BlobTransactionSidecar, BlobTransactionValidationError, PooledTransactionsElement, PooledTransactionsElementEcRecovered, SealedBlock, Transaction, TransactionSignedEcRecovered, - EIP1559_TX_TYPE_ID, EIP4844_TX_TYPE_ID, EIP7702_TX_TYPE_ID, }; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -356,6 +359,21 @@ pub trait TransactionPool: Send + Sync + Clone { sender: Address, ) -> Option>>; + /// Returns the transaction with the highest nonce that is executable given the on chain nonce. + /// In other words the highest non nonce gapped transaction. + /// + /// Note: The next pending pooled transaction must have the on chain nonce. + /// + /// For example, for a given on chain nonce of `5`, the next transaction must have that nonce. + /// If the pool contains txs `[5,6,7]` this returns tx `7`. + /// If the pool contains txs `[6,7]` this returns `None` because the next valid nonce (5) is + /// missing, which means txs `[6,7]` are nonce gapped. + fn get_highest_consecutive_transaction_by_sender( + &self, + sender: Address, + on_chain_nonce: u64, + ) -> Option>>; + /// Returns a transaction sent by a given user and a nonce fn get_transaction_by_sender_and_nonce( &self, @@ -1167,7 +1185,7 @@ impl PoolTransaction for EthPooledTransaction { /// For EIP-1559 transactions: `min(max_fee_per_gas - base_fee, max_priority_fee_per_gas)`. /// For legacy transactions: `gas_price - base_fee`. fn effective_tip_per_gas(&self, base_fee: u64) -> Option { - self.transaction.effective_tip_per_gas(Some(base_fee)) + self.transaction.effective_tip_per_gas(base_fee) } /// Returns the max priority fee per gas if the transaction is an EIP-1559 transaction, and @@ -1183,7 +1201,7 @@ impl PoolTransaction for EthPooledTransaction { } fn input(&self) -> &[u8] { - self.transaction.input().as_ref() + self.transaction.input() } /// Returns a measurement of the heap usage of this type and all its internals. diff --git a/crates/transaction-pool/src/validate/eth.rs b/crates/transaction-pool/src/validate/eth.rs index 49165f189a05..22744c58a798 100644 --- a/crates/transaction-pool/src/validate/eth.rs +++ b/crates/transaction-pool/src/validate/eth.rs @@ -11,11 +11,13 @@ use crate::{ EthBlobTransactionSidecar, EthPoolTransaction, LocalTransactionConfig, PoolTransaction, TransactionValidationOutcome, TransactionValidationTaskExecutor, TransactionValidator, }; +use alloy_consensus::constants::{ + EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID, EIP4844_TX_TYPE_ID, EIP7702_TX_TYPE_ID, + LEGACY_TX_TYPE_ID, +}; use reth_chainspec::{ChainSpec, EthereumHardforks}; use reth_primitives::{ constants::eip4844::MAX_BLOBS_PER_BLOCK, GotExpected, InvalidTransactionError, SealedBlock, - EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID, EIP4844_TX_TYPE_ID, EIP7702_TX_TYPE_ID, - LEGACY_TX_TYPE_ID, }; use reth_storage_api::{AccountReader, StateProviderFactory}; use reth_tasks::TaskSpawner; @@ -639,6 +641,7 @@ impl EthTransactionValidatorBuilder { pub fn with_head_timestamp(mut self, timestamp: u64) -> Self { self.cancun = self.chain_spec.is_cancun_active_at_timestamp(timestamp); self.shanghai = self.chain_spec.is_shanghai_active_at_timestamp(timestamp); + self.prague = self.chain_spec.is_prague_active_at_timestamp(timestamp); self } diff --git a/crates/trie/common/Cargo.toml b/crates/trie/common/Cargo.toml index 0bd28140f447..2c6ccbfe689b 100644 --- a/crates/trie/common/Cargo.toml +++ b/crates/trie/common/Cargo.toml @@ -41,8 +41,19 @@ hash-db = "=0.15.2" plain_hasher = "0.2" [features] -test-utils = ["dep:plain_hasher", "dep:hash-db", "arbitrary"] +test-utils = [ + "dep:plain_hasher", + "dep:hash-db", + "arbitrary", + "reth-primitives-traits/test-utils", + "reth-codecs/test-utils" +] arbitrary = [ - "alloy-trie/arbitrary", - "dep:arbitrary", + "alloy-trie/arbitrary", + "dep:arbitrary", + "reth-primitives-traits/arbitrary", + "alloy-consensus/arbitrary", + "alloy-primitives/arbitrary", + "nybbles/arbitrary", + "revm-primitives/arbitrary" ] diff --git a/crates/trie/common/src/proofs.rs b/crates/trie/common/src/proofs.rs index 8aca67f8d1ad..a94b2b96fbdf 100644 --- a/crates/trie/common/src/proofs.rs +++ b/crates/trie/common/src/proofs.rs @@ -1,6 +1,7 @@ //! Merkle trie proofs. use crate::{Nibbles, TrieAccount}; +use alloy_consensus::constants::KECCAK_EMPTY; use alloy_primitives::{keccak256, Address, Bytes, B256, U256}; use alloy_rlp::{encode_fixed_size, Decodable, EMPTY_STRING_CODE}; use alloy_trie::{ @@ -9,13 +10,13 @@ use alloy_trie::{ EMPTY_ROOT_HASH, }; use itertools::Itertools; -use reth_primitives_traits::{constants::KECCAK_EMPTY, Account}; +use reth_primitives_traits::Account; use serde::{Deserialize, Serialize}; use std::collections::HashMap; /// The state multiproof of target accounts and multiproofs of their storage tries. /// Multiproof is effectively a state subtrie that only contains the nodes -/// in the paths of target accounts. +/// in the paths of target accounts. #[derive(Clone, Default, Debug)] pub struct MultiProof { /// State trie multiproof for requested accounts. diff --git a/crates/trie/db/Cargo.toml b/crates/trie/db/Cargo.toml index 6d322ba3ff67..55fa9a851b17 100644 --- a/crates/trie/db/Cargo.toml +++ b/crates/trie/db/Cargo.toml @@ -17,7 +17,6 @@ reth-primitives.workspace = true reth-execution-errors.workspace = true reth-db.workspace = true reth-db-api.workspace = true -reth-stages-types.workspace = true reth-storage-errors.workspace = true reth-trie-common.workspace = true reth-trie.workspace = true @@ -32,10 +31,7 @@ alloy-primitives.workspace = true tracing.workspace = true # misc -rayon.workspace = true derive_more.workspace = true -auto_impl.workspace = true -itertools.workspace = true # `metrics` feature reth-metrics = { workspace = true, optional = true } @@ -56,7 +52,8 @@ reth-provider = { workspace = true, features = ["test-utils"] } reth-storage-errors.workspace = true reth-trie-common = { workspace = true, features = ["test-utils", "arbitrary"] } reth-trie = { workspace = true, features = ["test-utils"] } -reth-node-types.workspace = true + +alloy-consensus.workspace = true # trie triehash = "0.8" @@ -64,16 +61,28 @@ triehash = "0.8" # misc proptest.workspace = true proptest-arbitrary-interop.workspace = true -tokio = { workspace = true, default-features = false, features = [ - "sync", - "rt", - "macros", -] } -tokio-stream.workspace = true serde_json.workspace = true similar-asserts.workspace = true [features] metrics = ["reth-metrics", "reth-trie/metrics", "dep:metrics"] -serde = ["dep:serde"] -test-utils = ["triehash", "reth-trie-common/test-utils"] +serde = [ + "dep:serde", + "reth-provider/serde", + "reth-trie/serde", + "alloy-consensus/serde", + "alloy-primitives/serde", + "revm/serde", + "similar-asserts/serde" +] +test-utils = [ + "triehash", + "reth-trie-common/test-utils", + "reth-chainspec/test-utils", + "reth-primitives/test-utils", + "reth-db/test-utils", + "reth-db-api/test-utils", + "reth-provider/test-utils", + "reth-trie/test-utils", + "revm/test-utils" +] diff --git a/crates/trie/db/tests/proof.rs b/crates/trie/db/tests/proof.rs index 5ffa6729b49a..79a2ce96fced 100644 --- a/crates/trie/db/tests/proof.rs +++ b/crates/trie/db/tests/proof.rs @@ -1,9 +1,10 @@ #![allow(missing_docs)] +use alloy_consensus::EMPTY_ROOT_HASH; use alloy_primitives::{keccak256, Address, Bytes, B256, U256}; use alloy_rlp::EMPTY_STRING_CODE; use reth_chainspec::{Chain, ChainSpec, HOLESKY, MAINNET}; -use reth_primitives::{constants::EMPTY_ROOT_HASH, Account}; +use reth_primitives::Account; use reth_provider::test_utils::{create_test_provider_factory, insert_genesis}; use reth_trie::{proof::Proof, Nibbles}; use reth_trie_common::{AccountProof, StorageProof}; diff --git a/crates/trie/db/tests/trie.rs b/crates/trie/db/tests/trie.rs index 59fffec58d06..f5823404c899 100644 --- a/crates/trie/db/tests/trie.rs +++ b/crates/trie/db/tests/trie.rs @@ -1,5 +1,6 @@ #![allow(missing_docs)] +use alloy_consensus::EMPTY_ROOT_HASH; use alloy_primitives::{hex_literal::hex, keccak256, Address, B256, U256}; use proptest::{prelude::ProptestConfig, proptest}; use proptest_arbitrary_interop::arb; @@ -8,7 +9,7 @@ use reth_db_api::{ cursor::{DbCursorRO, DbCursorRW, DbDupCursorRO}, transaction::DbTxMut, }; -use reth_primitives::{constants::EMPTY_ROOT_HASH, Account, StorageEntry}; +use reth_primitives::{Account, StorageEntry}; use reth_provider::{ test_utils::create_test_provider_factory, DatabaseProviderRW, StorageTrieWriter, TrieWriter, }; diff --git a/crates/trie/db/tests/witness.rs b/crates/trie/db/tests/witness.rs index 59656383de4c..8e00472b4738 100644 --- a/crates/trie/db/tests/witness.rs +++ b/crates/trie/db/tests/witness.rs @@ -1,12 +1,15 @@ #![allow(missing_docs)] +use alloy_consensus::EMPTY_ROOT_HASH; use alloy_primitives::{ keccak256, map::{HashMap, HashSet}, Address, Bytes, B256, U256, }; use alloy_rlp::EMPTY_STRING_CODE; -use reth_primitives::{constants::EMPTY_ROOT_HASH, Account, StorageEntry}; +use reth_db::{cursor::DbCursorRW, tables}; +use reth_db_api::transaction::DbTxMut; +use reth_primitives::{Account, StorageEntry}; use reth_provider::{test_utils::create_test_provider_factory, HashingWriter}; use reth_trie::{proof::Proof, witness::TrieWitness, HashedPostState, HashedStorage, StateRoot}; use reth_trie_db::{DatabaseProof, DatabaseStateRoot, DatabaseTrieWitness}; @@ -91,3 +94,53 @@ fn includes_nodes_for_destroyed_storage_nodes() { assert_eq!(witness.get(&keccak256(node)), Some(node)); } } + +#[test] +fn correctly_decodes_branch_node_values() { + let factory = create_test_provider_factory(); + let provider = factory.provider_rw().unwrap(); + + let address = Address::random(); + let hashed_address = keccak256(address); + let hashed_slot1 = B256::with_last_byte(1); + let hashed_slot2 = B256::with_last_byte(2); + + // Insert account and slots into database + provider.insert_account_for_hashing([(address, Some(Account::default()))]).unwrap(); + let mut hashed_storage_cursor = + provider.tx_ref().cursor_dup_write::().unwrap(); + hashed_storage_cursor + .upsert(hashed_address, StorageEntry { key: hashed_slot1, value: U256::from(1) }) + .unwrap(); + hashed_storage_cursor + .upsert(hashed_address, StorageEntry { key: hashed_slot2, value: U256::from(1) }) + .unwrap(); + + let state_root = StateRoot::from_tx(provider.tx_ref()).root().unwrap(); + let multiproof = Proof::from_tx(provider.tx_ref()) + .multiproof(HashMap::from_iter([( + hashed_address, + HashSet::from_iter([hashed_slot1, hashed_slot2]), + )])) + .unwrap(); + + let witness = TrieWitness::from_tx(provider.tx_ref()) + .compute(HashedPostState { + accounts: HashMap::from([(hashed_address, Some(Account::default()))]), + storages: HashMap::from([( + hashed_address, + HashedStorage::from_iter( + false, + [hashed_slot1, hashed_slot2].map(|hashed_slot| (hashed_slot, U256::from(2))), + ), + )]), + }) + .unwrap(); + assert!(witness.contains_key(&state_root)); + for node in multiproof.account_subtree.values() { + assert_eq!(witness.get(&keccak256(node)), Some(node)); + } + for node in multiproof.storages.iter().flat_map(|(_, storage)| storage.subtree.values()) { + assert_eq!(witness.get(&keccak256(node)), Some(node)); + } +} diff --git a/crates/trie/parallel/Cargo.toml b/crates/trie/parallel/Cargo.toml index 64a4644bdce4..cc35fe9f914a 100644 --- a/crates/trie/parallel/Cargo.toml +++ b/crates/trie/parallel/Cargo.toml @@ -15,7 +15,6 @@ workspace = true # reth reth-primitives.workspace = true reth-db.workspace = true -reth-db-api.workspace = true reth-trie.workspace = true reth-trie-db.workspace = true reth-execution-errors.workspace = true @@ -46,7 +45,11 @@ reth-trie = { workspace = true, features = ["test-utils"] } # misc rand.workspace = true -tokio = { workspace = true, default-features = false, features = ["sync", "rt", "macros"] } +tokio = { workspace = true, default-features = false, features = [ + "sync", + "rt", + "macros", +] } rayon.workspace = true criterion = { workspace = true, features = ["async_tokio"] } proptest.workspace = true diff --git a/crates/trie/parallel/src/parallel_root.rs b/crates/trie/parallel/src/parallel_root.rs index a64b8351446e..e432b91062ca 100644 --- a/crates/trie/parallel/src/parallel_root.rs +++ b/crates/trie/parallel/src/parallel_root.rs @@ -320,7 +320,7 @@ mod tests { hashed_state .storages .entry(hashed_address) - .or_insert_with(|| HashedStorage::new(false)) + .or_insert_with(HashedStorage::default) .storage .insert(hashed_slot, *value); } diff --git a/crates/trie/sparse/Cargo.toml b/crates/trie/sparse/Cargo.toml index 4ebb56145e1f..26d036f57ff9 100644 --- a/crates/trie/sparse/Cargo.toml +++ b/crates/trie/sparse/Cargo.toml @@ -10,3 +10,34 @@ description = "Sparse MPT implementation" [lints] workspace = true + + +[dependencies] +# reth +reth-tracing.workspace = true +reth-trie-common.workspace = true +reth-trie.workspace = true + +# alloy +alloy-primitives.workspace = true +alloy-rlp.workspace = true + +# misc +smallvec = { workspace = true, features = ["const_new"] } +thiserror.workspace = true + +[dev-dependencies] +reth-testing-utils.workspace = true +reth-trie = { workspace = true, features = ["test-utils"] } +reth-trie-common = { workspace = true, features = ["test-utils", "arbitrary"] } + +assert_matches.workspace = true +criterion.workspace = true +itertools.workspace = true +pretty_assertions = "1.4" +proptest.workspace = true +rand.workspace = true + +[[bench]] +name = "root" +harness = false diff --git a/crates/trie/sparse/benches/root.rs b/crates/trie/sparse/benches/root.rs new file mode 100644 index 000000000000..248e3caeeeee --- /dev/null +++ b/crates/trie/sparse/benches/root.rs @@ -0,0 +1,204 @@ +#![allow(missing_docs, unreachable_pub)] +use alloy_primitives::{map::HashMap, B256, U256}; +use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; +use itertools::Itertools; +use proptest::{prelude::*, strategy::ValueTree, test_runner::TestRunner}; +use reth_trie::{ + hashed_cursor::{noop::NoopHashedStorageCursor, HashedPostStateStorageCursor}, + node_iter::{TrieElement, TrieNodeIter}, + trie_cursor::{noop::NoopStorageTrieCursor, InMemoryStorageTrieCursor}, + updates::StorageTrieUpdates, + walker::TrieWalker, + HashedStorage, +}; +use reth_trie_common::{HashBuilder, Nibbles}; +use reth_trie_sparse::SparseTrie; + +pub fn calculate_root_from_leaves(c: &mut Criterion) { + let mut group = c.benchmark_group("calculate root from leaves"); + group.sample_size(20); + + for size in [1_000, 5_000, 10_000, 100_000] { + let state = generate_test_data(size); + + // hash builder + group.bench_function(BenchmarkId::new("hash builder", size), |b| { + b.iter_with_setup(HashBuilder::default, |mut hb| { + for (key, value) in state.iter().sorted_by_key(|(key, _)| *key) { + hb.add_leaf(Nibbles::unpack(key), &alloy_rlp::encode_fixed_size(value)); + } + hb.root(); + }) + }); + + // sparse trie + group.bench_function(BenchmarkId::new("sparse trie", size), |b| { + b.iter_with_setup(SparseTrie::revealed_empty, |mut sparse| { + for (key, value) in &state { + sparse + .update_leaf( + Nibbles::unpack(key), + alloy_rlp::encode_fixed_size(value).to_vec(), + ) + .unwrap(); + } + sparse.root().unwrap(); + }) + }); + } +} + +pub fn calculate_root_from_leaves_repeated(c: &mut Criterion) { + let mut group = c.benchmark_group("calculate root from leaves repeated"); + group.sample_size(20); + + for init_size in [1_000, 10_000, 100_000] { + let init_state = generate_test_data(init_size); + + for update_size in [100, 1_000, 5_000, 10_000] { + for num_updates in [1, 3, 5, 10] { + let updates = + (0..num_updates).map(|_| generate_test_data(update_size)).collect::>(); + + // hash builder + let benchmark_id = BenchmarkId::new( + "hash builder", + format!("init size {init_size} | update size {update_size} | num updates {num_updates}"), + ); + group.bench_function(benchmark_id, |b| { + b.iter_with_setup( + || { + let init_storage = HashedStorage::from_iter(false, init_state.clone()); + let storage_updates = updates + .clone() + .into_iter() + .map(|update| HashedStorage::from_iter(false, update)) + .collect::>(); + + let mut hb = HashBuilder::default().with_updates(true); + for (key, value) in init_state.iter().sorted_by_key(|(key, _)| *key) { + hb.add_leaf( + Nibbles::unpack(key), + &alloy_rlp::encode_fixed_size(value), + ); + } + hb.root(); + + let (_, updates) = hb.split(); + let trie_updates = StorageTrieUpdates::new(updates); + (init_storage, storage_updates, trie_updates) + }, + |(init_storage, storage_updates, mut trie_updates)| { + let mut storage = init_storage; + let mut storage_updates = storage_updates.into_iter().peekable(); + while let Some(update) = storage_updates.next() { + storage.extend(&update); + + let prefix_set = update.construct_prefix_set().freeze(); + let (storage_sorted, trie_updates_sorted) = + if storage_updates.peek().is_some() { + ( + storage.clone().into_sorted(), + trie_updates.clone().into_sorted(), + ) + } else { + ( + std::mem::take(&mut storage).into_sorted(), + std::mem::take(&mut trie_updates).into_sorted(), + ) + }; + + let walker = TrieWalker::new( + InMemoryStorageTrieCursor::new( + B256::ZERO, + NoopStorageTrieCursor::default(), + Some(&trie_updates_sorted), + ), + prefix_set, + ); + let mut node_iter = TrieNodeIter::new( + walker, + HashedPostStateStorageCursor::new( + NoopHashedStorageCursor::default(), + Some(&storage_sorted), + ), + ); + + let mut hb = HashBuilder::default().with_updates(true); + while let Some(node) = node_iter.try_next().unwrap() { + match node { + TrieElement::Branch(node) => { + hb.add_branch( + node.key, + node.value, + node.children_are_in_trie, + ); + } + TrieElement::Leaf(hashed_slot, value) => { + hb.add_leaf( + Nibbles::unpack(hashed_slot), + alloy_rlp::encode_fixed_size(&value).as_ref(), + ); + } + } + } + hb.root(); + + if storage_updates.peek().is_some() { + trie_updates.finalize(node_iter.walker, hb); + } + } + }, + ) + }); + + // sparse trie + let benchmark_id = BenchmarkId::new( + "sparse trie", + format!("init size {init_size} | update size {update_size} | num updates {num_updates}"), + ); + group.bench_function(benchmark_id, |b| { + b.iter_with_setup( + || { + let mut sparse = SparseTrie::revealed_empty(); + for (key, value) in &init_state { + sparse + .update_leaf( + Nibbles::unpack(key), + alloy_rlp::encode_fixed_size(value).to_vec(), + ) + .unwrap(); + } + sparse.root().unwrap(); + sparse + }, + |mut sparse| { + for update in &updates { + for (key, value) in update { + sparse + .update_leaf( + Nibbles::unpack(key), + alloy_rlp::encode_fixed_size(value).to_vec(), + ) + .unwrap(); + } + sparse.root().unwrap(); + } + }, + ) + }); + } + } + } +} + +fn generate_test_data(size: usize) -> HashMap { + let mut runner = TestRunner::new(ProptestConfig::default()); + proptest::collection::hash_map(any::(), any::(), size) + .new_tree(&mut runner) + .unwrap() + .current() +} + +criterion_group!(root, calculate_root_from_leaves, calculate_root_from_leaves_repeated); +criterion_main!(root); diff --git a/crates/trie/sparse/src/errors.rs b/crates/trie/sparse/src/errors.rs new file mode 100644 index 000000000000..f60d1736c06f --- /dev/null +++ b/crates/trie/sparse/src/errors.rs @@ -0,0 +1,49 @@ +//! Errors for sparse trie. + +use alloy_primitives::{Bytes, B256}; +use reth_trie::Nibbles; +use thiserror::Error; + +/// Result type with [`SparseStateTrieError`] as error. +pub type SparseStateTrieResult = Result; + +/// Error encountered in [`crate::SparseStateTrie`]. +#[derive(Error, Debug)] +pub enum SparseStateTrieError { + /// Encountered invalid root node. + #[error("invalid root node at {path:?}: {node:?}")] + InvalidRootNode { + /// Path to first proof node. + path: Nibbles, + /// Encoded first proof node. + node: Bytes, + }, + /// Sparse trie error. + #[error(transparent)] + Sparse(#[from] SparseTrieError), + /// RLP error. + #[error(transparent)] + Rlp(#[from] alloy_rlp::Error), +} + +/// Result type with [`SparseTrieError`] as error. +pub type SparseTrieResult = Result; + +/// Error encountered in [`crate::SparseTrie`]. +#[derive(Error, Debug)] +pub enum SparseTrieError { + /// Sparse trie is still blind. Thrown on attempt to update it. + #[error("sparse trie is blind")] + Blind, + /// Encountered blinded node on update. + #[error("attempted to update blind node at {path:?}: {hash}")] + BlindedNode { + /// Blind node path. + path: Nibbles, + /// Node hash + hash: B256, + }, + /// RLP error. + #[error(transparent)] + Rlp(#[from] alloy_rlp::Error), +} diff --git a/crates/trie/sparse/src/lib.rs b/crates/trie/sparse/src/lib.rs index 5d3d4a5b6f8d..b3cb2c5fdffa 100644 --- a/crates/trie/sparse/src/lib.rs +++ b/crates/trie/sparse/src/lib.rs @@ -1 +1,10 @@ //! The implementation of sparse MPT. + +mod state; +pub use state::*; + +mod trie; +pub use trie::*; + +mod errors; +pub use errors::*; diff --git a/crates/trie/sparse/src/state.rs b/crates/trie/sparse/src/state.rs new file mode 100644 index 000000000000..cfb17ef36ff1 --- /dev/null +++ b/crates/trie/sparse/src/state.rs @@ -0,0 +1,131 @@ +use crate::{SparseStateTrieError, SparseStateTrieResult, SparseTrie}; +use alloy_primitives::{ + map::{HashMap, HashSet}, + Bytes, B256, +}; +use alloy_rlp::Decodable; +use reth_trie::{Nibbles, TrieNode}; + +/// Sparse state trie representing lazy-loaded Ethereum state trie. +#[derive(Default, Debug)] +pub struct SparseStateTrie { + /// Sparse account trie. + pub(crate) state: SparseTrie, + /// Sparse storage tries. + #[allow(dead_code)] + pub(crate) storages: HashMap, + /// Collection of revealed account and storage keys. + #[allow(dead_code)] + pub(crate) revealed: HashMap>, +} + +impl SparseStateTrie { + /// Create state trie from state trie. + pub fn from_state(state: SparseTrie) -> Self { + Self { state, ..Default::default() } + } + + /// Returns `true` if account was already revealed. + pub fn is_account_revealed(&self, account: &B256) -> bool { + self.revealed.contains_key(account) + } + + /// Returns `true` if storage slot for account was already revealed. + pub fn is_storage_slot_revealed(&self, account: &B256, slot: &B256) -> bool { + self.revealed.get(account).map_or(false, |slots| slots.contains(slot)) + } + + /// Reveal unknown trie paths from provided leaf path and its proof. + /// NOTE: This method does not extensively validate the proof. + pub fn reveal_account( + &mut self, + account: B256, + proof: impl IntoIterator, + ) -> SparseStateTrieResult<()> { + let mut proof = proof.into_iter().peekable(); + + // reveal root and initialize the trie if not already + let Some((path, node)) = proof.next() else { return Ok(()) }; + if !path.is_empty() { + return Err(SparseStateTrieError::InvalidRootNode { path, node }) + } + + // Decode root node and perform sanity check. + let root_node = TrieNode::decode(&mut &node[..])?; + if matches!(root_node, TrieNode::EmptyRoot) && proof.peek().is_some() { + return Err(SparseStateTrieError::InvalidRootNode { path, node }) + } + + // Reveal root node if it wasn't already. + let trie = self.state.reveal_root(root_node)?; + + // add the remaining proof nodes + for (path, bytes) in proof { + let node = TrieNode::decode(&mut &bytes[..])?; + trie.reveal_node(path, node)?; + } + + // Mark leaf path as revealed. + self.revealed.entry(account).or_default(); + + Ok(()) + } + + /// Update the leaf node. + pub fn update_leaf(&mut self, path: Nibbles, value: Vec) -> SparseStateTrieResult<()> { + self.state.update_leaf(path, value)?; + Ok(()) + } + + /// Returns sparse trie root if the trie has been revealed. + pub fn root(&mut self) -> Option { + self.state.root() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use alloy_primitives::Bytes; + use alloy_rlp::EMPTY_STRING_CODE; + use assert_matches::assert_matches; + use reth_trie::HashBuilder; + use reth_trie_common::proof::ProofRetainer; + + #[test] + fn sparse_trie_reveal_empty() { + let retainer = ProofRetainer::from_iter([Nibbles::default()]); + let mut hash_builder = HashBuilder::default().with_proof_retainer(retainer); + hash_builder.root(); + let proofs = hash_builder.take_proof_nodes(); + assert_eq!(proofs.len(), 1); + + let mut sparse = SparseStateTrie::default(); + assert_eq!(sparse.state, SparseTrie::Blind); + sparse.reveal_account(Default::default(), proofs.into_inner()).unwrap(); + assert_eq!(sparse.state, SparseTrie::revealed_empty()); + } + + #[test] + fn reveal_first_node_not_root() { + let mut sparse = SparseStateTrie::default(); + let proof = [(Nibbles::from_nibbles([0x1]), Bytes::from([EMPTY_STRING_CODE]))]; + assert_matches!( + sparse.reveal_account(Default::default(), proof), + Err(SparseStateTrieError::InvalidRootNode { .. }) + ); + } + + #[test] + fn reveal_invalid_proof_with_empty_root() { + let mut sparse = SparseStateTrie::default(); + let proof = [ + (Nibbles::default(), Bytes::from([EMPTY_STRING_CODE])), + (Nibbles::from_nibbles([0x1]), Bytes::new()), + ]; + assert_matches!( + sparse.reveal_account(Default::default(), proof), + Err(SparseStateTrieError::InvalidRootNode { .. }) + ); + } +} diff --git a/crates/trie/sparse/src/trie.rs b/crates/trie/sparse/src/trie.rs new file mode 100644 index 000000000000..0b9ffb5c0ed4 --- /dev/null +++ b/crates/trie/sparse/src/trie.rs @@ -0,0 +1,1230 @@ +use crate::{SparseTrieError, SparseTrieResult}; +use alloy_primitives::{hex, keccak256, map::HashMap, B256}; +use alloy_rlp::Decodable; +use reth_tracing::tracing::debug; +use reth_trie::{ + prefix_set::{PrefixSet, PrefixSetMut}, + RlpNode, +}; +use reth_trie_common::{ + BranchNodeRef, ExtensionNodeRef, LeafNodeRef, Nibbles, TrieMask, TrieNode, CHILD_INDEX_RANGE, + EMPTY_ROOT_HASH, +}; +use smallvec::SmallVec; +use std::{collections::HashSet, fmt}; + +/// Inner representation of the sparse trie. +/// Sparse trie is blind by default until nodes are revealed. +#[derive(PartialEq, Eq, Default, Debug)] +pub enum SparseTrie { + /// None of the trie nodes are known. + #[default] + Blind, + /// The trie nodes have been revealed. + Revealed(RevealedSparseTrie), +} + +impl SparseTrie { + /// Creates new revealed empty trie. + pub fn revealed_empty() -> Self { + Self::Revealed(RevealedSparseTrie::default()) + } + + /// Returns `true` if the sparse trie has no revealed nodes. + pub const fn is_blind(&self) -> bool { + matches!(self, Self::Blind) + } + + /// Returns mutable reference to revealed sparse trie if the trie is not blind. + pub fn as_revealed_mut(&mut self) -> Option<&mut RevealedSparseTrie> { + if let Self::Revealed(revealed) = self { + Some(revealed) + } else { + None + } + } + + /// Reveals the root node if the trie is blinded. + /// + /// # Returns + /// + /// Mutable reference to [`RevealedSparseTrie`]. + pub fn reveal_root(&mut self, root: TrieNode) -> SparseTrieResult<&mut RevealedSparseTrie> { + if self.is_blind() { + *self = Self::Revealed(RevealedSparseTrie::from_root(root)?) + } + Ok(self.as_revealed_mut().unwrap()) + } + + /// Update the leaf node. + pub fn update_leaf(&mut self, path: Nibbles, value: Vec) -> SparseTrieResult<()> { + let revealed = self.as_revealed_mut().ok_or(SparseTrieError::Blind)?; + revealed.update_leaf(path, value)?; + Ok(()) + } + + /// Calculates and returns the trie root if the trie has been revealed. + pub fn root(&mut self) -> Option { + Some(self.as_revealed_mut()?.root()) + } +} + +/// The representation of revealed sparse trie. +/// +/// ## Invariants +/// +/// - The root node is always present in `nodes` collection. +/// - Each leaf entry in `nodes` collection must have a corresponding entry in `values` collection. +/// The opposite is also true. +/// - All keys in `values` collection are full leaf paths. +#[derive(PartialEq, Eq)] +pub struct RevealedSparseTrie { + /// All trie nodes. + nodes: HashMap, + /// All leaf values. + values: HashMap>, + /// Prefix set. + prefix_set: PrefixSetMut, + /// Reusable buffer for RLP encoding of nodes. + rlp_buf: Vec, +} + +impl fmt::Debug for RevealedSparseTrie { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RevealedSparseTrie") + .field("nodes", &self.nodes) + .field("values", &self.values) + .field("prefix_set", &self.prefix_set) + .field("rlp_buf", &hex::encode(&self.rlp_buf)) + .finish() + } +} + +impl Default for RevealedSparseTrie { + fn default() -> Self { + Self { + nodes: HashMap::from_iter([(Nibbles::default(), SparseNode::Empty)]), + values: HashMap::default(), + prefix_set: PrefixSetMut::default(), + rlp_buf: Vec::new(), + } + } +} + +impl RevealedSparseTrie { + /// Create new revealed sparse trie from the given root node. + pub fn from_root(node: TrieNode) -> SparseTrieResult { + let mut this = Self { + nodes: HashMap::default(), + values: HashMap::default(), + prefix_set: PrefixSetMut::default(), + rlp_buf: Vec::new(), + }; + this.reveal_node(Nibbles::default(), node)?; + Ok(this) + } + + /// Reveal the trie node only if it was not known already. + pub fn reveal_node(&mut self, path: Nibbles, node: TrieNode) -> SparseTrieResult<()> { + // TODO: revise all inserts to not overwrite existing entries + match node { + TrieNode::EmptyRoot => { + debug_assert!(path.is_empty()); + self.nodes.insert(path, SparseNode::Empty); + } + TrieNode::Branch(branch) => { + let mut stack_ptr = branch.as_ref().first_child_index(); + for idx in CHILD_INDEX_RANGE { + if branch.state_mask.is_bit_set(idx) { + let mut child_path = path.clone(); + child_path.push_unchecked(idx); + self.reveal_node_or_hash(child_path, &branch.stack[stack_ptr])?; + stack_ptr += 1; + } + } + self.nodes + .insert(path, SparseNode::Branch { state_mask: branch.state_mask, hash: None }); + } + TrieNode::Extension(ext) => { + let mut child_path = path.clone(); + child_path.extend_from_slice_unchecked(&ext.key); + self.reveal_node_or_hash(child_path, &ext.child)?; + self.nodes.insert(path, SparseNode::Extension { key: ext.key, hash: None }); + } + TrieNode::Leaf(leaf) => { + let mut full = path.clone(); + full.extend_from_slice_unchecked(&leaf.key); + self.values.insert(full, leaf.value); + self.nodes.insert(path, SparseNode::new_leaf(leaf.key)); + } + } + + Ok(()) + } + + fn reveal_node_or_hash(&mut self, path: Nibbles, child: &[u8]) -> SparseTrieResult<()> { + if child.len() == B256::len_bytes() + 1 { + // TODO: revise insert to not overwrite existing entries + self.nodes.insert(path, SparseNode::Hash(B256::from_slice(&child[1..]))); + return Ok(()) + } + + self.reveal_node(path, TrieNode::decode(&mut &child[..])?) + } + + /// Update the leaf node with provided value. + pub fn update_leaf(&mut self, path: Nibbles, value: Vec) -> SparseTrieResult<()> { + self.prefix_set.insert(path.clone()); + let existing = self.values.insert(path.clone(), value); + if existing.is_some() { + // trie structure unchanged, return immediately + return Ok(()) + } + + let mut current = Nibbles::default(); + while let Some(node) = self.nodes.get_mut(¤t) { + match node { + SparseNode::Empty => { + *node = SparseNode::new_leaf(path); + break + } + SparseNode::Hash(hash) => { + return Err(SparseTrieError::BlindedNode { path: current, hash: *hash }) + } + SparseNode::Leaf { key: current_key, .. } => { + current.extend_from_slice_unchecked(current_key); + + // this leaf is being updated + if current == path { + unreachable!("we already checked leaf presence in the beginning"); + } + + // find the common prefix + let common = current.common_prefix_length(&path); + + // update existing node + let new_ext_key = current.slice(current.len() - current_key.len()..common); + *node = SparseNode::new_ext(new_ext_key); + + // create a branch node and corresponding leaves + self.nodes.insert( + current.slice(..common), + SparseNode::new_split_branch(current[common], path[common]), + ); + self.nodes.insert( + path.slice(..=common), + SparseNode::new_leaf(path.slice(common + 1..)), + ); + self.nodes.insert( + current.slice(..=common), + SparseNode::new_leaf(current.slice(common + 1..)), + ); + + break; + } + SparseNode::Extension { key, .. } => { + current.extend_from_slice(key); + if !path.starts_with(¤t) { + // find the common prefix + let common = current.common_prefix_length(&path); + + *key = current.slice(current.len() - key.len()..common); + + // create state mask for new branch node + // NOTE: this might overwrite the current extension node + let branch = SparseNode::new_split_branch(current[common], path[common]); + self.nodes.insert(current.slice(..common), branch); + + // create new leaf + let new_leaf = SparseNode::new_leaf(path.slice(common + 1..)); + self.nodes.insert(path.slice(..=common), new_leaf); + + // recreate extension to previous child if needed + let key = current.slice(common + 1..); + if !key.is_empty() { + self.nodes.insert(current.slice(..=common), SparseNode::new_ext(key)); + } + + break; + } + } + SparseNode::Branch { state_mask, .. } => { + let nibble = path[current.len()]; + current.push_unchecked(nibble); + if !state_mask.is_bit_set(nibble) { + state_mask.set_bit(nibble); + let new_leaf = SparseNode::new_leaf(path.slice(current.len()..)); + self.nodes.insert(current, new_leaf); + break; + } + } + }; + } + + Ok(()) + } + + /// Remove leaf node from the trie. + pub fn remove_leaf(&mut self, path: Nibbles) -> SparseTrieResult<()> { + self.prefix_set.insert(path.clone()); + let existing = self.values.remove(&path); + if existing.is_none() { + // trie structure unchanged, return immediately + return Ok(()) + } + + let mut removed_nodes = self.take_nodes_for_path(&path)?; + debug!(target: "trie::sparse", ?path, ?removed_nodes, "Removed nodes for path"); + // Pop the first node from the stack which is the leaf node we want to remove. + let mut child = removed_nodes.pop().expect("leaf exists"); + #[cfg(debug_assertions)] + { + let mut child_path = child.path.clone(); + let SparseNode::Leaf { key, .. } = &child.node else { panic!("expected leaf node") }; + child_path.extend_from_slice_unchecked(key); + assert_eq!(child_path, path); + } + + // If we don't have any other removed nodes, insert an empty node at the root. + if removed_nodes.is_empty() { + debug_assert!(self.nodes.is_empty()); + self.nodes.insert(Nibbles::default(), SparseNode::Empty); + + return Ok(()) + } + + // Walk the stack of removed nodes from the back and re-insert them back into the trie, + // adjusting the node type as needed. + while let Some(removed_node) = removed_nodes.pop() { + let removed_path = removed_node.path; + + let new_node = match &removed_node.node { + SparseNode::Empty => return Err(SparseTrieError::Blind), + SparseNode::Hash(hash) => { + return Err(SparseTrieError::BlindedNode { path: removed_path, hash: *hash }) + } + SparseNode::Leaf { .. } => { + unreachable!("we already popped the leaf node") + } + SparseNode::Extension { key, .. } => { + // If the node is an extension node, we need to look at its child to see if we + // need to merge them. + match &child.node { + SparseNode::Empty => return Err(SparseTrieError::Blind), + SparseNode::Hash(hash) => { + return Err(SparseTrieError::BlindedNode { + path: child.path, + hash: *hash, + }) + } + // For a leaf node, we collapse the extension node into a leaf node, + // extending the key. While it's impossible to encounter an extension node + // followed by a leaf node in a complete trie, it's possible here because we + // could have downgraded the extension node's child into a leaf node from + // another node type. + SparseNode::Leaf { key: leaf_key, .. } => { + self.nodes.remove(&child.path); + + let mut new_key = key.clone(); + new_key.extend_from_slice_unchecked(leaf_key); + SparseNode::new_leaf(new_key) + } + // For an extension node, we collapse them into one extension node, + // extending the key + SparseNode::Extension { key: extension_key, .. } => { + self.nodes.remove(&child.path); + + let mut new_key = key.clone(); + new_key.extend_from_slice_unchecked(extension_key); + SparseNode::new_ext(new_key) + } + // For a branch node, we just leave the extension node as-is. + SparseNode::Branch { .. } => removed_node.node, + } + } + SparseNode::Branch { mut state_mask, hash: _ } => { + // If the node is a branch node, we need to check the number of children left + // after deleting the child at the given nibble. + + if let Some(removed_nibble) = removed_node.unset_branch_nibble { + state_mask.unset_bit(removed_nibble); + } + + // If only one child is left set in the branch node, we need to collapse it. + if state_mask.count_bits() == 1 { + let child_nibble = + state_mask.first_set_bit_index().expect("state mask is not empty"); + + // Get full path of the only child node left. + let mut child_path = removed_path.clone(); + child_path.push_unchecked(child_nibble); + + // Remove the only child node. + let child = self.nodes.get(&child_path).unwrap(); + + debug!(target: "trie::sparse", ?removed_path, ?child_path, ?child, "Branch node has only one child"); + + let mut delete_child = false; + let new_node = match child { + SparseNode::Empty => return Err(SparseTrieError::Blind), + SparseNode::Hash(hash) => { + return Err(SparseTrieError::BlindedNode { + path: child_path, + hash: *hash, + }) + } + // If the only child is a leaf node, we downgrade the branch node into a + // leaf node, prepending the nibble to the key, and delete the old + // child. + SparseNode::Leaf { key, .. } => { + delete_child = true; + + let mut new_key = Nibbles::from_nibbles_unchecked([child_nibble]); + new_key.extend_from_slice_unchecked(key); + SparseNode::new_leaf(new_key) + } + // If the only child node is an extension node, we downgrade the branch + // node into an even longer extension node, prepending the nibble to the + // key, and delete the old child. + SparseNode::Extension { key, .. } => { + delete_child = true; + + let mut new_key = Nibbles::from_nibbles_unchecked([child_nibble]); + new_key.extend_from_slice_unchecked(key); + SparseNode::new_ext(new_key) + } + // If the only child is a branch node, we downgrade the current branch + // node into a one-nibble extension node. + SparseNode::Branch { .. } => { + SparseNode::new_ext(Nibbles::from_nibbles_unchecked([child_nibble])) + } + }; + + if delete_child { + self.nodes.remove(&child_path); + } + + new_node + } + // If more than one child is left set in the branch, we just re-insert it + // as-is. + else { + SparseNode::new_branch(state_mask) + } + } + }; + + child = RemovedSparseNode { + path: removed_path.clone(), + node: new_node.clone(), + unset_branch_nibble: None, + }; + debug!(target: "trie::sparse", ?removed_path, ?new_node, "Re-inserting the node"); + self.nodes.insert(removed_path, new_node); + } + + Ok(()) + } + + /// Traverse trie nodes down to the leaf node and collect all nodes along the path. + fn take_nodes_for_path(&mut self, path: &Nibbles) -> SparseTrieResult> { + let mut current = Nibbles::default(); // Start traversal from the root + let mut nodes = Vec::new(); // Collect traversed nodes + + while let Some(node) = self.nodes.remove(¤t) { + match &node { + SparseNode::Empty => return Err(SparseTrieError::Blind), + SparseNode::Hash(hash) => { + return Err(SparseTrieError::BlindedNode { path: current, hash: *hash }) + } + SparseNode::Leaf { key: _key, .. } => { + // Leaf node is always the one that we're deleting, and no other leaf nodes can + // be found during traversal. + + #[cfg(debug_assertions)] + { + let mut current = current.clone(); + current.extend_from_slice_unchecked(_key); + assert_eq!(¤t, path); + } + + nodes.push(RemovedSparseNode { + path: current.clone(), + node, + unset_branch_nibble: None, + }); + break + } + SparseNode::Extension { key, .. } => { + #[cfg(debug_assertions)] + { + let mut current = current.clone(); + current.extend_from_slice_unchecked(key); + assert!(path.starts_with(¤t)); + } + + let path = current.clone(); + current.extend_from_slice_unchecked(key); + nodes.push(RemovedSparseNode { path, node, unset_branch_nibble: None }); + } + SparseNode::Branch { state_mask, .. } => { + let nibble = path[current.len()]; + debug_assert!(state_mask.is_bit_set(nibble)); + + // If the branch node has a child that is a leaf node that we're removing, + // we need to unset this nibble. + // Any other branch nodes will not require unsetting the nibble, because + // deleting one leaf node can not remove the whole path + // where the branch node is located. + let mut child_path = + Nibbles::from_nibbles([current.as_slice(), &[nibble]].concat()); + let unset_branch_nibble = self + .nodes + .get(&child_path) + .map_or(false, move |node| match node { + SparseNode::Leaf { key, .. } => { + // Get full path of the leaf node + child_path.extend_from_slice_unchecked(key); + &child_path == path + } + _ => false, + }) + .then_some(nibble); + + nodes.push(RemovedSparseNode { + path: current.clone(), + node, + unset_branch_nibble, + }); + + current.push_unchecked(nibble); + } + } + } + + Ok(nodes) + } + + /// Return the root of the sparse trie. + /// Updates all remaining dirty nodes before calculating the root. + pub fn root(&mut self) -> B256 { + // take the current prefix set. + let mut prefix_set = std::mem::take(&mut self.prefix_set).freeze(); + let root_rlp = self.rlp_node(Nibbles::default(), &mut prefix_set); + if let Some(root_hash) = root_rlp.as_hash() { + root_hash + } else { + keccak256(root_rlp) + } + } + + /// Update node hashes only if their path exceeds the provided level. + pub fn update_rlp_node_level(&mut self, min_len: usize) { + let mut paths = Vec::from([Nibbles::default()]); + let mut targets = HashSet::::default(); + + while let Some(mut path) = paths.pop() { + match self.nodes.get(&path).unwrap() { + SparseNode::Empty | SparseNode::Hash(_) => {} + SparseNode::Leaf { .. } => { + targets.insert(path); + } + SparseNode::Extension { key, .. } => { + if path.len() >= min_len { + targets.insert(path); + } else { + path.extend_from_slice_unchecked(key); + paths.push(path); + } + } + SparseNode::Branch { state_mask, .. } => { + if path.len() >= min_len { + targets.insert(path); + } else { + for bit in CHILD_INDEX_RANGE { + if state_mask.is_bit_set(bit) { + let mut child_path = path.clone(); + child_path.push_unchecked(bit); + paths.push(child_path); + } + } + } + } + } + } + + let mut prefix_set = self.prefix_set.clone().freeze(); + for target in targets { + self.rlp_node(target, &mut prefix_set); + } + } + + fn rlp_node(&mut self, path: Nibbles, prefix_set: &mut PrefixSet) -> RlpNode { + // stack of paths we need rlp nodes for + let mut path_stack = Vec::from([path]); + // stack of rlp nodes + let mut rlp_node_stack = Vec::<(Nibbles, RlpNode)>::new(); + // reusable branch child path + let mut branch_child_buf = SmallVec::<[Nibbles; 16]>::new_const(); + // reusable branch value stack + let mut branch_value_stack_buf = SmallVec::<[RlpNode; 16]>::new_const(); + + 'main: while let Some(path) = path_stack.pop() { + let rlp_node = match self.nodes.get_mut(&path).unwrap() { + SparseNode::Empty => RlpNode::word_rlp(&EMPTY_ROOT_HASH), + SparseNode::Hash(hash) => RlpNode::word_rlp(hash), + SparseNode::Leaf { key, hash } => { + self.rlp_buf.clear(); + let mut path = path.clone(); + path.extend_from_slice_unchecked(key); + if let Some(hash) = hash.filter(|_| !prefix_set.contains(&path)) { + RlpNode::word_rlp(&hash) + } else { + let value = self.values.get(&path).unwrap(); + let rlp_node = LeafNodeRef { key, value }.rlp(&mut self.rlp_buf); + *hash = rlp_node.as_hash(); + rlp_node + } + } + SparseNode::Extension { key, hash } => { + let mut child_path = path.clone(); + child_path.extend_from_slice_unchecked(key); + if let Some(hash) = hash.filter(|_| !prefix_set.contains(&path)) { + RlpNode::word_rlp(&hash) + } else if rlp_node_stack.last().map_or(false, |e| e.0 == child_path) { + let (_, child) = rlp_node_stack.pop().unwrap(); + self.rlp_buf.clear(); + let rlp_node = ExtensionNodeRef::new(key, &child).rlp(&mut self.rlp_buf); + *hash = rlp_node.as_hash(); + rlp_node + } else { + path_stack.extend([path, child_path]); // need to get rlp node for child first + continue + } + } + SparseNode::Branch { state_mask, hash } => { + if let Some(hash) = hash.filter(|_| !prefix_set.contains(&path)) { + rlp_node_stack.push((path, RlpNode::word_rlp(&hash))); + continue + } + + branch_child_buf.clear(); + for bit in CHILD_INDEX_RANGE { + if state_mask.is_bit_set(bit) { + let mut child = path.clone(); + child.push_unchecked(bit); + branch_child_buf.push(child); + } + } + + branch_value_stack_buf.clear(); + for child_path in &branch_child_buf { + if rlp_node_stack.last().map_or(false, |e| &e.0 == child_path) { + let (_, child) = rlp_node_stack.pop().unwrap(); + branch_value_stack_buf.push(child); + } else { + debug_assert!(branch_value_stack_buf.is_empty()); + path_stack.push(path); + path_stack.extend(branch_child_buf.drain(..)); + continue 'main + } + } + + self.rlp_buf.clear(); + let rlp_node = BranchNodeRef::new(&branch_value_stack_buf, *state_mask) + .rlp(&mut self.rlp_buf); + *hash = rlp_node.as_hash(); + rlp_node + } + }; + rlp_node_stack.push((path, rlp_node)); + } + + rlp_node_stack.pop().unwrap().1 + } +} + +/// Enum representing trie nodes in sparse trie. +#[derive(PartialEq, Eq, Clone, Debug)] +pub enum SparseNode { + /// Empty trie node. + Empty, + /// The hash of the node that was not revealed. + Hash(B256), + /// Sparse leaf node with remaining key suffix. + Leaf { + /// Remaining key suffix for the leaf node. + key: Nibbles, + /// Pre-computed hash of the sparse node. + /// Can be reused unless this trie path has been updated. + hash: Option, + }, + /// Sparse extension node with key. + Extension { + /// The key slice stored by this extension node. + key: Nibbles, + /// Pre-computed hash of the sparse node. + /// Can be reused unless this trie path has been updated. + hash: Option, + }, + /// Sparse branch node with state mask. + Branch { + /// The bitmask representing children present in the branch node. + state_mask: TrieMask, + /// Pre-computed hash of the sparse node. + /// Can be reused unless this trie path has been updated. + hash: Option, + }, +} + +impl SparseNode { + /// Create new sparse node from [`TrieNode`]. + pub fn from_node(node: TrieNode) -> Self { + match node { + TrieNode::EmptyRoot => Self::Empty, + TrieNode::Leaf(leaf) => Self::new_leaf(leaf.key), + TrieNode::Extension(ext) => Self::new_ext(ext.key), + TrieNode::Branch(branch) => Self::new_branch(branch.state_mask), + } + } + + /// Create new [`SparseNode::Branch`] from state mask. + pub const fn new_branch(state_mask: TrieMask) -> Self { + Self::Branch { state_mask, hash: None } + } + + /// Create new [`SparseNode::Branch`] with two bits set. + pub const fn new_split_branch(bit_a: u8, bit_b: u8) -> Self { + let state_mask = TrieMask::new( + // set bits for both children + (1u16 << bit_a) | (1u16 << bit_b), + ); + Self::Branch { state_mask, hash: None } + } + + /// Create new [`SparseNode::Extension`] from the key slice. + pub const fn new_ext(key: Nibbles) -> Self { + Self::Extension { key, hash: None } + } + + /// Create new [`SparseNode::Leaf`] from leaf key and value. + pub const fn new_leaf(key: Nibbles) -> Self { + Self::Leaf { key, hash: None } + } +} + +#[derive(Debug)] +struct RemovedSparseNode { + path: Nibbles, + node: SparseNode, + unset_branch_nibble: Option, +} + +#[cfg(test)] +mod tests { + use std::collections::BTreeMap; + + use super::*; + use alloy_primitives::U256; + use itertools::Itertools; + use proptest::prelude::*; + use rand::seq::IteratorRandom; + use reth_testing_utils::generators; + use reth_trie::{BranchNode, ExtensionNode, LeafNode}; + use reth_trie_common::{ + proof::{ProofNodes, ProofRetainer}, + HashBuilder, + }; + + /// Calculate the state root by feeding the provided state to the hash builder and retaining the + /// proofs for the provided targets. + /// + /// Returns the state root and the retained proof nodes. + fn hash_builder_root_with_proofs>( + state: impl IntoIterator, + proof_targets: impl IntoIterator, + ) -> (B256, ProofNodes) { + let mut hash_builder = + HashBuilder::default().with_proof_retainer(ProofRetainer::from_iter(proof_targets)); + for (key, value) in state { + hash_builder.add_leaf(key, value.as_ref()); + } + (hash_builder.root(), hash_builder.take_proof_nodes()) + } + + /// Assert that the sparse trie nodes and the proof nodes from the hash builder are equal. + fn assert_eq_sparse_trie_proof_nodes( + sparse_trie: &RevealedSparseTrie, + proof_nodes: ProofNodes, + ) { + let proof_nodes = proof_nodes + .into_nodes_sorted() + .into_iter() + .map(|(path, node)| (path, TrieNode::decode(&mut node.as_ref()).unwrap())); + + let sparse_nodes = sparse_trie.nodes.iter().sorted_by_key(|(path, _)| *path); + + for ((proof_node_path, proof_node), (sparse_node_path, sparse_node)) in + proof_nodes.zip(sparse_nodes) + { + assert_eq!(&proof_node_path, sparse_node_path); + + let equals = match (&proof_node, &sparse_node) { + // Both nodes are empty + (TrieNode::EmptyRoot, SparseNode::Empty) => true, + // Both nodes are branches and have the same state mask + ( + TrieNode::Branch(BranchNode { state_mask: proof_state_mask, .. }), + SparseNode::Branch { state_mask: sparse_state_mask, .. }, + ) => proof_state_mask == sparse_state_mask, + // Both nodes are extensions and have the same key + ( + TrieNode::Extension(ExtensionNode { key: proof_key, .. }), + SparseNode::Extension { key: sparse_key, .. }, + ) | + // Both nodes are leaves and have the same key + ( + TrieNode::Leaf(LeafNode { key: proof_key, .. }), + SparseNode::Leaf { key: sparse_key, .. }, + ) => proof_key == sparse_key, + // Empty and hash nodes are specific to the sparse trie, skip them + (_, SparseNode::Empty | SparseNode::Hash(_)) => continue, + _ => false, + }; + assert!(equals, "proof node: {:?}, sparse node: {:?}", proof_node, sparse_node); + } + } + + #[test] + fn sparse_trie_is_blind() { + assert!(SparseTrie::default().is_blind()); + assert!(!SparseTrie::revealed_empty().is_blind()); + } + + #[test] + fn sparse_trie_empty_update_one() { + let path = Nibbles::unpack(B256::with_last_byte(42)); + let value = alloy_rlp::encode_fixed_size(&U256::from(1)); + + let (hash_builder_root, hash_builder_proof_nodes) = + hash_builder_root_with_proofs([(path.clone(), &value)], [path.clone()]); + + let mut sparse = RevealedSparseTrie::default(); + sparse.update_leaf(path, value.to_vec()).unwrap(); + let sparse_root = sparse.root(); + + assert_eq!(sparse_root, hash_builder_root); + assert_eq_sparse_trie_proof_nodes(&sparse, hash_builder_proof_nodes); + } + + #[test] + fn sparse_trie_empty_update_multiple_lower_nibbles() { + let paths = (0..=16).map(|b| Nibbles::unpack(B256::with_last_byte(b))).collect::>(); + let value = alloy_rlp::encode_fixed_size(&U256::from(1)); + + let (hash_builder_root, hash_builder_proof_nodes) = hash_builder_root_with_proofs( + paths.iter().cloned().zip(std::iter::repeat_with(|| value.clone())), + paths.clone(), + ); + + let mut sparse = RevealedSparseTrie::default(); + for path in &paths { + sparse.update_leaf(path.clone(), value.to_vec()).unwrap(); + } + let sparse_root = sparse.root(); + + assert_eq!(sparse_root, hash_builder_root); + assert_eq_sparse_trie_proof_nodes(&sparse, hash_builder_proof_nodes); + } + + #[test] + fn sparse_trie_empty_update_multiple_upper_nibbles() { + let paths = (239..=255).map(|b| Nibbles::unpack(B256::repeat_byte(b))).collect::>(); + let value = alloy_rlp::encode_fixed_size(&U256::from(1)); + + let (hash_builder_root, hash_builder_proof_nodes) = hash_builder_root_with_proofs( + paths.iter().cloned().zip(std::iter::repeat_with(|| value.clone())), + paths.clone(), + ); + + let mut sparse = RevealedSparseTrie::default(); + for path in &paths { + sparse.update_leaf(path.clone(), value.to_vec()).unwrap(); + } + let sparse_root = sparse.root(); + + assert_eq!(sparse_root, hash_builder_root); + assert_eq_sparse_trie_proof_nodes(&sparse, hash_builder_proof_nodes); + } + + #[test] + fn sparse_trie_empty_update_multiple() { + let paths = (0..=255) + .map(|b| { + Nibbles::unpack(if b % 2 == 0 { + B256::repeat_byte(b) + } else { + B256::with_last_byte(b) + }) + }) + .collect::>(); + let value = alloy_rlp::encode_fixed_size(&U256::from(1)); + + let (hash_builder_root, hash_builder_proof_nodes) = hash_builder_root_with_proofs( + paths.iter().sorted_unstable().cloned().zip(std::iter::repeat_with(|| value.clone())), + paths.clone(), + ); + + let mut sparse = RevealedSparseTrie::default(); + for path in &paths { + sparse.update_leaf(path.clone(), value.to_vec()).unwrap(); + } + let sparse_root = sparse.root(); + + assert_eq!(sparse_root, hash_builder_root); + assert_eq_sparse_trie_proof_nodes(&sparse, hash_builder_proof_nodes); + } + + #[test] + fn sparse_trie_empty_update_repeated() { + let paths = (0..=255).map(|b| Nibbles::unpack(B256::repeat_byte(b))).collect::>(); + let old_value = alloy_rlp::encode_fixed_size(&U256::from(1)); + let new_value = alloy_rlp::encode_fixed_size(&U256::from(2)); + + let (hash_builder_root, hash_builder_proof_nodes) = hash_builder_root_with_proofs( + paths.iter().cloned().zip(std::iter::repeat_with(|| old_value.clone())), + paths.clone(), + ); + + let mut sparse = RevealedSparseTrie::default(); + for path in &paths { + sparse.update_leaf(path.clone(), old_value.to_vec()).unwrap(); + } + let sparse_root = sparse.root(); + + assert_eq!(sparse_root, hash_builder_root); + assert_eq_sparse_trie_proof_nodes(&sparse, hash_builder_proof_nodes); + + let (hash_builder_root, hash_builder_proof_nodes) = hash_builder_root_with_proofs( + paths.iter().cloned().zip(std::iter::repeat_with(|| new_value.clone())), + paths.clone(), + ); + + for path in &paths { + sparse.update_leaf(path.clone(), new_value.to_vec()).unwrap(); + } + let sparse_root = sparse.root(); + + assert_eq!(sparse_root, hash_builder_root); + assert_eq_sparse_trie_proof_nodes(&sparse, hash_builder_proof_nodes); + } + + #[test] + fn sparse_trie_remove_leaf() { + reth_tracing::init_test_tracing(); + + let mut sparse = RevealedSparseTrie::default(); + + let value = alloy_rlp::encode_fixed_size(&U256::ZERO).to_vec(); + + sparse + .update_leaf(Nibbles::from_nibbles([0x5, 0x0, 0x2, 0x3, 0x1]), value.clone()) + .unwrap(); + sparse + .update_leaf(Nibbles::from_nibbles([0x5, 0x0, 0x2, 0x3, 0x3]), value.clone()) + .unwrap(); + sparse + .update_leaf(Nibbles::from_nibbles([0x5, 0x2, 0x0, 0x1, 0x3]), value.clone()) + .unwrap(); + sparse + .update_leaf(Nibbles::from_nibbles([0x5, 0x3, 0x1, 0x0, 0x2]), value.clone()) + .unwrap(); + sparse + .update_leaf(Nibbles::from_nibbles([0x5, 0x3, 0x3, 0x0, 0x2]), value.clone()) + .unwrap(); + sparse.update_leaf(Nibbles::from_nibbles([0x5, 0x3, 0x3, 0x2, 0x0]), value).unwrap(); + + // Extension (Key = 5) + // └── Branch (Mask = 1011) + // ├── 0 -> Extension (Key = 23) + // │ └── Branch (Mask = 0101) + // │ ├── 1 -> Leaf (Key = 1, Path = 50231) + // │ └── 3 -> Leaf (Key = 3, Path = 50233) + // ├── 2 -> Leaf (Key = 013, Path = 52013) + // └── 3 -> Branch (Mask = 0101) + // ├── 1 -> Leaf (Key = 3102, Path = 53102) + // └── 3 -> Branch (Mask = 1010) + // ├── 0 -> Leaf (Key = 3302, Path = 53302) + // └── 2 -> Leaf (Key = 3320, Path = 53320) + pretty_assertions::assert_eq!( + sparse.nodes.clone().into_iter().collect::>(), + BTreeMap::from_iter([ + (Nibbles::new(), SparseNode::new_ext(Nibbles::from_nibbles([0x5]))), + (Nibbles::from_nibbles([0x5]), SparseNode::new_branch(0b1101.into())), + ( + Nibbles::from_nibbles([0x5, 0x0]), + SparseNode::new_ext(Nibbles::from_nibbles([0x2, 0x3])) + ), + ( + Nibbles::from_nibbles([0x5, 0x0, 0x2, 0x3]), + SparseNode::new_branch(0b1010.into()) + ), + ( + Nibbles::from_nibbles([0x5, 0x0, 0x2, 0x3, 0x1]), + SparseNode::new_leaf(Nibbles::new()) + ), + ( + Nibbles::from_nibbles([0x5, 0x0, 0x2, 0x3, 0x3]), + SparseNode::new_leaf(Nibbles::new()) + ), + ( + Nibbles::from_nibbles([0x5, 0x2]), + SparseNode::new_leaf(Nibbles::from_nibbles([0x0, 0x1, 0x3])) + ), + (Nibbles::from_nibbles([0x5, 0x3]), SparseNode::new_branch(0b1010.into())), + ( + Nibbles::from_nibbles([0x5, 0x3, 0x1]), + SparseNode::new_leaf(Nibbles::from_nibbles([0x0, 0x2])) + ), + (Nibbles::from_nibbles([0x5, 0x3, 0x3]), SparseNode::new_branch(0b0101.into())), + ( + Nibbles::from_nibbles([0x5, 0x3, 0x3, 0x0]), + SparseNode::new_leaf(Nibbles::from_nibbles([0x2])) + ), + ( + Nibbles::from_nibbles([0x5, 0x3, 0x3, 0x2]), + SparseNode::new_leaf(Nibbles::from_nibbles([0x0])) + ) + ]) + ); + + sparse.remove_leaf(Nibbles::from_nibbles([0x5, 0x2, 0x0, 0x1, 0x3])).unwrap(); + + // Extension (Key = 5) + // └── Branch (Mask = 1001) + // ├── 0 -> Extension (Key = 23) + // │ └── Branch (Mask = 0101) + // │ ├── 1 -> Leaf (Key = 0231, Path = 50231) + // │ └── 3 -> Leaf (Key = 0233, Path = 50233) + // └── 3 -> Branch (Mask = 0101) + // ├── 1 -> Leaf (Key = 3102, Path = 53102) + // └── 3 -> Branch (Mask = 1010) + // ├── 0 -> Leaf (Key = 3302, Path = 53302) + // └── 2 -> Leaf (Key = 3320, Path = 53320) + pretty_assertions::assert_eq!( + sparse.nodes.clone().into_iter().collect::>(), + BTreeMap::from_iter([ + (Nibbles::new(), SparseNode::new_ext(Nibbles::from_nibbles([0x5]))), + (Nibbles::from_nibbles([0x5]), SparseNode::new_branch(0b1001.into())), + ( + Nibbles::from_nibbles([0x5, 0x0]), + SparseNode::new_ext(Nibbles::from_nibbles([0x2, 0x3])) + ), + ( + Nibbles::from_nibbles([0x5, 0x0, 0x2, 0x3]), + SparseNode::new_branch(0b1010.into()) + ), + ( + Nibbles::from_nibbles([0x5, 0x0, 0x2, 0x3, 0x1]), + SparseNode::new_leaf(Nibbles::new()) + ), + ( + Nibbles::from_nibbles([0x5, 0x0, 0x2, 0x3, 0x3]), + SparseNode::new_leaf(Nibbles::new()) + ), + (Nibbles::from_nibbles([0x5, 0x3]), SparseNode::new_branch(0b1010.into())), + ( + Nibbles::from_nibbles([0x5, 0x3, 0x1]), + SparseNode::new_leaf(Nibbles::from_nibbles([0x0, 0x2])) + ), + (Nibbles::from_nibbles([0x5, 0x3, 0x3]), SparseNode::new_branch(0b0101.into())), + ( + Nibbles::from_nibbles([0x5, 0x3, 0x3, 0x0]), + SparseNode::new_leaf(Nibbles::from_nibbles([0x2])) + ), + ( + Nibbles::from_nibbles([0x5, 0x3, 0x3, 0x2]), + SparseNode::new_leaf(Nibbles::from_nibbles([0x0])) + ) + ]) + ); + + sparse.remove_leaf(Nibbles::from_nibbles([0x5, 0x0, 0x2, 0x3, 0x1])).unwrap(); + + // Extension (Key = 5) + // └── Branch (Mask = 1001) + // ├── 0 -> Leaf (Key = 0233, Path = 50233) + // └── 3 -> Branch (Mask = 0101) + // ├── 1 -> Leaf (Key = 3102, Path = 53102) + // └── 3 -> Branch (Mask = 1010) + // ├── 0 -> Leaf (Key = 3302, Path = 53302) + // └── 2 -> Leaf (Key = 3320, Path = 53320) + pretty_assertions::assert_eq!( + sparse.nodes.clone().into_iter().collect::>(), + BTreeMap::from_iter([ + (Nibbles::new(), SparseNode::new_ext(Nibbles::from_nibbles([0x5]))), + (Nibbles::from_nibbles([0x5]), SparseNode::new_branch(0b1001.into())), + ( + Nibbles::from_nibbles([0x5, 0x0]), + SparseNode::new_leaf(Nibbles::from_nibbles([0x2, 0x3, 0x3])) + ), + (Nibbles::from_nibbles([0x5, 0x3]), SparseNode::new_branch(0b1010.into())), + ( + Nibbles::from_nibbles([0x5, 0x3, 0x1]), + SparseNode::new_leaf(Nibbles::from_nibbles([0x0, 0x2])) + ), + (Nibbles::from_nibbles([0x5, 0x3, 0x3]), SparseNode::new_branch(0b0101.into())), + ( + Nibbles::from_nibbles([0x5, 0x3, 0x3, 0x0]), + SparseNode::new_leaf(Nibbles::from_nibbles([0x2])) + ), + ( + Nibbles::from_nibbles([0x5, 0x3, 0x3, 0x2]), + SparseNode::new_leaf(Nibbles::from_nibbles([0x0])) + ) + ]) + ); + + sparse.remove_leaf(Nibbles::from_nibbles([0x5, 0x3, 0x1, 0x0, 0x2])).unwrap(); + + // Extension (Key = 5) + // └── Branch (Mask = 1001) + // ├── 0 -> Leaf (Key = 0233, Path = 50233) + // └── 3 -> Branch (Mask = 1010) + // ├── 0 -> Leaf (Key = 3302, Path = 53302) + // └── 2 -> Leaf (Key = 3320, Path = 53320) + pretty_assertions::assert_eq!( + sparse.nodes.clone().into_iter().collect::>(), + BTreeMap::from_iter([ + (Nibbles::new(), SparseNode::new_ext(Nibbles::from_nibbles([0x5]))), + (Nibbles::from_nibbles([0x5]), SparseNode::new_branch(0b1001.into())), + ( + Nibbles::from_nibbles([0x5, 0x0]), + SparseNode::new_leaf(Nibbles::from_nibbles([0x2, 0x3, 0x3])) + ), + ( + Nibbles::from_nibbles([0x5, 0x3]), + SparseNode::new_ext(Nibbles::from_nibbles([0x3])) + ), + (Nibbles::from_nibbles([0x5, 0x3, 0x3]), SparseNode::new_branch(0b0101.into())), + ( + Nibbles::from_nibbles([0x5, 0x3, 0x3, 0x0]), + SparseNode::new_leaf(Nibbles::from_nibbles([0x2])) + ), + ( + Nibbles::from_nibbles([0x5, 0x3, 0x3, 0x2]), + SparseNode::new_leaf(Nibbles::from_nibbles([0x0])) + ) + ]) + ); + + sparse.remove_leaf(Nibbles::from_nibbles([0x5, 0x3, 0x3, 0x2, 0x0])).unwrap(); + + // Extension (Key = 5) + // └── Branch (Mask = 1001) + // ├── 0 -> Leaf (Key = 0233, Path = 50233) + // └── 3 -> Leaf (Key = 3302, Path = 53302) + pretty_assertions::assert_eq!( + sparse.nodes.clone().into_iter().collect::>(), + BTreeMap::from_iter([ + (Nibbles::new(), SparseNode::new_ext(Nibbles::from_nibbles([0x5]))), + (Nibbles::from_nibbles([0x5]), SparseNode::new_branch(0b1001.into())), + ( + Nibbles::from_nibbles([0x5, 0x0]), + SparseNode::new_leaf(Nibbles::from_nibbles([0x2, 0x3, 0x3])) + ), + ( + Nibbles::from_nibbles([0x5, 0x3]), + SparseNode::new_leaf(Nibbles::from_nibbles([0x3, 0x0, 0x2])) + ), + ]) + ); + + sparse.remove_leaf(Nibbles::from_nibbles([0x5, 0x0, 0x2, 0x3, 0x3])).unwrap(); + + // Leaf (Key = 53302) + pretty_assertions::assert_eq!( + sparse.nodes.clone().into_iter().collect::>(), + BTreeMap::from_iter([( + Nibbles::new(), + SparseNode::new_leaf(Nibbles::from_nibbles([0x5, 0x3, 0x3, 0x0, 0x2])) + ),]) + ); + + sparse.remove_leaf(Nibbles::from_nibbles([0x5, 0x3, 0x3, 0x0, 0x2])).unwrap(); + + // Empty + pretty_assertions::assert_eq!( + sparse.nodes.clone().into_iter().collect::>(), + BTreeMap::from_iter([(Nibbles::new(), SparseNode::Empty),]) + ); + } + + #[test] + fn sparse_trie_fuzz() { + proptest!(ProptestConfig::with_cases(10), |(updates: Vec>)| { + let mut rng = generators::rng(); + + let mut state = BTreeMap::default(); + let mut unpacked_state = BTreeMap::default(); + let mut sparse = RevealedSparseTrie::default(); + + for update in updates { + let keys_to_delete_len = update.len() / 2; + + let unpacked_update = update.iter().map(|(key, value)| ( + Nibbles::unpack(key), + alloy_rlp::encode_fixed_size(value).to_vec() + )); + + // Insert state updates into the sparse trie and calculate the root + for (key, value) in unpacked_update.clone() { + sparse.update_leaf(key, value).unwrap(); + } + let sparse_root = sparse.root(); + + // Insert state updates into the hash builder and calculate the root + unpacked_state.extend(unpacked_update); + state.extend(update); + let keys = state.keys().map(Nibbles::unpack).collect::>(); + let (hash_builder_root, hash_builder_proof_nodes) = hash_builder_root_with_proofs( + unpacked_state.clone(), + keys, + ); + + // Assert that the sparse trie root matches the hash builder root + assert_eq!(sparse_root, hash_builder_root); + // Assert that the sparse trie nodes match the hash builder proof nodes + assert_eq_sparse_trie_proof_nodes(&sparse, hash_builder_proof_nodes); + + // Delete some keys from both the hash builder and the sparse trie and check + // that the sparse trie root still matches the hash builder root + + let keys_to_delete = state + .keys() + .choose_multiple(&mut rng, keys_to_delete_len) + .into_iter() + .copied() + .collect::>(); + for key in keys_to_delete { + state.remove(&key).unwrap(); + unpacked_state.remove(&Nibbles::unpack(key)).unwrap(); + sparse.remove_leaf(Nibbles::unpack(key)).unwrap(); + } + + let sparse_root = sparse.root(); + + let keys = state.keys().map(Nibbles::unpack).collect::>(); + let (hash_builder_root, hash_builder_proof_nodes) = hash_builder_root_with_proofs( + unpacked_state.clone(), + keys, + ); + + // Assert that the sparse trie root matches the hash builder root + assert_eq!(sparse_root, hash_builder_root); + // Assert that the sparse trie nodes match the hash builder proof nodes + assert_eq_sparse_trie_proof_nodes(&sparse, hash_builder_proof_nodes); + } + }); + } +} diff --git a/crates/trie/trie/Cargo.toml b/crates/trie/trie/Cargo.toml index d0f0fa092a77..112e661c0278 100644 --- a/crates/trie/trie/Cargo.toml +++ b/crates/trie/trie/Cargo.toml @@ -24,13 +24,13 @@ revm.workspace = true # alloy alloy-rlp.workspace = true alloy-primitives.workspace = true +alloy-consensus.workspace = true # tracing tracing.workspace = true # misc rayon.workspace = true -derive_more.workspace = true auto_impl.workspace = true itertools.workspace = true @@ -49,7 +49,6 @@ serde_with = { workspace = true, optional = true } [dev-dependencies] # reth -reth-chainspec.workspace = true reth-primitives = { workspace = true, features = ["test-utils", "arbitrary"] } reth-trie-common = { workspace = true, features = ["test-utils", "arbitrary"] } @@ -59,20 +58,29 @@ triehash = "0.8" # misc proptest.workspace = true proptest-arbitrary-interop.workspace = true -tokio = { workspace = true, default-features = false, features = [ - "sync", - "rt", - "macros", -] } 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"] +serde = [ + "dep:serde", + "alloy-consensus/serde", + "alloy-primitives/serde", + "revm/serde" +] +serde-bincode-compat = [ + "serde_with", + "reth-primitives/serde-bincode-compat", + "alloy-consensus/serde-bincode-compat" +] +test-utils = [ + "triehash", + "reth-trie-common/test-utils", + "reth-primitives/test-utils", + "revm/test-utils" +] [[bench]] name = "prefix_set" diff --git a/crates/trie/trie/src/prefix_set.rs b/crates/trie/trie/src/prefix_set.rs index af0fb173d98a..da912fbbdad9 100644 --- a/crates/trie/trie/src/prefix_set.rs +++ b/crates/trie/trie/src/prefix_set.rs @@ -82,7 +82,7 @@ pub struct TriePrefixSets { /// assert!(prefix_set.contains(&[0xa, 0xb])); /// assert!(prefix_set.contains(&[0xa, 0xb, 0xc])); /// ``` -#[derive(Clone, Default, Debug)] +#[derive(PartialEq, Eq, Clone, Default, Debug)] pub struct PrefixSetMut { /// Flag indicating that any entry should be considered changed. /// If set, the keys will be discarded. diff --git a/crates/trie/trie/src/trie.rs b/crates/trie/trie/src/trie.rs index b8aa133d6fa3..1bf8cf1ce797 100644 --- a/crates/trie/trie/src/trie.rs +++ b/crates/trie/trie/src/trie.rs @@ -9,10 +9,10 @@ use crate::{ walker::TrieWalker, HashBuilder, Nibbles, TrieAccount, }; +use alloy_consensus::EMPTY_ROOT_HASH; use alloy_primitives::{keccak256, Address, B256}; use alloy_rlp::{BufMut, Encodable}; use reth_execution_errors::{StateRootError, StorageRootError}; -use reth_primitives::constants::EMPTY_ROOT_HASH; use tracing::trace; #[cfg(feature = "metrics")] diff --git a/crates/trie/trie/src/witness.rs b/crates/trie/trie/src/witness.rs index 971f10cfbae1..39d82a7bda7a 100644 --- a/crates/trie/trie/src/witness.rs +++ b/crates/trie/trie/src/witness.rs @@ -5,6 +5,7 @@ use crate::{ trie_cursor::TrieCursorFactory, HashedPostState, }; +use alloy_consensus::EMPTY_ROOT_HASH; use alloy_primitives::{ keccak256, map::{HashMap, HashSet}, @@ -13,7 +14,6 @@ use alloy_primitives::{ use alloy_rlp::{BufMut, Decodable, Encodable}; use itertools::{Either, Itertools}; use reth_execution_errors::{StateProofError, TrieWitnessError}; -use reth_primitives::constants::EMPTY_ROOT_HASH; use reth_trie_common::{ BranchNode, HashBuilder, Nibbles, StorageMultiProof, TrieAccount, TrieNode, CHILD_INDEX_RANGE, }; @@ -216,9 +216,14 @@ where TrieNode::Branch(branch) => { next_path.push(key[path.len()]); let children = branch_node_children(path.clone(), &branch); - for (child_path, node_hash) in children { + for (child_path, value) in children { if !key.starts_with(&child_path) { - trie_nodes.insert(child_path, Either::Left(node_hash)); + let value = if value.len() < B256::len_bytes() { + Either::Right(value.to_vec()) + } else { + Either::Left(B256::from_slice(&value[1..])) + }; + trie_nodes.insert(child_path, value); } } } @@ -312,8 +317,13 @@ where match TrieNode::decode(&mut &node[..])? { TrieNode::Branch(branch) => { let children = branch_node_children(path, &branch); - for (child_path, branch_hash) in children { - hash_builder.add_branch(child_path, branch_hash, false); + for (child_path, value) in children { + if value.len() < B256::len_bytes() { + hash_builder.add_leaf(child_path, value); + } else { + let hash = B256::from_slice(&value[1..]); + hash_builder.add_branch(child_path, hash, false); + } } break } @@ -343,14 +353,14 @@ where } /// Returned branch node children with keys in order. -fn branch_node_children(prefix: Nibbles, node: &BranchNode) -> Vec<(Nibbles, B256)> { +fn branch_node_children(prefix: Nibbles, node: &BranchNode) -> Vec<(Nibbles, &[u8])> { let mut children = Vec::with_capacity(node.state_mask.count_ones() as usize); let mut stack_ptr = node.as_ref().first_child_index(); for index in CHILD_INDEX_RANGE { if node.state_mask.is_bit_set(index) { let mut child_path = prefix.clone(); child_path.push(index); - children.push((child_path, B256::from_slice(&node.stack[stack_ptr][1..]))); + children.push((child_path, &node.stack[stack_ptr][..])); stack_ptr += 1; } } diff --git a/docs/crates/db.md b/docs/crates/db.md index 3ccfb72e344a..79eeae5ee4ff 100644 --- a/docs/crates/db.md +++ b/docs/crates/db.md @@ -61,7 +61,6 @@ There are many tables within the node, all used to store different types of data - StageCheckpointProgresses - PruneCheckpoints - VersionHistory -- BlockRequests - ChainState
@@ -283,7 +282,6 @@ fn unwind(&mut self, provider: &DatabaseProviderRW, input: UnwindInput) { let mut body_cursor = tx.cursor_write::()?; let mut ommers_cursor = tx.cursor_write::()?; let mut withdrawals_cursor = tx.cursor_write::()?; - let mut requests_cursor = tx.cursor_write::()?; // Cursors to unwind transitions let mut tx_block_cursor = tx.cursor_write::()?; @@ -322,7 +320,7 @@ fn unwind(&mut self, provider: &DatabaseProviderRW, input: UnwindInput) { } ``` -This function first grabs a mutable cursor for the `BlockBodyIndices`, `BlockOmmers`, `BlockWithdrawals`, `BlockRequests`, `TransactionBlocks` tables. +This function first grabs a mutable cursor for the `BlockBodyIndices`, `BlockOmmers`, `BlockWithdrawals`, `TransactionBlocks` tables. Then it gets a walker of the block body cursor, and then walk backwards through the cursor to delete the block body entries from the last block number to the block number specified in the `UnwindInput` struct. diff --git a/etc/README.md b/etc/README.md index f80b5b774b6d..4f4ce7f20e45 100644 --- a/etc/README.md +++ b/etc/README.md @@ -2,7 +2,8 @@ This directory contains miscellaneous files, such as example Grafana dashboards and Prometheus configuration. -The files in this directory may undergo a lot of changes while reth is unstable, so do not expect them to necessarily be up to date. +The files in this directory may undergo a lot of changes while reth is unstable, so do not expect them to necessarily be +up to date. ### Overview @@ -11,8 +12,67 @@ The files in this directory may undergo a lot of changes while reth is unstable, ### Docker Compose -To run Reth, Grafana or Prometheus with Docker Compose, refer to the [docker docs](/book/installation/docker.md#using-docker-compose). +To run Reth, Grafana or Prometheus with Docker Compose, refer to +the [docker docs](/book/installation/docker.md#using-docker-compose). -### Import Grafana dashboards +### Grafana -Running Grafana in Docker makes it possible to import existing dashboards, refer to [docs on how to run only Grafana in Docker](/book/installation/docker.md#using-docker-compose#run-only-grafana-in-docker). \ No newline at end of file +#### Adding a new metric to Grafana + +To set up a new metric in Reth and its Grafana dashboard (this assumes running Reth and Grafana instances): + +1. Add the metric to the codebase following the [metrics section](../docs/design/metrics.md#creating-metrics) + documentation. + +1. Access Grafana: + + - Open `http://localhost:3000/` in a browser + - Log in with username and password `admin` + - Navigate to the `Dashboards` tab + +1. Create or modify a dashboard: + + - Select an existing dashboard or create a new one + - Click `Add` > `Visualization` to create a new panel + +1. Configure your metric panel: + + - Set a panel title and description + - Select metric(s) from the `Metrics browser` or use the `PromQL` terminal + - Document your metric(s) by setting units, legends, etc. + - When adding multiple metrics, use field overwrites if needed + +1. Save and arrange: + + - Click `Apply` to save the panel + - Drag the panel to desired position on the dashboard + +1. Export the dashboard: + + - Click `Share` > `Export` + - Toggle `Export for sharing externally` + - Click `Save to file` + +1. Update dashboard file: + - Replace the content of the corresponding file in the [dashboards folder](./grafana/dashboards) with the exported + JSON + +Your new metric is now integrated into the Reth Grafana dashboard. + +#### Import Grafana dashboards + +If you are running Reth and Grafana outside of docker, and wish to import new Grafana dashboards or update a dashboard: + +1. Go to `Home` > `Dashboards` + +1. Click `New` > `Import` + +1. Drag the JSON dashboard file to import it + +1. If updating an existing dashboard, you will need to change the name and UID of the imported dashboard in order to + avoid conflict + +1. Delete the old dashboard + +If you are running Reth and Grafana using docker, after having pulled the updated dashboards from `main`, restart the +Grafana service. This will update all dashboards. \ No newline at end of file diff --git a/etc/docker-compose.yml b/etc/docker-compose.yml index 618aa6f5ae63..cd7dd6dd263b 100644 --- a/etc/docker-compose.yml +++ b/etc/docker-compose.yml @@ -65,7 +65,7 @@ services: sh -c "cp -r /etc/grafana/provisioning_temp/dashboards/. /etc/grafana/provisioning/dashboards && find /etc/grafana/provisioning/dashboards/ -name '*.json' -exec sed -i 's/$${DS_PROMETHEUS}/Prometheus/g' {} \+ && /run.sh" - + volumes: mainnet_data: driver: local diff --git a/etc/grafana/dashboards/overview.json b/etc/grafana/dashboards/overview.json index 15786764f429..8c77f5979fe9 100644 --- a/etc/grafana/dashboards/overview.json +++ b/etc/grafana/dashboards/overview.json @@ -1007,13 +1007,242 @@ "title": "Sync progress (stage progress as highest block number reached)", "type": "timeseries" }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Tracks the number of critical tasks currently ran by the executor.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "semi-dark-red", + "value": 0 + } + ] + }, + "unit": "tasks" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 20 + }, + "id": 248, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_executor_spawn_critical_tasks_total{instance=\"$instance\"}- reth_executor_spawn_finished_critical_tasks_total{instance=\"$instance\"}", + "hide": false, + "instant": false, + "legendFormat": "Tasks running", + "range": true, + "refId": "C" + } + ], + "title": "Task Executor critical tasks", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "Tracks the number of regular tasks currently ran by the executor.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "semi-dark-red", + "value": 80 + } + ] + }, + "unit": "tasks/s" + }, + "overrides": [ + { + "matcher": { + "id": "byFrameRefID", + "options": "C" + }, + "properties": [ + { + "id": "unit", + "value": "tasks" + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 20 + }, + "id": 247, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "rate(reth_executor_spawn_regular_tasks_total{instance=\"$instance\"}[$__rate_interval])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "Tasks started", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "reth_executor_spawn_regular_tasks_total{instance=\"$instance\"}- reth_executor_spawn_finished_regular_tasks_total{instance=\"$instance\"}", + "hide": false, + "instant": false, + "legendFormat": "Tasks running", + "range": true, + "refId": "C" + } + ], + "title": "Task Executor regular tasks", + "type": "timeseries" + }, { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, - "y": 20 + "y": 28 }, "id": 38, "panels": [], @@ -1085,7 +1314,7 @@ "h": 8, "w": 12, "x": 0, - "y": 21 + "y": 29 }, "id": 40, "options": { @@ -1145,7 +1374,7 @@ "h": 8, "w": 12, "x": 12, - "y": 21 + "y": 29 }, "id": 42, "maxDataPoints": 25, @@ -1273,7 +1502,7 @@ "h": 8, "w": 12, "x": 0, - "y": 29 + "y": 37 }, "id": 117, "options": { @@ -1370,7 +1599,7 @@ "h": 8, "w": 12, "x": 12, - "y": 29 + "y": 37 }, "id": 116, "options": { @@ -1471,7 +1700,7 @@ "h": 8, "w": 12, "x": 0, - "y": 37 + "y": 45 }, "id": 119, "options": { @@ -1572,7 +1801,7 @@ "h": 8, "w": 12, "x": 12, - "y": 37 + "y": 45 }, "id": 118, "options": { @@ -1634,7 +1863,7 @@ "h": 8, "w": 12, "x": 0, - "y": 45 + "y": 53 }, "id": 48, "options": { @@ -1746,7 +1975,7 @@ "h": 8, "w": 12, "x": 12, - "y": 45 + "y": 53 }, "id": 52, "options": { @@ -1804,7 +2033,7 @@ "h": 8, "w": 12, "x": 0, - "y": 53 + "y": 61 }, "id": 50, "options": { @@ -1972,7 +2201,7 @@ "h": 8, "w": 12, "x": 12, - "y": 53 + "y": 61 }, "id": 58, "options": { @@ -2073,7 +2302,7 @@ "h": 8, "w": 12, "x": 0, - "y": 61 + "y": 69 }, "id": 113, "options": { @@ -2110,7 +2339,7 @@ "h": 1, "w": 24, "x": 0, - "y": 69 + "y": 77 }, "id": 203, "panels": [], @@ -2144,7 +2373,7 @@ "h": 8, "w": 8, "x": 0, - "y": 70 + "y": 78 }, "id": 202, "options": { @@ -2305,7 +2534,7 @@ "h": 8, "w": 8, "x": 8, - "y": 70 + "y": 78 }, "id": 204, "options": { @@ -2455,7 +2684,7 @@ "h": 8, "w": 8, "x": 16, - "y": 70 + "y": 78 }, "id": 205, "options": { @@ -2556,7 +2785,7 @@ "h": 8, "w": 12, "x": 0, - "y": 78 + "y": 86 }, "id": 206, "options": { @@ -2653,7 +2882,7 @@ "h": 8, "w": 12, "x": 12, - "y": 78 + "y": 86 }, "id": 207, "options": { @@ -2690,7 +2919,7 @@ "h": 1, "w": 24, "x": 0, - "y": 86 + "y": 94 }, "id": 46, "panels": [], @@ -2761,7 +2990,7 @@ "h": 8, "w": 24, "x": 0, - "y": 87 + "y": 95 }, "id": 56, "options": { @@ -2857,7 +3086,7 @@ "h": 11, "w": 24, "x": 0, - "y": 95 + "y": 103 }, "id": 240, "options": { @@ -2916,7 +3145,7 @@ "h": 1, "w": 24, "x": 0, - "y": 106 + "y": 114 }, "id": 24, "panels": [], @@ -3014,7 +3243,7 @@ "h": 8, "w": 12, "x": 0, - "y": 107 + "y": 115 }, "id": 26, "options": { @@ -3148,7 +3377,7 @@ "h": 8, "w": 12, "x": 12, - "y": 107 + "y": 115 }, "id": 33, "options": { @@ -3268,7 +3497,7 @@ "h": 8, "w": 12, "x": 0, - "y": 115 + "y": 123 }, "id": 36, "options": { @@ -3317,7 +3546,7 @@ "h": 1, "w": 24, "x": 0, - "y": 123 + "y": 131 }, "id": 32, "panels": [], @@ -3425,7 +3654,7 @@ "h": 8, "w": 12, "x": 0, - "y": 124 + "y": 132 }, "id": 30, "options": { @@ -3591,7 +3820,7 @@ "h": 8, "w": 12, "x": 12, - "y": 124 + "y": 132 }, "id": 28, "options": { @@ -3711,7 +3940,7 @@ "h": 8, "w": 12, "x": 0, - "y": 132 + "y": 140 }, "id": 35, "options": { @@ -3837,7 +4066,7 @@ "h": 8, "w": 12, "x": 12, - "y": 132 + "y": 140 }, "id": 73, "options": { @@ -3964,7 +4193,7 @@ "h": 8, "w": 12, "x": 0, - "y": 140 + "y": 148 }, "id": 102, "options": { @@ -4027,7 +4256,7 @@ "h": 1, "w": 24, "x": 0, - "y": 148 + "y": 156 }, "id": 79, "panels": [], @@ -4101,7 +4330,7 @@ "h": 8, "w": 12, "x": 0, - "y": 149 + "y": 157 }, "id": 74, "options": { @@ -4198,7 +4427,7 @@ "h": 8, "w": 12, "x": 12, - "y": 149 + "y": 157 }, "id": 80, "options": { @@ -4295,7 +4524,7 @@ "h": 8, "w": 12, "x": 0, - "y": 157 + "y": 165 }, "id": 81, "options": { @@ -4392,7 +4621,7 @@ "h": 8, "w": 12, "x": 12, - "y": 157 + "y": 165 }, "id": 114, "options": { @@ -4489,7 +4718,7 @@ "h": 8, "w": 12, "x": 12, - "y": 165 + "y": 173 }, "id": 190, "options": { @@ -4527,7 +4756,7 @@ "h": 1, "w": 24, "x": 0, - "y": 173 + "y": 181 }, "id": 87, "panels": [], @@ -4601,7 +4830,7 @@ "h": 8, "w": 12, "x": 0, - "y": 174 + "y": 182 }, "id": 83, "options": { @@ -4697,7 +4926,7 @@ "h": 8, "w": 12, "x": 12, - "y": 174 + "y": 182 }, "id": 84, "options": { @@ -4805,7 +5034,7 @@ "h": 8, "w": 12, "x": 0, - "y": 182 + "y": 190 }, "id": 85, "options": { @@ -4902,7 +5131,7 @@ "h": 8, "w": 12, "x": 12, - "y": 182 + "y": 190 }, "id": 210, "options": { @@ -5227,7 +5456,7 @@ "h": 8, "w": 12, "x": 0, - "y": 190 + "y": 198 }, "id": 211, "options": { @@ -5552,7 +5781,7 @@ "h": 8, "w": 12, "x": 12, - "y": 190 + "y": 198 }, "id": 212, "options": { @@ -5775,9 +6004,9 @@ "h": 8, "w": 24, "x": 0, - "y": 198 + "y": 206 }, - "id": 213, + "id": 213, "options": { "legend": { "calcs": [], @@ -5811,7 +6040,7 @@ "h": 1, "w": 24, "x": 0, - "y": 198 + "y": 214 }, "id": 214, "panels": [], @@ -5883,7 +6112,7 @@ "h": 8, "w": 12, "x": 0, - "y": 199 + "y": 215 }, "id": 215, "options": { @@ -5979,7 +6208,7 @@ "h": 8, "w": 12, "x": 12, - "y": 199 + "y": 215 }, "id": 216, "options": { @@ -6030,7 +6259,7 @@ "h": 1, "w": 24, "x": 0, - "y": 207 + "y": 223 }, "id": 68, "panels": [], @@ -6104,7 +6333,7 @@ "h": 8, "w": 12, "x": 0, - "y": 208 + "y": 224 }, "id": 60, "options": { @@ -6200,7 +6429,7 @@ "h": 8, "w": 12, "x": 12, - "y": 208 + "y": 224 }, "id": 62, "options": { @@ -6296,7 +6525,7 @@ "h": 8, "w": 12, "x": 0, - "y": 216 + "y": 232 }, "id": 64, "options": { @@ -6333,7 +6562,7 @@ "h": 1, "w": 24, "x": 0, - "y": 224 + "y": 240 }, "id": 97, "panels": [], @@ -6418,7 +6647,7 @@ "h": 8, "w": 12, "x": 0, - "y": 225 + "y": 241 }, "id": 98, "options": { @@ -6581,7 +6810,7 @@ "h": 8, "w": 12, "x": 12, - "y": 225 + "y": 241 }, "id": 101, "options": { @@ -6679,7 +6908,7 @@ "h": 8, "w": 12, "x": 0, - "y": 233 + "y": 249 }, "id": 99, "options": { @@ -6777,7 +7006,7 @@ "h": 8, "w": 12, "x": 12, - "y": 233 + "y": 249 }, "id": 100, "options": { @@ -6815,7 +7044,7 @@ "h": 1, "w": 24, "x": 0, - "y": 241 + "y": 257 }, "id": 105, "panels": [], @@ -6888,7 +7117,7 @@ "h": 8, "w": 12, "x": 0, - "y": 242 + "y": 258 }, "id": 106, "options": { @@ -6986,7 +7215,7 @@ "h": 8, "w": 12, "x": 12, - "y": 242 + "y": 258 }, "id": 107, "options": { @@ -7083,7 +7312,7 @@ "h": 8, "w": 12, "x": 0, - "y": 250 + "y": 266 }, "id": 217, "options": { @@ -7121,7 +7350,7 @@ "h": 1, "w": 24, "x": 0, - "y": 258 + "y": 274 }, "id": 108, "panels": [], @@ -7219,7 +7448,7 @@ "h": 8, "w": 12, "x": 0, - "y": 259 + "y": 275 }, "id": 109, "options": { @@ -7281,7 +7510,7 @@ "h": 8, "w": 12, "x": 12, - "y": 259 + "y": 275 }, "id": 111, "maxDataPoints": 25, @@ -7411,7 +7640,7 @@ "h": 8, "w": 12, "x": 0, - "y": 267 + "y": 283 }, "id": 120, "options": { @@ -7469,7 +7698,7 @@ "h": 8, "w": 12, "x": 12, - "y": 267 + "y": 283 }, "id": 112, "maxDataPoints": 25, @@ -7623,7 +7852,7 @@ "h": 8, "w": 12, "x": 0, - "y": 275 + "y": 291 }, "id": 198, "options": { @@ -7809,9 +8038,9 @@ "h": 8, "w": 12, "x": 12, - "y": 275 + "y": 291 }, - "id": 213, + "id": 246, "options": { "legend": { "calcs": [], @@ -7848,7 +8077,7 @@ "h": 1, "w": 24, "x": 0, - "y": 283 + "y": 299 }, "id": 236, "panels": [], @@ -7920,7 +8149,7 @@ "h": 8, "w": 12, "x": 0, - "y": 284 + "y": 300 }, "id": 237, "options": { @@ -8017,7 +8246,7 @@ "h": 8, "w": 12, "x": 12, - "y": 284 + "y": 300 }, "id": 238, "options": { @@ -8114,7 +8343,7 @@ "h": 8, "w": 12, "x": 0, - "y": 292 + "y": 308 }, "id": 239, "options": { @@ -8223,7 +8452,7 @@ "h": 8, "w": 12, "x": 12, - "y": 292 + "y": 308 }, "id": 219, "options": { @@ -8288,7 +8517,7 @@ "h": 8, "w": 12, "x": 0, - "y": 300 + "y": 316 }, "id": 220, "options": { @@ -8332,7 +8561,7 @@ "h": 1, "w": 24, "x": 0, - "y": 308 + "y": 324 }, "id": 241, "panels": [], @@ -8405,7 +8634,7 @@ "h": 8, "w": 12, "x": 0, - "y": 309 + "y": 325 }, "id": 243, "options": { @@ -8517,7 +8746,7 @@ "h": 8, "w": 12, "x": 12, - "y": 309 + "y": 325 }, "id": 244, "options": { @@ -8630,7 +8859,7 @@ "h": 8, "w": 12, "x": 0, - "y": 317 + "y": 333 }, "id": 245, "options": { @@ -8669,7 +8898,7 @@ "h": 1, "w": 24, "x": 0, - "y": 325 + "y": 341 }, "id": 226, "panels": [], @@ -8767,7 +8996,7 @@ "h": 8, "w": 12, "x": 0, - "y": 326 + "y": 342 }, "id": 225, "options": { @@ -8896,7 +9125,7 @@ "h": 8, "w": 12, "x": 12, - "y": 326 + "y": 342 }, "id": 227, "options": { @@ -9025,7 +9254,7 @@ "h": 8, "w": 12, "x": 0, - "y": 334 + "y": 350 }, "id": 235, "options": { @@ -9154,7 +9383,7 @@ "h": 8, "w": 12, "x": 12, - "y": 334 + "y": 350 }, "id": 234, "options": { diff --git a/examples/beacon-api-sidecar-fetcher/src/mined_sidecar.rs b/examples/beacon-api-sidecar-fetcher/src/mined_sidecar.rs index cc761aa98a61..1c53e4f41051 100644 --- a/examples/beacon-api-sidecar-fetcher/src/mined_sidecar.rs +++ b/examples/beacon-api-sidecar-fetcher/src/mined_sidecar.rs @@ -109,6 +109,7 @@ where match self.pool.get_all_blobs_exact(txs.iter().map(|(tx, _)| tx.hash()).collect()) { Ok(blobs) => { + actions_to_queue.reserve_exact(txs.len()); for ((tx, _), sidecar) in txs.iter().zip(blobs.iter()) { let transaction = BlobTransaction::try_from_signed(tx.clone(), sidecar.clone()) .expect("should not fail to convert blob tx if it is already eip4844"); diff --git a/examples/custom-engine-types/src/main.rs b/examples/custom-engine-types/src/main.rs index 34f8186be7f8..46a7d7d9af93 100644 --- a/examples/custom-engine-types/src/main.rs +++ b/examples/custom-engine-types/src/main.rs @@ -34,11 +34,15 @@ use alloy_rpc_types::{ use reth::{ api::PayloadTypes, builder::{ - components::{ComponentsBuilder, EngineValidatorBuilder, PayloadServiceBuilder}, + components::{ComponentsBuilder, PayloadServiceBuilder}, node::{NodeTypes, NodeTypesWithEngine}, - BuilderContext, FullNodeTypes, Node, NodeBuilder, PayloadBuilderConfig, + rpc::{EngineValidatorBuilder, RpcAddOns}, + BuilderContext, FullNodeTypes, Node, NodeAdapter, NodeBuilder, NodeComponentsBuilder, + PayloadBuilderConfig, }, + network::NetworkHandle, providers::{CanonStateSubscriptions, StateProviderFactory}, + rpc::eth::EthApi, tasks::TaskManager, transaction_pool::TransactionPool, }; @@ -49,13 +53,13 @@ use reth_basic_payload_builder::{ use reth_chainspec::{Chain, ChainSpec, ChainSpecProvider}; use reth_node_api::{ payload::{EngineApiMessageVersion, EngineObjectValidationError, PayloadOrAttributes}, - validate_version_specific_fields, EngineTypes, EngineValidator, PayloadAttributes, - PayloadBuilderAttributes, + validate_version_specific_fields, AddOnsContext, EngineTypes, EngineValidator, + FullNodeComponents, PayloadAttributes, PayloadBuilderAttributes, }; use reth_node_core::{args::RpcServerArgs, node_config::NodeConfig}; use reth_node_ethereum::{ node::{ - EthereumAddOns, EthereumConsensusBuilder, EthereumExecutorBuilder, EthereumNetworkBuilder, + EthereumConsensusBuilder, EthereumExecutorBuilder, EthereumNetworkBuilder, EthereumPoolBuilder, }, EthEvmConfig, @@ -153,9 +157,9 @@ impl PayloadTypes for CustomEngineTypes { impl EngineTypes for CustomEngineTypes { type ExecutionPayloadV1 = ExecutionPayloadV1; - type ExecutionPayloadV2 = ExecutionPayloadEnvelopeV2; - type ExecutionPayloadV3 = ExecutionPayloadEnvelopeV3; - type ExecutionPayloadV4 = ExecutionPayloadEnvelopeV4; + type ExecutionPayloadEnvelopeV2 = ExecutionPayloadEnvelopeV2; + type ExecutionPayloadEnvelopeV3 = ExecutionPayloadEnvelopeV3; + type ExecutionPayloadEnvelopeV4 = ExecutionPayloadEnvelopeV4; } /// Custom engine validator @@ -201,12 +205,14 @@ pub struct CustomEngineValidatorBuilder; impl EngineValidatorBuilder for CustomEngineValidatorBuilder where - N: FullNodeTypes>, + N: FullNodeComponents< + Types: NodeTypesWithEngine, + >, { type Validator = CustomEngineValidator; - async fn build_validator(self, ctx: &BuilderContext) -> eyre::Result { - Ok(CustomEngineValidator { chain_spec: ctx.chain_spec() }) + async fn build(self, ctx: &AddOnsContext<'_, N>) -> eyre::Result { + Ok(CustomEngineValidator { chain_spec: ctx.config.chain.clone() }) } } @@ -225,6 +231,18 @@ impl NodeTypesWithEngine for MyCustomNode { type Engine = CustomEngineTypes; } +/// Custom addons configuring RPC types +pub type MyNodeAddOns = RpcAddOns< + N, + EthApi< + ::Provider, + ::Pool, + NetworkHandle, + ::Evm, + >, + CustomEngineValidatorBuilder, +>; + /// Implement the Node trait for the custom node /// /// This provides a preset configuration for the node @@ -239,9 +257,10 @@ where EthereumNetworkBuilder, EthereumExecutorBuilder, EthereumConsensusBuilder, - CustomEngineValidatorBuilder, >; - type AddOns = EthereumAddOns; + type AddOns = MyNodeAddOns< + NodeAdapter>::Components>, + >; fn components_builder(&self) -> Self::ComponentsBuilder { ComponentsBuilder::default() @@ -251,11 +270,10 @@ where .network(EthereumNetworkBuilder::default()) .executor(EthereumExecutorBuilder::default()) .consensus(EthereumConsensusBuilder::default()) - .engine_validator(CustomEngineValidatorBuilder::default()) } fn add_ons(&self) -> Self::AddOns { - EthereumAddOns::default() + MyNodeAddOns::default() } } diff --git a/examples/custom-evm/src/main.rs b/examples/custom-evm/src/main.rs index 9c421f9c6a59..55063fc9bbcb 100644 --- a/examples/custom-evm/src/main.rs +++ b/examples/custom-evm/src/main.rs @@ -31,7 +31,7 @@ use reth_node_api::{ use reth_node_core::{args::RpcServerArgs, node_config::NodeConfig}; use reth_node_ethereum::{ node::{EthereumAddOns, EthereumPayloadBuilder}, - EthExecutorProvider, EthereumNode, + BasicBlockExecutorProvider, EthExecutionStrategyFactory, EthereumNode, }; use reth_primitives::{ revm_primitives::{CfgEnvWithHandlerCfg, TxEnv}, @@ -158,7 +158,7 @@ where Node: FullNodeTypes>, { type EVM = MyEvmConfig; - type Executor = EthExecutorProvider; + type Executor = BasicBlockExecutorProvider>; async fn build_evm( self, @@ -166,7 +166,10 @@ where ) -> eyre::Result<(Self::EVM, Self::Executor)> { Ok(( MyEvmConfig::new(ctx.chain_spec()), - EthExecutorProvider::new(ctx.chain_spec(), MyEvmConfig::new(ctx.chain_spec())), + BasicBlockExecutorProvider::new(EthExecutionStrategyFactory::new( + ctx.chain_spec(), + MyEvmConfig::new(ctx.chain_spec()), + )), )) } } diff --git a/examples/custom-payload-builder/src/job.rs b/examples/custom-payload-builder/src/job.rs index 26b594be94b8..014198259596 100644 --- a/examples/custom-payload-builder/src/job.rs +++ b/examples/custom-payload-builder/src/job.rs @@ -3,6 +3,7 @@ use reth::{ providers::StateProviderFactory, tasks::TaskSpawner, transaction_pool::TransactionPool, }; use reth_basic_payload_builder::{PayloadBuilder, PayloadConfig}; +use reth_node_api::PayloadKind; use reth_payload_builder::{KeepPayloadJobAlive, PayloadBuilderError, PayloadJob}; use std::{ @@ -52,7 +53,10 @@ where Ok(self.config.attributes.clone()) } - fn resolve(&mut self) -> (Self::ResolvePayloadFuture, KeepPayloadJobAlive) { + fn resolve_kind( + &mut self, + _kind: PayloadKind, + ) -> (Self::ResolvePayloadFuture, KeepPayloadJobAlive) { let payload = self.best_payload(); (futures_util::future::ready(payload), KeepPayloadJobAlive::No) } diff --git a/examples/custom-rlpx-subprotocol/Cargo.toml b/examples/custom-rlpx-subprotocol/Cargo.toml index d59d16f35cfc..18c136671c06 100644 --- a/examples/custom-rlpx-subprotocol/Cargo.toml +++ b/examples/custom-rlpx-subprotocol/Cargo.toml @@ -13,8 +13,6 @@ reth-eth-wire.workspace = true reth-network.workspace = true reth-network-api.workspace = true reth-node-ethereum.workspace = true -reth-provider = { workspace = true, features = ["test-utils"] } -reth-primitives.workspace = true reth.workspace = true tokio-stream.workspace = true eyre.workspace = true diff --git a/examples/manual-p2p/Cargo.toml b/examples/manual-p2p/Cargo.toml index 2303bfbfeac6..b1642f66ca04 100644 --- a/examples/manual-p2p/Cargo.toml +++ b/examples/manual-p2p/Cargo.toml @@ -14,6 +14,8 @@ reth-eth-wire.workspace = true reth-ecies.workspace = true reth-network-peers.workspace = true +alloy-consensus.workspace = true + secp256k1 = { workspace = true, features = ["global-context", "rand-std", "recovery"] } futures.workspace = true diff --git a/examples/manual-p2p/src/main.rs b/examples/manual-p2p/src/main.rs index edaaf8584897..857a8a1c1269 100644 --- a/examples/manual-p2p/src/main.rs +++ b/examples/manual-p2p/src/main.rs @@ -8,6 +8,7 @@ use std::time::Duration; +use alloy_consensus::constants::MAINNET_GENESIS_HASH; use futures::StreamExt; use reth_chainspec::{Chain, MAINNET}; use reth_discv4::{DiscoveryUpdate, Discv4, Discv4ConfigBuilder, DEFAULT_DISCOVERY_ADDRESS}; @@ -17,7 +18,7 @@ use reth_eth_wire::{ }; use reth_network::config::rng_secret_key; use reth_network_peers::{mainnet_nodes, pk2id, NodeRecord}; -use reth_primitives::{EthereumHardfork, Head, MAINNET_GENESIS_HASH}; +use reth_primitives::{EthereumHardfork, Head}; use secp256k1::{SecretKey, SECP256K1}; use std::sync::LazyLock; use tokio::net::TcpStream; diff --git a/examples/polygon-p2p/Cargo.toml b/examples/polygon-p2p/Cargo.toml index bdf9a27ce560..e18f32a64737 100644 --- a/examples/polygon-p2p/Cargo.toml +++ b/examples/polygon-p2p/Cargo.toml @@ -20,6 +20,5 @@ reth-primitives.workspace = true serde_json.workspace = true reth-tracing.workspace = true tokio-stream.workspace = true -reth-provider = { workspace = true, features = ["test-utils"] } reth-discv4 = { workspace = true, features = ["test-utils"] } alloy-primitives.workspace = true diff --git a/examples/stateful-precompile/src/main.rs b/examples/stateful-precompile/src/main.rs index 26ebdfe4124b..b0165e4de26c 100644 --- a/examples/stateful-precompile/src/main.rs +++ b/examples/stateful-precompile/src/main.rs @@ -20,7 +20,10 @@ use reth::{ use reth_chainspec::{Chain, ChainSpec}; use reth_node_api::{ConfigureEvm, ConfigureEvmEnv, FullNodeTypes, NodeTypes}; use reth_node_core::{args::RpcServerArgs, node_config::NodeConfig}; -use reth_node_ethereum::{node::EthereumAddOns, EthEvmConfig, EthExecutorProvider, EthereumNode}; +use reth_node_ethereum::{ + node::EthereumAddOns, BasicBlockExecutorProvider, EthEvmConfig, EthExecutionStrategyFactory, + EthereumNode, +}; use reth_primitives::{ revm_primitives::{SpecId, StatefulPrecompileMut}, Header, TransactionSigned, @@ -224,7 +227,7 @@ where Node: FullNodeTypes>, { type EVM = MyEvmConfig; - type Executor = EthExecutorProvider; + type Executor = BasicBlockExecutorProvider>; async fn build_evm( self, @@ -234,7 +237,13 @@ where inner: EthEvmConfig::new(ctx.chain_spec()), precompile_cache: self.precompile_cache.clone(), }; - Ok((evm_config.clone(), EthExecutorProvider::new(ctx.chain_spec(), evm_config))) + Ok(( + evm_config.clone(), + BasicBlockExecutorProvider::new(EthExecutionStrategyFactory::new( + ctx.chain_spec(), + evm_config, + )), + )) } } diff --git a/fork.yaml b/fork.yaml index 93920105fe2a..707a313ffda8 100644 --- a/fork.yaml +++ b/fork.yaml @@ -4,7 +4,7 @@ footer: | base: name: reth url: https://github.com/paradigmxyz/reth - hash: 161605313a567a081ea2eaf23ab1f6eeba29d19f + hash: 7119bb1fe000dbd7cb58e0fcd5a66c549538e9a2 fork: name: scroll-reth url: https://github.com/scroll-tech/reth diff --git a/testing/ef-tests/Cargo.toml b/testing/ef-tests/Cargo.toml index df68f5154fc4..a56c44ec3db5 100644 --- a/testing/ef-tests/Cargo.toml +++ b/testing/ef-tests/Cargo.toml @@ -13,7 +13,11 @@ workspace = true [features] ef-tests = [] -asm-keccak = ["reth-primitives/asm-keccak"] +asm-keccak = [ + "reth-primitives/asm-keccak", + "alloy-primitives/asm-keccak", + "revm/asm-keccak" +] [dependencies] reth-chainspec.workspace = true diff --git a/testing/ef-tests/src/models.rs b/testing/ef-tests/src/models.rs index 3f3df15363a1..30e5e5bb20c2 100644 --- a/testing/ef-tests/src/models.rs +++ b/testing/ef-tests/src/models.rs @@ -87,7 +87,7 @@ pub struct Header { /// Parent beacon block root. pub parent_beacon_block_root: Option, /// Requests root. - pub requests_root: Option, + pub requests_hash: Option, } impl From
for SealedHeader { @@ -113,7 +113,7 @@ impl From
for SealedHeader { blob_gas_used: value.blob_gas_used.map(|v| v.to::()), excess_blob_gas: value.excess_blob_gas.map(|v| v.to::()), parent_beacon_block_root: value.parent_beacon_block_root, - requests_root: value.requests_root, + requests_hash: value.requests_hash, }; Self::new(header, value.hash) } diff --git a/testing/testing-utils/Cargo.toml b/testing/testing-utils/Cargo.toml index 49a59ecf6ae3..98bfeabdfb15 100644 --- a/testing/testing-utils/Cargo.toml +++ b/testing/testing-utils/Cargo.toml @@ -14,10 +14,12 @@ workspace = true [dependencies] reth-primitives = { workspace = true, features = ["secp256k1"] } -alloy-eips.workspace = true alloy-genesis.workspace = true alloy-primitives.workspace = true alloy-consensus.workspace = true rand.workspace = true secp256k1 = { workspace = true, features = ["rand"] } + +[dev-dependencies] +alloy-eips.workspace = true diff --git a/testing/testing-utils/src/generators.rs b/testing/testing-utils/src/generators.rs index 70d6cc02b655..571727cb2fd6 100644 --- a/testing/testing-utils/src/generators.rs +++ b/testing/testing-utils/src/generators.rs @@ -1,17 +1,14 @@ //! Generators for different data structures like block headers, block bodies and ranges of those. -use alloy_consensus::TxLegacy; -use alloy_eips::{ - eip6110::DepositRequest, eip7002::WithdrawalRequest, eip7251::ConsolidationRequest, -}; +use alloy_consensus::{Transaction as _, TxLegacy}; use alloy_primitives::{Address, BlockNumber, Bytes, Parity, Sealable, TxKind, B256, U256}; pub use rand::Rng; use rand::{ distributions::uniform::SampleRange, rngs::StdRng, seq::SliceRandom, thread_rng, SeedableRng, }; use reth_primitives::{ - proofs, sign_message, Account, BlockBody, Header, Log, Receipt, Request, Requests, SealedBlock, - SealedHeader, StorageEntry, Transaction, TransactionSigned, Withdrawal, Withdrawals, + proofs, sign_message, Account, BlockBody, Header, Log, Receipt, SealedBlock, SealedHeader, + StorageEntry, Transaction, TransactionSigned, Withdrawal, Withdrawals, }; use secp256k1::{Keypair, Secp256k1}; use std::{ @@ -201,11 +198,6 @@ pub fn random_block(rng: &mut R, number: u64, block_params: BlockParams) let transactions_root = proofs::calculate_transaction_root(&transactions); let ommers_hash = proofs::calculate_ommers_root(&ommers); - let requests = block_params - .requests_count - .map(|count| (0..count).map(|_| random_request(rng)).collect::>()); - let requests_root = requests.as_ref().map(|requests| proofs::calculate_requests_root(requests)); - let withdrawals = block_params.withdrawals_count.map(|count| { (0..count) .map(|i| Withdrawal { @@ -226,7 +218,8 @@ pub fn random_block(rng: &mut R, number: u64, block_params: BlockParams) transactions_root, ommers_hash, base_fee_per_gas: Some(rng.gen()), - requests_root, + // TODO(onbjerg): Proper EIP-7685 request support + requests_hash: None, withdrawals_root, ..Default::default() } @@ -236,12 +229,7 @@ pub fn random_block(rng: &mut R, number: u64, block_params: BlockParams) SealedBlock { header: SealedHeader::new(header, seal), - body: BlockBody { - transactions, - ommers, - withdrawals: withdrawals.map(Withdrawals::new), - requests: requests.map(Requests), - }, + body: BlockBody { transactions, ommers, withdrawals: withdrawals.map(Withdrawals::new) }, } } @@ -470,31 +458,6 @@ pub fn random_log(rng: &mut R, address: Option
, topics_count: O ) } -/// Generate random request -pub fn random_request(rng: &mut R) -> Request { - let request_type = rng.gen_range(0..3); - match request_type { - 0 => Request::DepositRequest(DepositRequest { - pubkey: rng.gen(), - withdrawal_credentials: rng.gen(), - amount: rng.gen(), - signature: rng.gen(), - index: rng.gen(), - }), - 1 => Request::WithdrawalRequest(WithdrawalRequest { - source_address: rng.gen(), - validator_pubkey: rng.gen(), - amount: rng.gen(), - }), - 2 => Request::ConsolidationRequest(ConsolidationRequest { - source_address: rng.gen(), - source_pubkey: rng.gen(), - target_pubkey: rng.gen(), - }), - _ => panic!("invalid request type"), - } -} - #[cfg(test)] mod tests { use super::*;