diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index d17ebe3c176..76e9bfcaaa2 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -76,7 +76,7 @@ body: id: terms attributes: label: Code of Conduct - description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/foundry-rs/reth/blob/main/CODE_OF_CONDUCT.md) + description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/paradigmxyz/reth/blob/main/CONTRIBUTING.md#code-of-conduct) options: - label: I agree to follow the Code of Conduct required: true diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index cd5b3f54d6a..db26044dfa5 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -5,7 +5,6 @@ on: branches: [main] env: - RUSTFLAGS: -D warnings CARGO_TERM_COLOR: always concurrency: diff --git a/.github/workflows/dependencies.yml b/.github/workflows/dependencies.yml new file mode 100644 index 00000000000..e731a7a5692 --- /dev/null +++ b/.github/workflows/dependencies.yml @@ -0,0 +1,61 @@ +# Automatically run `cargo update` periodically + +name: Update Dependencies + +on: + schedule: + # Run weekly + - cron: "0 0 * * SUN" + workflow_dispatch: + # Needed so we can run it manually + +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BRANCH: cargo-update + TITLE: "chore(deps): weekly `cargo update`" + BODY: | + Automation to keep dependencies in `Cargo.lock` current. + +
cargo update log +

+ + ```log + $cargo_update_log + ``` + +

+
+ +jobs: + update: + name: Update + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@nightly + + - name: cargo update + # Remove first line that always just says "Updating crates.io index" + run: cargo update --color never 2>&1 | sed '/crates.io index/d' | tee -a cargo_update.log + + - name: craft commit message and PR body + id: msg + run: | + export cargo_update_log="$(cat cargo_update.log)" + + echo "commit_message<> $GITHUB_OUTPUT + printf "$TITLE\n\n$cargo_update_log\n" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + echo "body<> $GITHUB_OUTPUT + echo "$BODY" | envsubst >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v5 + with: + add-paths: ./Cargo.lock + commit-message: ${{ steps.msg.outputs.commit_message }} + title: ${{ env.TITLE }} + body: ${{ steps.msg.outputs.body }} + branch: ${{ env.BRANCH }} diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 06dbff26f2d..476d9c82100 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -9,7 +9,6 @@ on: env: REPO_NAME: ${{ github.repository_owner }}/reth IMAGE_NAME: ${{ github.repository_owner }}/reth - RUSTFLAGS: -D warnings CARGO_TERM_COLOR: always DOCKER_IMAGE_NAME: ghcr.io/${{ github.repository_owner }}/reth DOCKER_USERNAME: ${{ github.actor }} diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml index ebe22fedc72..07a31797afb 100644 --- a/.github/workflows/fuzz.yml +++ b/.github/workflows/fuzz.yml @@ -5,7 +5,6 @@ on: branches: [main] env: - RUSTFLAGS: -D warnings CARGO_TERM_COLOR: always concurrency: diff --git a/.github/workflows/hive.yml b/.github/workflows/hive.yml index b564803e4e4..fbc598b95ac 100644 --- a/.github/workflows/hive.yml +++ b/.github/workflows/hive.yml @@ -4,7 +4,6 @@ on: - cron: '0 0 * * *' env: - RUSTFLAGS: -D warnings CARGO_TERM_COLOR: always concurrency: diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 20886b9e4ba..e45643ae55a 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -5,7 +5,6 @@ on: branches: [main] env: - RUSTFLAGS: -D warnings CARGO_TERM_COLOR: always GETH_BUILD: 1.12.0-e501b3b0 SEED: rustethereumethereumrust diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5a5bedaaf7c..1417695755f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,7 +10,6 @@ on: env: REPO_NAME: ${{ github.repository_owner }}/reth IMAGE_NAME: ${{ github.repository_owner }}/reth - RUSTFLAGS: -D warnings CARGO_TERM_COLOR: always jobs: diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index e46f5a112f2..6c89dca7160 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -5,7 +5,6 @@ on: branches: [main] env: - RUSTFLAGS: -D warnings CARGO_TERM_COLOR: always SEED: rustethereumethereumrust diff --git a/.gitignore b/.gitignore index c2c144f4a1c..bf70ac93f2b 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,6 @@ target/ # MSVC Windows builds of rustc generate these, which store debugging information *.pdb - # Generated by Intellij-based IDEs. .idea diff --git a/Cargo.lock b/Cargo.lock index 1d5a1f6b229..5ba8118176f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,9 +14,9 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" dependencies = [ "gimli", ] @@ -51,12 +51,12 @@ dependencies = [ [[package]] name = "aes" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" dependencies = [ "cfg-if", - "cipher 0.4.3", + "cipher 0.4.4", "cpufeatures", ] @@ -80,7 +80,7 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom 0.2.9", + "getrandom 0.2.10", "once_cell", "version_check", ] @@ -92,7 +92,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" dependencies = [ "cfg-if", - "getrandom 0.2.9", + "getrandom 0.2.10", "once_cell", "version_check", ] @@ -108,9 +108,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" dependencies = [ "memchr", ] @@ -151,22 +151,72 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" +[[package]] +name = "anstream" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" + +[[package]] +name = "anstyle-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +dependencies = [ + "anstyle", + "windows-sys 0.48.0", +] + [[package]] name = "anyhow" -version = "1.0.71" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" [[package]] name = "aquamarine" -version = "0.3.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759d98a5db12e9c9d98ef2b92f794ae5c7ded6ec18d21c3fa485c9c65bec237d" +checksum = "df752953c49ce90719c7bf1fc587bc8227aed04732ea0c0f85e5397d7fdbd1a1" dependencies = [ + "include_dir", "itertools 0.10.5", "proc-macro-error", "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "syn 1.0.109", ] @@ -179,11 +229,20 @@ dependencies = [ "derive_arbitrary", ] +[[package]] +name = "array-init" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23589ecb866b460d3a0f1278834750268c607e8e28a1b982c907219f3178cd72" +dependencies = [ + "nodrop", +] + [[package]] name = "arrayvec" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "assert_matches" @@ -193,9 +252,9 @@ checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" [[package]] name = "async-compression" -version = "0.3.15" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942c7cd7ae39e91bde4820d74132e9862e62c2f386c3aa90ccf55949f5bad63a" +checksum = "62b74f44609f0f91493e3082d3734d98497e094777144380ea4db9f9905dd5b6" dependencies = [ "brotli", "flate2", @@ -203,8 +262,8 @@ dependencies = [ "memchr", "pin-project-lite", "tokio", - "zstd 0.11.2+zstd.1.5.2", - "zstd-safe 5.0.2+zstd.1.5.2", + "zstd", + "zstd-safe", ] [[package]] @@ -218,13 +277,13 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.68" +version = "0.1.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" dependencies = [ "proc-macro2 1.0.66", - "quote 1.0.31", - "syn 2.0.26", + "quote 1.0.32", + "syn 2.0.27", ] [[package]] @@ -249,9 +308,9 @@ dependencies = [ [[package]] name = "atomic-polyfill" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c314e70d181aa6053b26e3f7fbf86d1dfff84f816a6175b967666b3506ef7289" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" dependencies = [ "critical-section", ] @@ -276,7 +335,7 @@ checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" dependencies = [ "proc-macro-error", "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "syn 1.0.109", ] @@ -288,21 +347,21 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backon" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f34fac4d7cdaefa2deded0eda2d5d59dbfd43370ff3f856209e72340ae84c294" +checksum = "0c1a6197b2120bb2185a267f6515038558b019e92b832bb0320e96d66268dcf9" dependencies = [ - "futures", + "fastrand 1.9.0", + "futures-core", "pin-project", - "rand 0.8.5", "tokio", ] [[package]] name = "backtrace" -version = "0.3.67" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" dependencies = [ "addr2line", "cc", @@ -327,15 +386,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5" - -[[package]] -name = "base64" -version = "0.21.0" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" [[package]] name = "base64ct" @@ -345,9 +398,9 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "basic-toml" -version = "0.1.1" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e819b667739967cd44d308b8c7b71305d8bb0729ac44a248aa08f33d01950b4" +checksum = "7bfc506e7a2370ec239e1d072507b2a80c833083699d3c6fa176fbb4de8448c6" dependencies = [ "serde", ] @@ -389,7 +442,7 @@ dependencies = [ "lazycell", "peeking_take_while", "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "regex", "rustc-hash", "shlex", @@ -410,11 +463,11 @@ dependencies = [ "peeking_take_while", "prettyplease", "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "regex", "rustc-hash", "shlex", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -487,9 +540,9 @@ dependencies = [ [[package]] name = "block-padding" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a90ec2df9600c28a01c56c4784c9207a96d2451833aeceb8cc97e4c9548bb78" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" dependencies = [ "generic-array", ] @@ -497,7 +550,7 @@ dependencies = [ [[package]] name = "boa_ast" version = "0.17.0" -source = "git+https://github.com/boa-dev/boa#b51e7cfb84fd6a4dc474d103e5654efedfe94a4e" +source = "git+https://github.com/boa-dev/boa#d459ff1b444ac119fe58e46c4e32e874a8135ce3" dependencies = [ "bitflags 2.3.3", "boa_interner", @@ -510,7 +563,7 @@ dependencies = [ [[package]] name = "boa_engine" version = "0.17.0" -source = "git+https://github.com/boa-dev/boa#b51e7cfb84fd6a4dc474d103e5654efedfe94a4e" +source = "git+https://github.com/boa-dev/boa#d459ff1b444ac119fe58e46c4e32e874a8135ce3" dependencies = [ "bitflags 2.3.3", "boa_ast", @@ -548,7 +601,7 @@ dependencies = [ [[package]] name = "boa_gc" version = "0.17.0" -source = "git+https://github.com/boa-dev/boa#b51e7cfb84fd6a4dc474d103e5654efedfe94a4e" +source = "git+https://github.com/boa-dev/boa#d459ff1b444ac119fe58e46c4e32e874a8135ce3" dependencies = [ "boa_macros", "boa_profiler", @@ -558,7 +611,7 @@ dependencies = [ [[package]] name = "boa_icu_provider" version = "0.17.0" -source = "git+https://github.com/boa-dev/boa#b51e7cfb84fd6a4dc474d103e5654efedfe94a4e" +source = "git+https://github.com/boa-dev/boa#d459ff1b444ac119fe58e46c4e32e874a8135ce3" dependencies = [ "icu_collections", "icu_normalizer", @@ -571,7 +624,7 @@ dependencies = [ [[package]] name = "boa_interner" version = "0.17.0" -source = "git+https://github.com/boa-dev/boa#b51e7cfb84fd6a4dc474d103e5654efedfe94a4e" +source = "git+https://github.com/boa-dev/boa#d459ff1b444ac119fe58e46c4e32e874a8135ce3" dependencies = [ "boa_gc", "boa_macros", @@ -586,18 +639,18 @@ dependencies = [ [[package]] name = "boa_macros" version = "0.17.0" -source = "git+https://github.com/boa-dev/boa#b51e7cfb84fd6a4dc474d103e5654efedfe94a4e" +source = "git+https://github.com/boa-dev/boa#d459ff1b444ac119fe58e46c4e32e874a8135ce3" dependencies = [ "proc-macro2 1.0.66", - "quote 1.0.31", - "syn 2.0.26", + "quote 1.0.32", + "syn 2.0.27", "synstructure 0.13.0", ] [[package]] name = "boa_parser" version = "0.17.0" -source = "git+https://github.com/boa-dev/boa#b51e7cfb84fd6a4dc474d103e5654efedfe94a4e" +source = "git+https://github.com/boa-dev/boa#d459ff1b444ac119fe58e46c4e32e874a8135ce3" dependencies = [ "bitflags 2.3.3", "boa_ast", @@ -617,7 +670,7 @@ dependencies = [ [[package]] name = "boa_profiler" version = "0.17.0" -source = "git+https://github.com/boa-dev/boa#b51e7cfb84fd6a4dc474d103e5654efedfe94a4e" +source = "git+https://github.com/boa-dev/boa#d459ff1b444ac119fe58e46c4e32e874a8135ce3" [[package]] name = "brotli" @@ -657,14 +710,14 @@ checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" dependencies = [ "lazy_static", "memchr", - "regex-automata", + "regex-automata 0.1.10", ] [[package]] name = "bstr" -version = "1.3.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ffdb39cb703212f3c11973452c2861b972f757b021158f3516ba10f2fa8b2c1" +checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" dependencies = [ "memchr", "serde", @@ -672,9 +725,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "byte-slice-cast" @@ -705,18 +758,18 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.3" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6031a462f977dd38968b6f23378356512feeace69cef817e1a4475108093cec3" +checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" dependencies = [ "serde", ] [[package]] name = "cargo-platform" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" +checksum = "2cfa25e60aea747ec7e1124f238816749faa93759c6ff5b31f1ccdda137f4479" dependencies = [ "serde", ] @@ -786,9 +839,9 @@ dependencies = [ [[package]] name = "ciborium" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f" +checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" dependencies = [ "ciborium-io", "ciborium-ll", @@ -797,15 +850,15 @@ dependencies = [ [[package]] name = "ciborium-io" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369" +checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" [[package]] name = "ciborium-ll" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b" +checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" dependencies = [ "ciborium-io", "half", @@ -822,9 +875,9 @@ dependencies = [ [[package]] name = "cipher" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1873270f8f7942c191139cb8a40fd228da6c3fd2fc376d7e92d47aa14aeb59e" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ "crypto-common", "inout", @@ -832,9 +885,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ed9a53e5d4d9c573ae844bfac6872b159cb1d1585a83b29e7a64b7eef7332a" +checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" dependencies = [ "glob", "libc", @@ -843,40 +896,44 @@ dependencies = [ [[package]] name = "clap" -version = "4.1.8" +version = "4.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5" +checksum = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d" dependencies = [ - "bitflags 1.3.2", + "clap_builder", "clap_derive", - "clap_lex", - "is-terminal", "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", "strsim 0.10.0", - "termcolor", ] [[package]] name = "clap_derive" -version = "4.1.8" +version = "4.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bec8e5c9d09e439c4335b1af0abaab56dcf3b94999a936e1bb47b9134288f0" +checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" dependencies = [ "heck", - "proc-macro-error", "proc-macro2 1.0.66", - "quote 1.0.31", - "syn 1.0.109", + "quote 1.0.32", + "syn 2.0.27", ] [[package]] name = "clap_lex" -version = "0.3.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09" -dependencies = [ - "os_str_bytes", -] +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" [[package]] name = "cobs" @@ -891,19 +948,9 @@ dependencies = [ "convert_case 0.6.0", "parity-scale-codec", "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "serde", - "syn 2.0.26", -] - -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", + "syn 2.0.27", ] [[package]] @@ -915,13 +962,13 @@ dependencies = [ "bincode", "bs58", "coins-core", - "digest 0.10.6", - "getrandom 0.2.9", + "digest 0.10.7", + "getrandom 0.2.10", "hmac", "k256", "lazy_static", "serde", - "sha2 0.10.6", + "sha2 0.10.7", "thiserror", ] @@ -933,12 +980,12 @@ checksum = "84f4d04ee18e58356accd644896aeb2094ddeafb6a713e056cef0c0a8e468c15" dependencies = [ "bitvec 0.17.4", "coins-bip32", - "getrandom 0.2.9", + "getrandom 0.2.10", "hmac", "once_cell", - "pbkdf2 0.12.1", + "pbkdf2 0.12.2", "rand 0.8.5", - "sha2 0.10.6", + "sha2 0.10.7", "thiserror", ] @@ -948,20 +995,26 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b949a1c63fb7eb591eb7ba438746326aedf0ae843e51ec92ba6bec5bb382c4f" dependencies = [ - "base64 0.21.0", + "base64 0.21.2", "bech32", "bs58", - "digest 0.10.6", + "digest 0.10.7", "generic-array", "hex", "ripemd", "serde", "serde_derive", - "sha2 0.10.6", + "sha2 0.10.7", "sha3", "thiserror", ] +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "comfy-table" version = "7.0.1" @@ -1000,9 +1053,9 @@ dependencies = [ [[package]] name = "const-oid" -version = "0.9.2" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" +checksum = "795bc6e66a8e340f075fcf6227e417a2dc976b92b91f3cdc778bb858778b6747" [[package]] name = "const-str" @@ -1037,9 +1090,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "cpp_demangle" @@ -1052,9 +1105,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.6" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" dependencies = [ "libc", ] @@ -1128,9 +1181,9 @@ checksum = "6548a0ad5d2549e111e1f6a11a6c2e2d00ce6a3dafe22948d67c2b443f775e52" [[package]] name = "crossbeam-channel" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" dependencies = [ "cfg-if", "crossbeam-utils", @@ -1149,9 +1202,9 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.14" +version = "0.9.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" dependencies = [ "autocfg", "cfg-if", @@ -1162,9 +1215,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.15" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", ] @@ -1218,9 +1271,9 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-bigint" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2538c4e68e52548bacb3e83ac549f903d44f011ac9d5abb5e132e67d0808f7" +checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" dependencies = [ "generic-array", "rand_core 0.6.4", @@ -1238,16 +1291,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "ctor" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" -dependencies = [ - "quote 1.0.31", - "syn 1.0.109", -] - [[package]] name = "ctr" version = "0.8.0" @@ -1263,66 +1306,35 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ - "cipher 0.4.3", + "cipher 0.4.4", ] [[package]] name = "curve25519-dalek" -version = "4.0.0-rc.2" +version = "4.0.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d928d978dbec61a1167414f5ec534f24bea0d7a0d24dd9b6233d3d8223e585" +checksum = "436ace70fc06e06f7f689d2624dc4e2f0ea666efb5aa704215f7249ae6e047a7" dependencies = [ "cfg-if", - "digest 0.10.6", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.10.7", "fiat-crypto", - "packed_simd_2", "platforms", + "rustc_version", "subtle", "zeroize", ] [[package]] -name = "cxx" -version = "1.0.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86d3488e7665a7a483b57e25bdd90d0aeb2bc7608c8d0346acf2ad3f1caf1d62" -dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] - -[[package]] -name = "cxx-build" -version = "1.0.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48fcaf066a053a41a81dfb14d57d99738b767febb8b735c3016e469fac5da690" -dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2 1.0.66", - "quote 1.0.31", - "scratch", - "syn 1.0.109", -] - -[[package]] -name = "cxxbridge-flags" -version = "1.0.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2ef98b8b717a829ca5603af80e1f9e2e48013ab227b68ef37872ef84ee479bf" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.91" +name = "curve25519-dalek-derive" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892" +checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" dependencies = [ "proc-macro2 1.0.66", - "quote 1.0.31", - "syn 1.0.109", + "quote 1.0.32", + "syn 2.0.27", ] [[package]] @@ -1337,22 +1349,12 @@ dependencies = [ [[package]] name = "darling" -version = "0.14.3" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0808e1bd8671fb44a113a14e13497557533369847788fa2ae912b6ebfce9fa8" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" dependencies = [ - "darling_core 0.14.3", - "darling_macro 0.14.3", -] - -[[package]] -name = "darling" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0558d22a7b463ed0241e993f76f09f30b126687447751a8638587b864e4b3944" -dependencies = [ - "darling_core 0.20.1", - "darling_macro 0.20.1", + "darling_core 0.20.3", + "darling_macro 0.20.3", ] [[package]] @@ -1364,37 +1366,23 @@ dependencies = [ "fnv", "ident_case", "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "strsim 0.9.3", "syn 1.0.109", ] [[package]] name = "darling_core" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "001d80444f28e193f30c2f293455da62dcf9a6b29918a4253152ae2b1de592cb" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2 1.0.66", - "quote 1.0.31", - "strsim 0.10.0", - "syn 1.0.109", -] - -[[package]] -name = "darling_core" -version = "0.20.1" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab8bfa2e259f8ee1ce5e97824a3c55ec4404a0d772ca7fa96bf19f0752a046eb" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" dependencies = [ "fnv", "ident_case", "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "strsim 0.10.0", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -1404,30 +1392,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" dependencies = [ "darling_core 0.10.2", - "quote 1.0.31", + "quote 1.0.32", "syn 1.0.109", ] [[package]] name = "darling_macro" -version = "0.14.3" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b36230598a2d5de7ec1c6f51f72d8a99a9208daff41de2084d06e3fd3ea56685" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ - "darling_core 0.14.3", - "quote 1.0.31", - "syn 1.0.109", -] - -[[package]] -name = "darling_macro" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a" -dependencies = [ - "darling_core 0.20.1", - "quote 1.0.31", - "syn 2.0.26", + "darling_core 0.20.3", + "quote 1.0.32", + "syn 2.0.27", ] [[package]] @@ -1445,9 +1422,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" +checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" [[package]] name = "debugid" @@ -1455,7 +1432,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" dependencies = [ - "uuid 1.3.0", + "uuid 1.4.1", ] [[package]] @@ -1470,9 +1447,9 @@ dependencies = [ [[package]] name = "der" -version = "0.7.3" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b10af9f9f9f2134a42d3f8aa74658660f2e0234b0eb81bd171df8aa32779ed" +checksum = "0c7ed52955ce76b1554f509074bb357d3fb8ac9b51288a65a3fd480d1dfba946" dependencies = [ "const-oid", "zeroize", @@ -1480,13 +1457,13 @@ dependencies = [ [[package]] name = "derive_arbitrary" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cdeb9ec472d588e539a818b2dee436825730da08ad0017c4b1a17676bdc8b7" +checksum = "53e0efad4403bfc52dc201159c4b842a246a14b98c64b55dfd0f2d89729dfeb8" dependencies = [ "proc-macro2 1.0.66", - "quote 1.0.31", - "syn 1.0.109", + "quote 1.0.32", + "syn 2.0.27", ] [[package]] @@ -1498,7 +1475,7 @@ dependencies = [ "darling 0.10.2", "derive_builder_core", "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "syn 1.0.109", ] @@ -1510,7 +1487,7 @@ checksum = "2791ea3e372c8495c0bc2033991d76b512cd799d07491fbd6890124db9458bef" dependencies = [ "darling 0.10.2", "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "syn 1.0.109", ] @@ -1522,7 +1499,7 @@ checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "convert_case 0.4.0", "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "rustc_version", "syn 1.0.109", ] @@ -1550,9 +1527,9 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", "const-oid", @@ -1566,16 +1543,16 @@ version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" dependencies = [ - "dirs-sys", + "dirs-sys 0.3.7", ] [[package]] name = "dirs" -version = "4.0.0" +version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" dependencies = [ - "dirs-sys", + "dirs-sys 0.4.1", ] [[package]] @@ -1599,6 +1576,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -1612,14 +1601,14 @@ dependencies = [ [[package]] name = "discv5" -version = "0.3.0" -source = "git+https://github.com/sigp/discv5#f78d538ef8f3c3b3981cfbb8ce2ba3179295eeab" +version = "0.3.1" +source = "git+https://github.com/sigp/discv5#a9f1e99321aec746fb9d6e8df889aa515a5e1254" dependencies = [ "aes 0.7.5", "aes-gcm", "arrayvec", "delay_map", - "enr", + "enr 0.9.0", "fnv", "futures", "hashlink", @@ -1631,8 +1620,8 @@ dependencies = [ "parking_lot 0.11.2", "rand 0.8.5", "rlp", - "smallvec", - "socket2", + "smallvec 1.11.0", + "socket2 0.4.9", "tokio", "tracing", "tracing-subscriber", @@ -1647,8 +1636,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2 1.0.66", - "quote 1.0.31", - "syn 2.0.26", + "quote 1.0.32", + "syn 2.0.27", ] [[package]] @@ -1659,7 +1648,7 @@ checksum = "53ecafc952c4528d9b51a458d1a8904b81783feff9fde08ab6ed2545ff396872" dependencies = [ "cfg-if", "libc", - "socket2", + "socket2 0.4.9", "winapi", ] @@ -1671,27 +1660,28 @@ checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" [[package]] name = "dunce" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bd4b30a6560bbd9b4620f4de34c3f14f60848e58a9b7216801afcb4c7b31c3c" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" [[package]] name = "dyn-clone" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" +checksum = "304e6508efa593091e97a9abbc10f90aa7ca635b6d2784feff3c89d41dd12272" [[package]] name = "ecdsa" -version = "0.16.6" +version = "0.16.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a48e5d537b8a30c0b023116d981b16334be1485af7ca68db3a2b7024cbc957fd" +checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" dependencies = [ "der", - "digest 0.10.6", + "digest 0.10.7", "elliptic-curve", "rfc6979", "signature", + "spki", ] [[package]] @@ -1706,27 +1696,27 @@ dependencies = [ [[package]] name = "ed25519-dalek" -version = "2.0.0-rc.2" +version = "2.0.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "798f704d128510932661a3489b08e3f4c934a01d61c5def59ae7b8e48f19665a" +checksum = "faa8e9049d5d72bfc12acbc05914731b5322f79b5e2f195e9f2d705fca22ab4c" dependencies = [ "curve25519-dalek", "ed25519", "rand_core 0.6.4", "serde", - "sha2 0.10.6", + "sha2 0.10.7", "zeroize", ] [[package]] name = "educe" -version = "0.4.20" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0188e3c3ba8df5753894d54461f0e39bc91741dc5b22e1c46999ec2c71f4e4" +checksum = "079044df30bb07de7d846d41a184c4b00e66ebdac93ee459253474f3a47e50ae" dependencies = [ "enum-ordinalize", "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "syn 1.0.109", ] @@ -1742,7 +1732,6 @@ dependencies = [ "reth-rlp", "reth-stages", "serde", - "serde_bytes", "serde_json", "thiserror", "tokio", @@ -1751,19 +1740,19 @@ dependencies = [ [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "elliptic-curve" -version = "0.13.4" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75c71eaa367f2e5d556414a8eea812bc62985c879748d6403edabd9cb03f16e7" +checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" dependencies = [ "base16ct", "crypto-bigint", - "digest 0.10.6", + "digest 0.10.7", "ff", "generic-array", "group", @@ -1803,7 +1792,6 @@ checksum = "cf56acd72bb22d2824e66ae8e9e5ada4d0de17a69c7fd35569dde2ada8ec9116" dependencies = [ "base64 0.13.1", "bytes", - "ed25519-dalek", "hex", "k256", "log", @@ -1815,6 +1803,26 @@ dependencies = [ "zeroize", ] +[[package]] +name = "enr" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0be7b2ac146c1f99fe245c02d16af0696450d8e06c135db75e10eeb9e642c20d" +dependencies = [ + "base64 0.21.2", + "bytes", + "ed25519-dalek", + "hex", + "k256", + "log", + "rand 0.8.5", + "rlp", + "serde", + "serde-hex", + "sha3", + "zeroize", +] + [[package]] name = "enum-as-inner" version = "0.3.4" @@ -1823,7 +1831,7 @@ checksum = "570d109b813e904becc80d8d5da38376818a143348413f7149f1340fe04754d4" dependencies = [ "heck", "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "syn 1.0.109", ] @@ -1835,40 +1843,39 @@ checksum = "c9720bba047d567ffc8a3cba48bf19126600e249ab7f128e9233e6376976a116" dependencies = [ "heck", "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "syn 1.0.109", ] [[package]] name = "enum-ordinalize" -version = "3.1.12" +version = "3.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bb1df8b45ecb7ffa78dca1c17a438fb193eb083db0b1b494d2a61bcb5096a" +checksum = "e4f76552f53cefc9a7f64987c3701b99d982f7690606fd67de1d09712fbf52f1" dependencies = [ "num-bigint", "num-traits", "proc-macro2 1.0.66", - "quote 1.0.31", - "rustc_version", - "syn 1.0.109", + "quote 1.0.32", + "syn 2.0.27", ] [[package]] name = "enumn" -version = "0.1.8" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48016319042fb7c87b78d2993084a831793a897a5cd1a2a67cab9d1eeb4b7d76" +checksum = "b893c4eb2dc092c811165f84dc7447fae16fb66521717968c34c509b39b1a5c5" dependencies = [ "proc-macro2 1.0.66", - "quote 1.0.31", - "syn 2.0.26", + "quote 1.0.32", + "syn 2.0.27", ] [[package]] name = "equivalent" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" @@ -1908,9 +1915,9 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fda3bf123be441da5260717e0661c25a2fd9cb2b2c1d20bf2e05580047158ab" dependencies = [ - "aes 0.8.2", + "aes 0.8.3", "ctr 0.9.2", - "digest 0.10.6", + "digest 0.10.7", "hex", "hmac", "pbkdf2 0.11.0", @@ -1918,7 +1925,7 @@ dependencies = [ "scrypt", "serde", "serde_json", - "sha2 0.10.6", + "sha2 0.10.7", "sha3", "thiserror", "uuid 0.8.2", @@ -2005,12 +2012,12 @@ dependencies = [ "hex", "prettyplease", "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "regex", "serde", "serde_json", - "syn 2.0.26", - "toml 0.7.5", + "syn 2.0.27", + "toml 0.7.6", "walkdir", ] @@ -2025,9 +2032,9 @@ dependencies = [ "ethers-core", "hex", "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "serde_json", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -2053,7 +2060,7 @@ dependencies = [ "serde", "serde_json", "strum 0.25.0", - "syn 2.0.26", + "syn 2.0.27", "tempfile", "thiserror", "tiny-keccak", @@ -2110,9 +2117,9 @@ checksum = "b411b119f1cf0efb69e2190883dee731251882bb21270f893ee9513b3a697c48" dependencies = [ "async-trait", "auto_impl", - "base64 0.21.0", + "base64 0.21.2", "bytes", - "enr", + "enr 0.8.1", "ethers-core", "futures-channel", "futures-core", @@ -2153,7 +2160,7 @@ dependencies = [ "ethers-core", "hex", "rand 0.8.5", - "sha2 0.10.6", + "sha2 0.10.7", "thiserror", "tracing", ] @@ -2217,6 +2224,12 @@ dependencies = [ "instant", ] +[[package]] +name = "fastrand" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" + [[package]] name = "fdlimit" version = "0.2.1" @@ -2268,9 +2281,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" dependencies = [ "crc32fast", "miniz_oxide", @@ -2293,9 +2306,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] @@ -2377,8 +2390,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2 1.0.66", - "quote 1.0.31", - "syn 2.0.26", + "quote 1.0.32", + "syn 2.0.27", ] [[package]] @@ -2454,9 +2467,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "js-sys", @@ -2477,9 +2490,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.27.2" +version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" [[package]] name = "glob" @@ -2489,12 +2502,12 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "globset" -version = "0.4.10" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" +checksum = "aca8bbd8e0707c1887a8bbb7e6b40e228f251ff5d62c8220a4a7a53c73aff006" dependencies = [ - "aho-corasick 0.7.20", - "bstr 1.3.0", + "aho-corasick 1.0.2", + "bstr 1.6.0", "fnv", "log", "regex", @@ -2502,14 +2515,15 @@ dependencies = [ [[package]] name = "gloo-net" -version = "0.2.6" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9902a044653b26b99f7e3693a42f171312d9be8b26b5697bd1e43ad1f8a35e10" +checksum = "a66b4e3c7d9ed8d315fd6b97c8b1f74a7c6ecbbc2320e65ae7ed38b7068cc620" dependencies = [ "futures-channel", "futures-core", "futures-sink", "gloo-utils", + "http", "js-sys", "pin-project", "serde", @@ -2534,9 +2548,9 @@ dependencies = [ [[package]] name = "gloo-utils" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8e8fc851e9c7b9852508bc6e3f690f452f474417e8545ec9857b7f7377036b5" +checksum = "037fcb07216cb3a30f7292bd0176b050b7b9a052ba830ef7d5d65f6dc64ba58e" dependencies = [ "js-sys", "serde", @@ -2558,9 +2572,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.18" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" +checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" dependencies = [ "bytes", "fnv", @@ -2680,18 +2694,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" [[package]] name = "hex" @@ -2729,16 +2734,16 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", ] [[package]] name = "home" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747309b4b440c06d57b0b25f2aee03ee9b5e5397d288c60e21fc709bb98a7408" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" dependencies = [ - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -2776,9 +2781,9 @@ dependencies = [ [[package]] name = "http-range-header" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" +checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" [[package]] name = "httparse" @@ -2794,9 +2799,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "human_bytes" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39b528196c838e8b3da8b665e08c30958a6f2ede91d79f2ffcd0d4664b9c64eb" +checksum = "27e2b089f28ad15597b48d8c0a8fe94eeb1c1cb26ca99b6f66ac9582ae10c5e6" [[package]] name = "humantime" @@ -2816,9 +2821,9 @@ dependencies = [ [[package]] name = "hyper" -version = "0.14.25" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -2831,7 +2836,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2", + "socket2 0.4.9", "tokio", "tower-service", "tracing", @@ -2840,10 +2845,11 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.24.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0646026eb1b3eea4cd9ba47912ea5ce9cc07713d105b1a14698f4e6433d348b7" +checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" dependencies = [ + "futures-util", "http", "hyper", "log", @@ -2875,26 +2881,25 @@ checksum = "71a816c97c42258aa5834d07590b718b4c9a598944cd39a52dc25b351185d678" [[package]] name = "iana-time-zone" -version = "0.1.53" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "winapi", + "windows", ] [[package]] name = "iana-time-zone-haiku" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ - "cxx", - "cxx-build", + "cc", ] [[package]] @@ -2931,7 +2936,7 @@ dependencies = [ "icu_collections", "icu_properties", "icu_provider", - "smallvec", + "smallvec 1.11.0", "utf16_iter", "utf8_iter", "write16", @@ -2975,7 +2980,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b728b9421e93eff1d9f8681101b78fa745e0748c95c655c83f337044a7e10" dependencies = [ "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "syn 1.0.109", ] @@ -2998,9 +3003,9 @@ dependencies = [ [[package]] name = "idna" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -3063,10 +3068,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "syn 1.0.109", ] +[[package]] +name = "include_dir" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18762faeff7122e89e0857b02f7ce6fcc0d101d5e9ad2ad7846cc01d61b7f19e" +dependencies = [ + "include_dir_macros", +] + +[[package]] +name = "include_dir_macros" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" +dependencies = [ + "proc-macro2 1.0.66", + "quote 1.0.32", +] + [[package]] name = "indenter" version = "0.3.3" @@ -3133,32 +3157,32 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ - "hermit-abi 0.3.1", + "hermit-abi", "libc", "windows-sys 0.48.0", ] [[package]] name = "ipconfig" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd302af1b90f2463a98fa5ad469fc212c8e3175a41c3068601bfa2727591c5be" +checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ - "socket2", + "socket2 0.5.3", "widestring", - "winapi", - "winreg", + "windows-sys 0.48.0", + "winreg 0.50.0", ] [[package]] name = "ipnet" -version = "2.7.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" [[package]] name = "iri-string" @@ -3172,13 +3196,12 @@ dependencies = [ [[package]] name = "is-terminal" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ - "hermit-abi 0.3.1", - "io-lifetimes", - "rustix 0.37.11", + "hermit-abi", + "rustix 0.38.4", "windows-sys 0.48.0", ] @@ -3202,15 +3225,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "jemalloc-ctl" -version = "0.5.0" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1891c671f3db85d8ea8525dd43ab147f9977041911d24a03e5a36187a7bfde9" +checksum = "7cffc705424a344c054e135d12ee591402f4539245e8bbd64e6c9eaa9458b63c" dependencies = [ "jemalloc-sys", "libc", @@ -3219,9 +3242,9 @@ dependencies = [ [[package]] name = "jemalloc-sys" -version = "0.5.3+5.3.0-patched" +version = "0.5.4+5.3.0-patched" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9bd5d616ea7ed58b571b2e209a65759664d7fb021a0819d7a790afc67e47ca1" +checksum = "ac6c1946e1cea1788cbfde01c993b52a10e2da07f4bac608228d1bed20bfebf2" dependencies = [ "cc", "libc", @@ -3229,9 +3252,9 @@ dependencies = [ [[package]] name = "jemallocator" -version = "0.5.0" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16c2514137880c52b0b4822b563fadd38257c1f380858addb74a400889696ea6" +checksum = "a0de374a9f8e63150e6f5e8a60cc14c668226d7a347d8aee1a45766e3c4dd3bc" dependencies = [ "jemalloc-sys", "libc", @@ -3248,18 +3271,18 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.61" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] [[package]] name = "jsonrpsee" -version = "0.18.2" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1822d18e4384a5e79d94dc9e4d1239cfa9fad24e55b44d2efeff5b394c9fece4" +checksum = "e5f3783308bddc49d0218307f66a09330c106fbd792c58bac5c8dc294fdd0f98" dependencies = [ "jsonrpsee-client-transport", "jsonrpsee-core", @@ -3274,9 +3297,9 @@ dependencies = [ [[package]] name = "jsonrpsee-client-transport" -version = "0.18.2" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11aa5766d5c430b89cb26a99b88f3245eb91534be8126102cea9e45ee3891b22" +checksum = "abc5630e4fa0096f00ec7b44d520701fda4504170cb85e22dca603ae5d7ad0d7" dependencies = [ "futures-channel", "futures-util", @@ -3296,9 +3319,9 @@ dependencies = [ [[package]] name = "jsonrpsee-core" -version = "0.18.2" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c6832a55f662b5a6ecc844db24b8b9c387453f923de863062c60ce33d62b81" +checksum = "5aaa4c4d5fb801dcc316d81f76422db259809037a86b3194ae538dd026b05ed7" dependencies = [ "anyhow", "async-lock", @@ -3324,9 +3347,9 @@ dependencies = [ [[package]] name = "jsonrpsee-http-client" -version = "0.18.2" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1705c65069729e3dccff6fd91ee431d5d31cabcf00ce68a62a2c6435ac713af9" +checksum = "aa7165efcbfbc951d180162ff28fe91b657ed81925e37a35e4a396ce12109f96" dependencies = [ "async-trait", "hyper", @@ -3343,22 +3366,22 @@ dependencies = [ [[package]] name = "jsonrpsee-proc-macros" -version = "0.18.2" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6027ac0b197ce9543097d02a290f550ce1d9432bf301524b013053c0b75cc94" +checksum = "21dc12b1d4f16a86e8c522823c4fab219c88c03eb7c924ec0501a64bf12e058b" dependencies = [ "heck", "proc-macro-crate", "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "syn 1.0.109", ] [[package]] name = "jsonrpsee-server" -version = "0.18.2" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f06661d1a6b6e5b85469dc9c29acfbb9b3bb613797a6fd10a3ebb8a70754057" +checksum = "6e79d78cfd5abd8394da10753723093c3ff64391602941c9c4b1d80a3414fd53" dependencies = [ "futures-util", "hyper", @@ -3376,9 +3399,9 @@ dependencies = [ [[package]] name = "jsonrpsee-types" -version = "0.18.2" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e5bf6c75ce2a4217421154adfc65a24d2b46e77286e59bba5d9fa6544ccc8f4" +checksum = "00aa7cc87bc42e04e26c8ac3e7186142f7fd2949c763d9b6a7e64a69672d8fb2" dependencies = [ "anyhow", "beef", @@ -3390,9 +3413,9 @@ dependencies = [ [[package]] name = "jsonrpsee-wasm-client" -version = "0.18.2" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34e6ea7c6d862e60f8baebd946c037b70c6808a4e4e31e792a4029184e3ce13a" +checksum = "0fe953c2801356f214d3f4051f786b3d11134512a46763ee8c39a9e3fa2cc1c0" dependencies = [ "jsonrpsee-client-transport", "jsonrpsee-core", @@ -3401,9 +3424,9 @@ dependencies = [ [[package]] name = "jsonrpsee-ws-client" -version = "0.18.2" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a64b2589680ba1ad7863f279cd2d5083c1dc0a7c0ea959d22924553050f8ab9f" +checksum = "5c71b2597ec1c958c6d5bc94bb61b44d74eb28e69dc421731ab0035706f13882" dependencies = [ "http", "jsonrpsee-client-transport", @@ -3413,11 +3436,11 @@ dependencies = [ [[package]] name = "jsonwebtoken" -version = "8.2.0" +version = "8.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09f4f04699947111ec1733e71778d763555737579e44b85844cae8e1940a1828" +checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" dependencies = [ - "base64 0.13.1", + "base64 0.21.2", "pem", "ring", "serde", @@ -3435,15 +3458,15 @@ dependencies = [ "ecdsa", "elliptic-curve", "once_cell", - "sha2 0.10.6", + "sha2 0.10.7", "signature", ] [[package]] name = "keccak" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" dependencies = [ "cpufeatures", ] @@ -3481,15 +3504,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" - -[[package]] -name = "libm" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" +checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" [[package]] name = "libproc" @@ -3511,15 +3528,6 @@ dependencies = [ "bytes", ] -[[package]] -name = "link-cplusplus" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" -dependencies = [ - "cc", -] - [[package]] name = "linked-hash-map" version = "0.5.6" @@ -3543,9 +3551,9 @@ checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" [[package]] name = "linux-raw-sys" -version = "0.3.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" +checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" [[package]] name = "litemap" @@ -3580,9 +3588,9 @@ dependencies = [ [[package]] name = "lru" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03f1160296536f10c833a82dca22267d5486734230d47bf00bf435885814ba1e" +checksum = "718e8fae447df0c7e1ba7f5189829e63fd536945c8988d61444c19039f16b670" dependencies = [ "hashbrown 0.13.2", ] @@ -3626,7 +3634,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" dependencies = [ - "regex-automata", + "regex-automata 0.1.10", ] [[package]] @@ -3635,6 +3643,12 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" +[[package]] +name = "maybe-uninit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" + [[package]] name = "memchr" version = "2.5.0" @@ -3652,22 +3666,33 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" dependencies = [ "autocfg", ] [[package]] name = "metrics" -version = "0.20.1" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b9b8653cec6897f73b519a43fba5ee3d50f62fe9af80b428accdcc093b4a849" +dependencies = [ + "ahash 0.7.6", + "metrics-macros 0.6.0", + "portable-atomic 0.3.20", +] + +[[package]] +name = "metrics" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b9b8653cec6897f73b519a43fba5ee3d50f62fe9af80b428accdcc093b4a849" +checksum = "fde3af1a009ed76a778cb84fdef9e7dbbdf5775ae3e4cc1f434a6a307f6f76c5" dependencies = [ - "ahash 0.7.6", - "metrics-macros", - "portable-atomic", + "ahash 0.8.3", + "metrics-macros 0.7.0", + "portable-atomic 1.4.2", ] [[package]] @@ -3679,10 +3704,10 @@ dependencies = [ "hyper", "indexmap 1.9.3", "ipnet", - "metrics", + "metrics 0.20.1", "metrics-util", "parking_lot 0.12.1", - "portable-atomic", + "portable-atomic 0.3.20", "quanta", "thiserror", "tokio", @@ -3696,19 +3721,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "731f8ecebd9f3a4aa847dfe75455e4757a45da40a7793d2f0b1f9b6ed18b23f3" dependencies = [ "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "syn 1.0.109", ] +[[package]] +name = "metrics-macros" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddece26afd34c31585c74a4db0630c376df271c285d682d1e55012197830b6df" +dependencies = [ + "proc-macro2 1.0.66", + "quote 1.0.32", + "syn 2.0.27", +] + [[package]] name = "metrics-process" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99eab79be9f7c18565e889d6eaed6f1ebdafb2b6a88aef446d2fee5e7796ed10" +checksum = "006271a8019ad7a9a28cfac2cc40e3ee104d54be763c4a0901e228a63f49d706" dependencies = [ "libproc", "mach2", - "metrics", + "metrics 0.21.1", "once_cell", "procfs", "rlimit", @@ -3726,11 +3762,11 @@ dependencies = [ "crossbeam-utils", "hashbrown 0.12.3", "indexmap 1.9.3", - "metrics", + "metrics 0.20.1", "num_cpus", "ordered-float", "parking_lot 0.12.1", - "portable-atomic", + "portable-atomic 0.3.20", "quanta", "radix_trie", "sketches-ddsketch", @@ -3738,9 +3774,9 @@ dependencies = [ [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mime_guess" @@ -3760,9 +3796,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.6.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", ] @@ -3781,9 +3817,9 @@ dependencies = [ [[package]] name = "mockall" -version = "0.11.3" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50e4a1c770583dac7ab5e2f6c139153b783a53a1bbee9729613f193e59828326" +checksum = "4c84490118f2ee2d74570d114f3d0493cbf02790df303d2707606c3e14e07c96" dependencies = [ "cfg-if", "downcast", @@ -3796,13 +3832,13 @@ dependencies = [ [[package]] name = "mockall_derive" -version = "0.11.3" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "832663583d5fa284ca8810bf7015e46c9fff9622d3cf34bd1eea5003fec06dd0" +checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" dependencies = [ "cfg-if", "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "syn 1.0.109", ] @@ -3823,7 +3859,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" dependencies = [ "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "syn 1.0.109", ] @@ -3839,7 +3875,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" dependencies = [ - "smallvec", + "smallvec 1.11.0", ] [[package]] @@ -3854,6 +3890,12 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + [[package]] name = "nom" version = "7.1.3" @@ -3882,9 +3924,9 @@ dependencies = [ [[package]] name = "num" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" +checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" dependencies = [ "num-bigint", "num-complex", @@ -3960,21 +4002,21 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", - "libm 0.2.6", + "libm", ] [[package]] name = "num_cpus" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.2.6", + "hermit-abi", "libc", ] @@ -3995,15 +4037,24 @@ checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" dependencies = [ "proc-macro-crate", "proc-macro2 1.0.66", - "quote 1.0.31", - "syn 2.0.26", + "quote 1.0.32", + "syn 2.0.27", +] + +[[package]] +name = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", ] [[package]] name = "object" -version = "0.30.3" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" dependencies = [ "memchr", ] @@ -4014,7 +4065,7 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" dependencies = [ - "atomic-polyfill 1.0.2", + "atomic-polyfill 1.0.3", "critical-section", ] @@ -4051,7 +4102,7 @@ checksum = "003b2be5c6c53c1cfeb0a238b8a1c3915cd410feb684457a36c10038f764bb1c" dependencies = [ "bytes", "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "syn 1.0.109", ] @@ -4061,6 +4112,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "ordered-float" version = "2.10.0" @@ -4070,37 +4127,12 @@ dependencies = [ "num-traits", ] -[[package]] -name = "os_str_bytes" -version = "6.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" - -[[package]] -name = "output_vt100" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" -dependencies = [ - "winapi", -] - [[package]] name = "overload" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" -[[package]] -name = "packed_simd_2" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1914cd452d8fccd6f9db48147b29fd4ae05bea9dc5d9ad578509f72415de282" -dependencies = [ - "cfg-if", - "libm 0.1.4", -] - [[package]] name = "page_size" version = "0.4.2" @@ -4113,9 +4145,9 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.4.0" +version = "3.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "637935964ff85a605d114591d4d2c13c5d1ba2806dae97cea6bf180238a749ac" +checksum = "dd8e946cc0cc711189c0b0249fb8b599cbeeab9784d83c415719368bb8d4ac64" dependencies = [ "arrayvec", "bitvec 1.0.1", @@ -4128,13 +4160,13 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.1.4" +version = "3.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" +checksum = "2a296c3079b5fefbc499e1de58dc26c09b1b9a5952d26694ee89f04a43ebbb3e" dependencies = [ "proc-macro-crate", "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "syn 1.0.109", ] @@ -4183,7 +4215,7 @@ dependencies = [ "instant", "libc", "redox_syscall 0.2.16", - "smallvec", + "smallvec 1.11.0", "winapi", ] @@ -4196,15 +4228,15 @@ dependencies = [ "cfg-if", "libc", "redox_syscall 0.3.5", - "smallvec", + "smallvec 1.11.0", "windows-targets 0.48.1", ] [[package]] name = "paste" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pbkdf2" @@ -4212,16 +4244,16 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", ] [[package]] name = "pbkdf2" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0ca0b5a68607598bf3bad68f32227a8164f6254833f84eafaac409cd6746c31" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", "hmac", ] @@ -4242,9 +4274,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pharos" @@ -4268,9 +4300,9 @@ dependencies = [ [[package]] name = "phf_generator" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1181c94580fa345f50f19d738aaa39c0ed30a600d95cb2d3e23f94266f14fbf" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" dependencies = [ "phf_shared", "rand 0.8.5", @@ -4285,8 +4317,8 @@ dependencies = [ "phf_generator", "phf_shared", "proc-macro2 1.0.66", - "quote 1.0.31", - "syn 2.0.26", + "quote 1.0.32", + "syn 2.0.27", ] [[package]] @@ -4300,22 +4332,22 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead" +checksum = "030ad2bc4db10a8944cb0d837f158bdfec4d4a4873ab701a95046770d11f8842" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" +checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" dependencies = [ "proc-macro2 1.0.66", - "quote 1.0.31", - "syn 2.0.26", + "quote 1.0.32", + "syn 2.0.27", ] [[package]] @@ -4342,9 +4374,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "plain_hasher" @@ -4363,9 +4395,9 @@ checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630" [[package]] name = "plotters" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97" +checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" dependencies = [ "num-traits", "plotters-backend", @@ -4376,15 +4408,15 @@ dependencies = [ [[package]] name = "plotters-backend" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142" +checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" [[package]] name = "plotters-svg" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f" +checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" dependencies = [ "plotters-backend", ] @@ -4409,15 +4441,24 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "0.3.19" +version = "0.3.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e30165d31df606f5726b090ec7592c308a0eaf61721ff64c9a3018e344a8753e" +dependencies = [ + "portable-atomic 1.4.2", +] + +[[package]] +name = "portable-atomic" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26f6a7b87c2e435a3241addceeeff740ff8b7e76b74c13bf9acb17fa454ea00b" +checksum = "f32154ba0af3a075eefa1eda8bb414ee928f62303a54ea85b8d6638ff1a6ee9e" [[package]] name = "postcard" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfa512cd0d087cc9f99ad30a1bf64795b67871edbead083ffc3a4dfafa59aa00" +checksum = "c9ee729232311d3cd113749948b689627618133b1c5012b77342c1950b25eaeb" dependencies = [ "cobs", "heapless", @@ -4426,9 +4467,9 @@ dependencies = [ [[package]] name = "pprof" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b90f8560ad8bd57b207b8293bc5226e48e89039a6e590c12a297d91b84c7e60" +checksum = "978385d59daf9269189d052ca8a84c1acfd0715c0599a5d5188d4acc078ca46a" dependencies = [ "backtrace", "cfg-if", @@ -4440,7 +4481,7 @@ dependencies = [ "nix", "once_cell", "parking_lot 0.12.1", - "smallvec", + "smallvec 1.11.0", "symbolic-demangle", "tempfile", "thiserror", @@ -4468,15 +4509,15 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f883590242d3c6fc5bf50299011695fa6590c2c70eac95ee1bdb9a733ad1a2" +checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" [[package]] name = "predicates-tree" -version = "1.0.7" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54ff541861505aabf6ea722d2131ee980b8276e10a1297b94e896dd8b621850d" +checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" dependencies = [ "predicates-core", "termtree", @@ -4484,24 +4525,22 @@ dependencies = [ [[package]] name = "pretty_assertions" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" dependencies = [ - "ctor", "diff", - "output_vt100", "yansi", ] [[package]] name = "prettyplease" -version = "0.2.4" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ceca8aaf45b5c46ec7ed39fff75f57290368c1846d33d24a122ca81416ab058" +checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62" dependencies = [ "proc-macro2 1.0.66", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -4536,7 +4575,7 @@ checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "syn 1.0.109", "version_check", ] @@ -4548,7 +4587,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "version_check", ] @@ -4580,21 +4619,20 @@ dependencies = [ "byteorder", "hex", "lazy_static", - "rustix 0.36.11", + "rustix 0.36.15", ] [[package]] name = "proptest" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29f1b898011ce9595050a68e60f90bad083ff2987a695a42357134c8381fba70" +checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" dependencies = [ "bit-set", "bitflags 1.3.2", "byteorder", "lazy_static", "num-traits", - "quick-error 2.0.1", "rand 0.8.5", "rand_chacha 0.3.1", "rand_xorshift", @@ -4658,12 +4696,6 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" -[[package]] -name = "quick-error" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" - [[package]] name = "quick-xml" version = "0.26.0" @@ -4684,9 +4716,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.31" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2 1.0.66", ] @@ -4772,7 +4804,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.9", + "getrandom 0.2.10", ] [[package]] @@ -4848,20 +4880,21 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "getrandom 0.2.9", + "getrandom 0.2.10", "redox_syscall 0.2.16", "thiserror", ] [[package]] name = "regex" -version = "1.8.1" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" dependencies = [ - "aho-corasick 1.0.1", + "aho-corasick 1.0.2", "memchr", - "regex-syntax 0.7.1", + "regex-automata 0.3.4", + "regex-syntax 0.7.4", ] [[package]] @@ -4873,6 +4906,17 @@ dependencies = [ "regex-syntax 0.6.29", ] +[[package]] +name = "regex-automata" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7b6d6190b7594385f61bd3911cd1be99dfddcfc365a4160cc2ab5bff4aed294" +dependencies = [ + "aho-corasick 1.0.2", + "memchr", + "regex-syntax 0.7.4", +] + [[package]] name = "regex-syntax" version = "0.6.29" @@ -4881,9 +4925,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.7.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] name = "regress" @@ -4901,7 +4945,7 @@ version = "0.11.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" dependencies = [ - "base64 0.21.0", + "base64 0.21.2", "bytes", "encoding_rs", "futures-core", @@ -4926,7 +4970,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "winreg", + "winreg 0.10.1", ] [[package]] @@ -4936,7 +4980,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" dependencies = [ "hostname", - "quick-error 1.2.3", + "quick-error", ] [[package]] @@ -4970,6 +5014,7 @@ dependencies = [ "reth-beacon-consensus", "reth-blockchain-tree", "reth-config", + "reth-consensus-common", "reth-db", "reth-discv4", "reth-downloaders", @@ -4992,6 +5037,7 @@ dependencies = [ "reth-tasks", "reth-tracing", "reth-transaction-pool", + "reth-trie", "secp256k1", "serde", "serde_json", @@ -4999,7 +5045,7 @@ dependencies = [ "tempfile", "thiserror", "tokio", - "toml 0.7.5", + "toml 0.7.6", "tracing", "tui", "vergen", @@ -5076,7 +5122,7 @@ dependencies = [ "aquamarine", "assert_matches", "linked_hash_set", - "lru 0.10.0", + "lru 0.10.1", "parking_lot 0.12.1", "reth-db", "reth-interfaces", @@ -5177,7 +5223,7 @@ name = "reth-discv4" version = "0.1.0-alpha.4" dependencies = [ "discv5", - "enr", + "enr 0.8.1", "generic-array", "hex", "rand 0.8.5", @@ -5201,7 +5247,7 @@ version = "0.1.0-alpha.4" dependencies = [ "async-trait", "data-encoding", - "enr", + "enr 0.8.1", "linked_hash_set", "parking_lot 0.12.1", "reth-net-common", @@ -5226,7 +5272,7 @@ dependencies = [ "assert_matches", "futures", "futures-util", - "itertools 0.10.5", + "itertools 0.11.0", "pin-project", "rayon", "reth-db", @@ -5248,12 +5294,12 @@ dependencies = [ name = "reth-ecies" version = "0.1.0-alpha.4" dependencies = [ - "aes 0.8.2", + "aes 0.8.3", "block-padding", "byteorder", - "cipher 0.4.3", + "cipher 0.4.4", "ctr 0.9.2", - "digest 0.10.6", + "digest 0.10.7", "educe", "futures", "generic-array", @@ -5265,7 +5311,7 @@ dependencies = [ "reth-primitives", "reth-rlp", "secp256k1", - "sha2 0.10.6", + "sha2 0.10.7", "sha3", "thiserror", "tokio", @@ -5389,7 +5435,7 @@ name = "reth-metrics" version = "0.1.0-alpha.4" dependencies = [ "futures", - "metrics", + "metrics 0.20.1", "reth-metrics-derive", "tokio", ] @@ -5398,13 +5444,13 @@ dependencies = [ name = "reth-metrics-derive" version = "0.1.0-alpha.4" dependencies = [ - "metrics", + "metrics 0.20.1", "once_cell", "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "regex", "serial_test 0.10.0", - "syn 2.0.26", + "syn 2.0.27", "trybuild", ] @@ -5438,7 +5484,7 @@ dependencies = [ "aquamarine", "async-trait", "auto_impl", - "enr", + "enr 0.8.1", "ethers-core", "ethers-middleware", "ethers-providers", @@ -5506,7 +5552,7 @@ dependencies = [ "reth-rlp", "reth-rpc-types", "revm-primitives", - "sha2 0.10.6", + "sha2 0.10.7", "thiserror", "tokio", "tokio-stream", @@ -5554,11 +5600,11 @@ dependencies = [ "tiny-keccak", "tokio", "tokio-stream", - "toml 0.7.5", + "toml 0.7.6", "tracing", "triehash", "url", - "zstd 0.12.3+zstd.1.5.2", + "zstd", ] [[package]] @@ -5567,7 +5613,7 @@ version = "0.1.0-alpha.4" dependencies = [ "auto_impl", "derive_more", - "itertools 0.10.5", + "itertools 0.11.0", "parking_lot 0.12.1", "pin-project", "reth-db", @@ -5587,7 +5633,7 @@ name = "reth-prune" version = "0.1.0-alpha.4" dependencies = [ "assert_matches", - "itertools 0.10.5", + "itertools 0.11.0", "rayon", "reth-db", "reth-interfaces", @@ -5661,8 +5707,8 @@ name = "reth-rlp-derive" version = "0.1.0-alpha.4" dependencies = [ "proc-macro2 1.0.66", - "quote 1.0.31", - "syn 2.0.26", + "quote 1.0.32", + "syn 2.0.27", ] [[package]] @@ -5682,6 +5728,7 @@ dependencies = [ "jsonwebtoken", "pin-project", "rand 0.8.5", + "rayon", "reth-consensus-common", "reth-interfaces", "reth-metrics", @@ -5741,8 +5788,6 @@ version = "0.1.0-alpha.4" dependencies = [ "hyper", "jsonrpsee", - "pin-project", - "rayon", "reth-beacon-consensus", "reth-interfaces", "reth-ipc", @@ -5781,6 +5826,7 @@ dependencies = [ "reth-payload-builder", "reth-primitives", "reth-provider", + "reth-rlp", "reth-rpc-api", "reth-rpc-types", "reth-tasks", @@ -5793,10 +5839,9 @@ dependencies = [ name = "reth-rpc-types" version = "0.1.0-alpha.4" dependencies = [ - "assert_matches", + "itertools 0.11.0", "jsonrpsee-types", "rand 0.8.5", - "reth-interfaces", "reth-primitives", "reth-rlp", "serde", @@ -5814,7 +5859,7 @@ dependencies = [ "async-trait", "criterion", "futures-util", - "itertools 0.10.5", + "itertools 0.11.0", "num-traits", "paste", "pin-project", @@ -5872,10 +5917,12 @@ dependencies = [ "async-trait", "auto_impl", "bitflags 1.3.2", + "criterion", "fnv", "futures-util", "parking_lot 0.12.1", "paste", + "proptest", "rand 0.8.5", "reth-interfaces", "reth-metrics", @@ -5942,7 +5989,7 @@ dependencies = [ "revm-primitives", "ripemd", "secp256k1", - "sha2 0.10.6", + "sha2 0.10.7", "sha3", "substrate-bn", ] @@ -6011,7 +6058,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -6041,7 +6088,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" dependencies = [ "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "syn 1.0.109", ] @@ -6068,9 +6115,9 @@ checksum = "e666a5496a0b2186dbcd0ff6106e29e093c15591bde62c20d3842007c6978a09" [[package]] name = "rustc-demangle" -version = "0.1.21" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustc-hash" @@ -6095,12 +6142,12 @@ dependencies = [ [[package]] name = "rustix" -version = "0.36.11" +version = "0.36.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4165c9963ab29e422d6c26fbc1d37f15bace6b2810221f9d925023480fcf0e" +checksum = "c37f1bd5ef1b5422177b7646cba67430579cfe2ace80f284fee876bca52ad941" dependencies = [ "bitflags 1.3.2", - "errno 0.2.8", + "errno 0.3.1", "io-lifetimes", "libc", "linux-raw-sys 0.1.4", @@ -6109,23 +6156,22 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.11" +version = "0.38.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85597d61f83914ddeba6a47b3b8ffe7365107221c2e557ed94426489fefb5f77" +checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.3.3", "errno 0.3.1", - "io-lifetimes", "libc", - "linux-raw-sys 0.3.1", + "linux-raw-sys 0.4.3", "windows-sys 0.48.0", ] [[package]] name = "rustls" -version = "0.21.1" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c911ba11bc8433e811ce56fde130ccf32f5127cab0e0194e9c68c5a5b671791e" +checksum = "79ea77c539259495ce8ca47f53e66ae0330a8819f67e23ac96ca02f50e7b7d36" dependencies = [ "log", "ring", @@ -6135,9 +6181,9 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ "openssl-probe", "rustls-pemfile", @@ -6147,18 +6193,18 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" dependencies = [ - "base64 0.21.0", + "base64 0.21.2", ] [[package]] name = "rustls-webpki" -version = "0.100.1" +version = "0.101.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b" +checksum = "513722fd73ad80a71f72b61009ea1b584bcfa1483ca93949c8f290298837fa59" dependencies = [ "ring", "untrusted", @@ -6177,16 +6223,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" dependencies = [ "fnv", - "quick-error 1.2.3", + "quick-error", "tempfile", "wait-timeout", ] [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "ryu-js" @@ -6200,7 +6246,7 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" dependencies = [ - "cipher 0.4.3", + "cipher 0.4.4", ] [[package]] @@ -6214,9 +6260,9 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.5.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cfdffd972d76b22f3d7f81c8be34b2296afd3a25e0a547bd9abe340a4dbbe97" +checksum = "35c0a159d0c45c12b20c5a844feb1fe4bea86e28f17b92a5f0c42193634d3782" dependencies = [ "cfg-if", "derive_more", @@ -6226,23 +6272,23 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.5.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61fa974aea2d63dd18a4ec3a49d59af9f34178c73a4f56d2f18205628d00681e" +checksum = "912e55f6d20e0e80d63733872b40e1227c0bce1e1ab81ba67d696339bfd7fd29" dependencies = [ "proc-macro-crate", "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "syn 1.0.109", ] [[package]] name = "schannel" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys 0.42.0", + "windows-sys 0.48.0", ] [[package]] @@ -6258,15 +6304,9 @@ dependencies = [ [[package]] name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "scratch" -version = "1.0.4" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d5e082f6ea090deaf0e6dd04b68360fd5cddb152af6ce8927c9d25db299f98c" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "scrypt" @@ -6277,7 +6317,7 @@ dependencies = [ "hmac", "pbkdf2 0.11.0", "salsa20", - "sha2 0.10.6", + "sha2 0.10.7", ] [[package]] @@ -6292,9 +6332,9 @@ dependencies = [ [[package]] name = "sec1" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0aec48e813d6b90b15f0b8948af3c63483992dee44c03e9930b3eebdabe046e" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct", "der", @@ -6326,9 +6366,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.8.2" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ "bitflags 1.3.2", "core-foundation", @@ -6339,9 +6379,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.8.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", @@ -6349,9 +6389,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" dependencies = [ "serde", ] @@ -6370,38 +6410,40 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.171" +version = "1.0.178" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" +checksum = "60363bdd39a7be0266a520dab25fdc9241d2f987b08a01e01f0ec6d06a981348" dependencies = [ "serde_derive", ] [[package]] -name = "serde_bytes" -version = "0.11.9" +name = "serde-hex" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416bda436f9aab92e02c8e10d49a15ddd339cea90b6e340fe51ed97abb548294" +checksum = "ca37e3e4d1b39afd7ff11ee4e947efae85adfddf4841787bfa47c470e96dc26d" dependencies = [ + "array-init", "serde", + "smallvec 0.6.14", ] [[package]] name = "serde_derive" -version = "1.0.171" +version = "1.0.178" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" +checksum = "f28482318d6641454cb273da158647922d1be6b5a2fcc6165cd89ebdd7ed576b" dependencies = [ "proc-macro2 1.0.66", - "quote 1.0.31", - "syn 2.0.26", + "quote 1.0.32", + "syn 2.0.27", ] [[package]] name = "serde_json" -version = "1.0.103" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d03b412469450d4404fe8499a268edd7f8b79fecb074b0d812ad64ca21f4031b" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" dependencies = [ "itoa", "ryu", @@ -6431,9 +6473,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "2.2.0" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d904179146de381af4c93d3af6ca4984b3152db687dacb9c3c35e86f39809c" +checksum = "07ff71d2c147a7b57362cead5e22f772cd52f6ab31cfcd9edcd7f6aeb2a0afbe" dependencies = [ "base64 0.13.1", "chrono", @@ -6447,14 +6489,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "2.2.0" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1966009f3c05f095697c537312f5415d1e3ed31ce0a56942bac4c771c5c335e" +checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" dependencies = [ - "darling 0.14.3", + "darling 0.20.3", "proc-macro2 1.0.66", - "quote 1.0.31", - "syn 1.0.109", + "quote 1.0.32", + "syn 2.0.27", ] [[package]] @@ -6492,7 +6534,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b64f9e531ce97c88b4778aad0ceee079216071cffec6ac9b904277f8f92e7fe3" dependencies = [ "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "syn 1.0.109", ] @@ -6503,8 +6545,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ "proc-macro2 1.0.66", - "quote 1.0.31", - "syn 2.0.26", + "quote 1.0.32", + "syn 2.0.27", ] [[package]] @@ -6528,7 +6570,7 @@ checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -6539,7 +6581,7 @@ checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -6557,22 +6599,22 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.6", + "digest 0.10.7", ] [[package]] name = "sha3" -version = "0.10.7" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54c2bb1a323307527314a36bfb73f24febb08ce2b8a554bf4ffd6f51ad15198c" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", "keccak", ] @@ -6587,9 +6629,9 @@ dependencies = [ [[package]] name = "shellexpand" -version = "3.0.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd1c7ddea665294d484c39fd0c0d2b7e35bbfe10035c5fe1854741a57f6880e1" +checksum = "da03fa3b94cc19e3ebfc88c4229c49d8f08cdbd1228870a45f0ffdf84988e14b" dependencies = [ "dirs", ] @@ -6602,9 +6644,9 @@ checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" [[package]] name = "signal-hook" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b824b6e687aff278cdbf3b36f07aa52d4bd4099699324d5da86a2ebce3aa00b3" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" dependencies = [ "libc", "signal-hook-registry", @@ -6636,7 +6678,7 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", "rand_core 0.6.4", ] @@ -6680,9 +6722,9 @@ checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" [[package]] name = "sketches-ddsketch" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceb945e54128e09c43d8e4f1277851bd5044c6fc540bbaa2ad888f60b3da9ae7" +checksum = "68a406c1882ed7f29cd5e248c9848a80e7cb6ae0fea82346d2746f2f941c07e1" [[package]] name = "slab" @@ -6693,6 +6735,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "smallvec" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" +dependencies = [ + "maybe-uninit", +] + [[package]] name = "smallvec" version = "1.11.0" @@ -6724,6 +6775,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "socket2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "soketto" version = "0.7.1" @@ -6757,9 +6818,9 @@ dependencies = [ [[package]] name = "spki" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a5be806ab6f127c3da44b7378837ebf01dadca8510a0e572460216b228bd0e" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" dependencies = [ "base64ct", "der", @@ -6824,7 +6885,7 @@ checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck", "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "rustversion", "syn 1.0.109", ] @@ -6837,9 +6898,9 @@ checksum = "6069ca09d878a33f883cc06aaa9718ede171841d3832450354410b718b097232" dependencies = [ "heck", "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "rustversion", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -6882,21 +6943,21 @@ dependencies = [ [[package]] name = "symbolic-common" -version = "12.2.0" +version = "12.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f7afd8bcd36190409e6b71d89928f7f09d918a7aa3460d847bc49a538d672e" +checksum = "167a4ffd7c35c143fd1030aa3c2caf76ba42220bd5a6b5f4781896434723b8c3" dependencies = [ "debugid", "memmap2", "stable_deref_trait", - "uuid 1.3.0", + "uuid 1.4.1", ] [[package]] name = "symbolic-demangle" -version = "12.2.0" +version = "12.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec64922563a36e3fe686b6d99f06f25dacad2a202ac7502ed642930a188fb20a" +checksum = "e378c50e80686c1c5c205674e1f86a2858bec3d2a7dfdd690331a8a19330f293" dependencies = [ "cpp_demangle", "rustc-demangle", @@ -6921,18 +6982,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.26" +version = "2.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45c3457aacde3c65315de5031ec191ce46604304d2446e803d71ade03308d970" +checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" dependencies = [ "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "unicode-ident", ] @@ -6943,7 +7004,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "syn 1.0.109", "unicode-xid 0.2.4", ] @@ -6955,8 +7016,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "285ba80e733fac80aa4270fbcdf83772a79b80aa35c97075320abfee4a915b06" dependencies = [ "proc-macro2 1.0.66", - "quote 1.0.31", - "syn 2.0.26", + "quote 1.0.32", + "syn 2.0.27", "unicode-xid 0.2.4", ] @@ -6968,15 +7029,15 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.5.0" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +checksum = "5486094ee78b2e5038a6382ed7645bc084dc2ec433426ca4c3cb61e2007b8998" dependencies = [ "cfg-if", - "fastrand", + "fastrand 2.0.0", "redox_syscall 0.3.5", - "rustix 0.37.11", - "windows-sys 0.45.0", + "rustix 0.38.4", + "windows-sys 0.48.0", ] [[package]] @@ -6990,15 +7051,15 @@ dependencies = [ [[package]] name = "termtree" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95059e91184749cb66be6dc994f67f182b6d897cb3df74a5bf66b5e709295fd8" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "test-fuzz" -version = "4.0.0" +version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7bb2f404d5d20140588fb209481f5841920a7e29c36124f3d1ac1041eb1842c" +checksum = "954c28be70cafa409ddf615dd3c14a62478d38b44a94ade29469e82258cd5229" dependencies = [ "serde", "test-fuzz-internal", @@ -7008,40 +7069,40 @@ dependencies = [ [[package]] name = "test-fuzz-internal" -version = "4.0.0" +version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385624eb0031d550fe1bf99c08af79b838605fc4fcec2c4d55e229a2c342fdd0" +checksum = "0f0528a7ad0bc85f826aa831434a37833aea622a5ae155f5b5dd431b25244213" dependencies = [ "cargo_metadata", "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "serde", - "strum_macros 0.24.3", + "strum_macros 0.25.1", ] [[package]] name = "test-fuzz-macro" -version = "4.0.0" +version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69247423e2d89bd51160e42200f6f45f921a23e5b44a0e5b57b888a378334037" +checksum = "646c4ee44c47ae7888e88fb8441c857bc21136fa2c7d6a0a766b0caf609b35e8" dependencies = [ - "darling 0.20.1", + "darling 0.20.3", "if_chain", "itertools 0.10.5", "lazy_static", "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "subprocess", - "syn 2.0.26", + "syn 2.0.27", "test-fuzz-internal", "toolchain_find", ] [[package]] name = "test-fuzz-runtime" -version = "4.0.0" +version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5054a92d02b1a95a0120155d20aef49b5c5673ba8a65d6f4ce667c2a6f3146c" +checksum = "491cf4aba9f04a5fa35cd2f69f73e4f390779c864d20a96c357c3be16eb7d501" dependencies = [ "bincode", "hex", @@ -7059,22 +7120,22 @@ checksum = "aac81b6fd6beb5884b0cf3321b8117e6e5d47ecb6fc89f414cfdcca8b2fe2dd8" [[package]] name = "thiserror" -version = "1.0.43" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" +checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.43" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" +checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" dependencies = [ "proc-macro2 1.0.66", - "quote 1.0.31", - "syn 2.0.26", + "quote 1.0.32", + "syn 2.0.27", ] [[package]] @@ -7089,11 +7150,13 @@ dependencies = [ [[package]] name = "time" -version = "0.3.20" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" +checksum = "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446" dependencies = [ "itoa", + "libc", + "num_threads", "serde", "time-core", "time-macros", @@ -7101,15 +7164,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" [[package]] name = "time-macros" -version = "0.2.8" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" +checksum = "96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4" dependencies = [ "time-core", ] @@ -7160,11 +7223,12 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.28.2" +version = "1.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" dependencies = [ "autocfg", + "backtrace", "bytes", "libc", "mio", @@ -7172,7 +7236,7 @@ dependencies = [ "parking_lot 0.12.1", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.4.9", "tokio-macros", "windows-sys 0.48.0", ] @@ -7184,15 +7248,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2 1.0.66", - "quote 1.0.31", - "syn 2.0.26", + "quote 1.0.32", + "syn 2.0.27", ] [[package]] name = "tokio-rustls" -version = "0.24.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0d409377ff5b1e3ca6437aa86c1eb7d40c134bfec254e44c830defa92669db5" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ "rustls", "tokio", @@ -7200,9 +7264,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.12" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" dependencies = [ "futures-core", "pin-project-lite", @@ -7224,9 +7288,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ "bytes", "futures-core", @@ -7249,9 +7313,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebafdf5ad1220cb59e7d17cf4d2c72015297b75b19a10472f99b89225089240" +checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" dependencies = [ "serde", "serde_spanned", @@ -7270,9 +7334,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.19.11" +version = "0.19.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266f016b7f039eec8a1a80dfe6156b633d208b9fccca5e4db1d6775b0c4e34a7" +checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" dependencies = [ "indexmap 2.0.0", "serde", @@ -7317,13 +7381,13 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.4.0" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d1d42a9b3f3ec46ba828e8d376aec14592ea199f70a06a548587ecd1c4ab658" +checksum = "55ae70283aba8d2a8b411c695c437fe25b8b5e44e23e780662002fc72fb47a82" dependencies = [ "async-compression", - "base64 0.20.0", - "bitflags 1.3.2", + "base64 0.21.2", + "bitflags 2.3.3", "bytes", "futures-core", "futures-util", @@ -7342,7 +7406,7 @@ dependencies = [ "tower-layer", "tower-service", "tracing", - "uuid 1.3.0", + "uuid 1.4.1", ] [[package]] @@ -7383,20 +7447,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.23" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2 1.0.66", - "quote 1.0.31", - "syn 1.0.109", + "quote 1.0.32", + "syn 2.0.27", ] [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", "valuable", @@ -7438,16 +7502,16 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" dependencies = [ "matchers", "nu-ansi-term", "once_cell", "regex", "sharded-slab", - "smallvec", + "smallvec 1.11.0", "thread_local", "tracing", "tracing-core", @@ -7502,7 +7566,7 @@ dependencies = [ "lazy_static", "log", "rand 0.8.5", - "smallvec", + "smallvec 1.11.0", "thiserror", "tinyvec", "tokio", @@ -7526,7 +7590,7 @@ dependencies = [ "ipnet", "lazy_static", "rand 0.8.5", - "smallvec", + "smallvec 1.11.0", "thiserror", "tinyvec", "tokio", @@ -7547,7 +7611,7 @@ dependencies = [ "lru-cache", "parking_lot 0.12.1", "resolv-conf", - "smallvec", + "smallvec 1.11.0", "thiserror", "tokio", "tracing", @@ -7562,9 +7626,9 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "trybuild" -version = "1.0.78" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "223fc354447478d08231355617eb8c37affad0e83d33aeac30a8c275786b905a" +checksum = "a84e0202ea606ba5ebee8507ab2bfbe89b98551ed9b8f0be198109275cff284b" dependencies = [ "basic-toml", "glob", @@ -7642,9 +7706,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.10" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" @@ -7703,12 +7767,12 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" dependencies = [ "form_urlencoded", - "idna 0.3.0", + "idna 0.4.0", "percent-encoding", ] @@ -7730,23 +7794,29 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64a8922555b9500e3d865caed19330172cd67cbf82203f1a3311d8c305cc9f33" +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "uuid" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" dependencies = [ - "getrandom 0.2.9", + "getrandom 0.2.10", "serde", ] [[package]] name = "uuid" -version = "1.3.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79" +checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" dependencies = [ - "getrandom 0.2.9", + "getrandom 0.2.10", ] [[package]] @@ -7757,9 +7827,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "vergen" -version = "8.1.3" +version = "8.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e03272e388fb78fc79481a493424f78d77be1d55f21bcd314b5a6716e195afe" +checksum = "bbc5ad0d9d26b2c49a5ab7da76c3e79d3ee37e7821799f8223fcb8f2f391a2e7" dependencies = [ "anyhow", "rustversion", @@ -7793,11 +7863,10 @@ dependencies = [ [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -7821,9 +7890,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -7831,24 +7900,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2 1.0.66", - "quote 1.0.31", - "syn 1.0.109", + "quote 1.0.32", + "syn 2.0.27", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.34" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ "cfg-if", "js-sys", @@ -7858,38 +7927,38 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ - "quote 1.0.31", + "quote 1.0.32", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2 1.0.66", - "quote 1.0.31", - "syn 1.0.109", + "quote 1.0.32", + "syn 2.0.27", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "web-sys" -version = "0.3.61" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", @@ -7897,18 +7966,18 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.23.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa54963694b65584e170cf5dc46aeb4dcaa5584e652ff5f3952e56d66aff0125" +checksum = "b291546d5d9d1eab74f069c77749f2cb8504a12caa20f0f2de93ddbf6f411888" dependencies = [ "rustls-webpki", ] [[package]] name = "widestring" -version = "0.5.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983" +checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" [[package]] name = "wildmatch" @@ -7956,21 +8025,6 @@ dependencies = [ "windows-targets 0.48.1", ] -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - [[package]] name = "windows-sys" version = "0.45.0" @@ -8105,9 +8159,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.4.7" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca0ace3845f0d96209f0375e6d367e3eb87eb65d27d445bdc9f1843a26f39448" +checksum = "25b5872fa2e10bd067ae946f927e726d7d603eaeb6e02fa6a350e0722d2b8c11" dependencies = [ "memchr", ] @@ -8121,6 +8175,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "write16" version = "1.0.0" @@ -8163,9 +8227,9 @@ dependencies = [ [[package]] name = "xml-rs" -version = "0.8.14" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52839dc911083a8ef63efa4d039d1f58b5e409f923e44c80828f206f66e5541c" +checksum = "47430998a7b5d499ccee752b41567bc3afc57e1327dc855b1a2aa44ce29b5fa1" [[package]] name = "xmltree" @@ -8201,7 +8265,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af46c169923ed7516eef0aa32b56d2651b229f57458ebe46b49ddd6efef5b7a2" dependencies = [ "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "syn 1.0.109", "synstructure 0.12.6", ] @@ -8222,7 +8286,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4eae7c1f7d4b8eafce526bc0771449ddc2f250881ae31c50d22c032b5a1c499" dependencies = [ "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "syn 1.0.109", "synstructure 0.12.6", ] @@ -8243,8 +8307,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2 1.0.66", - "quote 1.0.31", - "syn 2.0.26", + "quote 1.0.32", + "syn 2.0.27", ] [[package]] @@ -8265,44 +8329,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "486558732d5dde10d0f8cb2936507c1bb21bc539d924c949baf5f36a58e51bac" dependencies = [ "proc-macro2 1.0.66", - "quote 1.0.31", + "quote 1.0.32", "syn 1.0.109", "synstructure 0.12.6", ] [[package]] name = "zstd" -version = "0.11.2+zstd.1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" -dependencies = [ - "zstd-safe 5.0.2+zstd.1.5.2", -] - -[[package]] -name = "zstd" -version = "0.12.3+zstd.1.5.2" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76eea132fb024e0e13fd9c2f5d5d595d8a967aa72382ac2f9d39fcc95afd0806" +checksum = "1a27595e173641171fc74a1232b7b1c7a7cb6e18222c11e9dfb9888fa424c53c" dependencies = [ - "zstd-safe 6.0.5+zstd.1.5.4", -] - -[[package]] -name = "zstd-safe" -version = "5.0.2+zstd.1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" -dependencies = [ - "libc", - "zstd-sys", + "zstd-safe", ] [[package]] name = "zstd-safe" -version = "6.0.5+zstd.1.5.4" +version = "6.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56d9e60b4b1758206c238a10165fbcae3ca37b01744e394c463463f6529d23b" +checksum = "ee98ffd0b48ee95e6c5168188e44a54550b1564d9d530ee21d5f0eaed1069581" dependencies = [ "libc", "zstd-sys", diff --git a/Cargo.toml b/Cargo.toml index ff1fe7c89ee..738636e6b42 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -110,12 +110,14 @@ ethers-middleware = { version = "2.0.8", default-features = false } ## misc bytes = "1.4" tracing = "0.1.0" +tracing-appender = "0.2" thiserror = "1.0.37" serde_json = "1.0.94" serde = { version = "1.0", default-features = false } rand = "0.8.5" strum = "0.25" rayon = "1.7" +itertools = "0.11" ### proc-macros proc-macro2 = "1.0" @@ -132,5 +134,10 @@ futures = "0.3.26" pin-project = "1.0.12" futures-util = "0.3.25" +## json +jsonrpsee = { version = "0.19" } +jsonrpsee-core = { version = "0.19" } +jsonrpsee-types = { version = "0.19" } + ## crypto secp256k1 = { version = "0.27.0", default-features = false, features = ["global-context", "rand-std", "recovery"] } diff --git a/bin/reth/Cargo.toml b/bin/reth/Cargo.toml index 5655605d943..6382057fc1f 100644 --- a/bin/reth/Cargo.toml +++ b/bin/reth/Cargo.toml @@ -21,6 +21,7 @@ reth-interfaces = { workspace = true, features = ["test-utils", "clap"] } reth-transaction-pool.workspace = true reth-beacon-consensus = { path = "../../crates/consensus/beacon" } reth-auto-seal-consensus = { path = "../../crates/consensus/auto-seal" } +reth-consensus-common = { path = "../../crates/consensus/common" } reth-blockchain-tree = { path = "../../crates/blockchain-tree" } reth-rpc-engine-api = { path = "../../crates/rpc/rpc-engine-api" } reth-rpc-builder = { path = "../../crates/rpc/rpc-builder" } @@ -37,6 +38,7 @@ reth-basic-payload-builder = { path = "../../crates/payload/basic" } reth-discv4 = { path = "../../crates/net/discv4" } reth-metrics.workspace = true reth-prune = { path = "../../crates/prune" } +reth-trie = { path = "../../crates/trie" } # crypto secp256k1 = { workspace = true, features = ["global-context", "rand-std", "recovery"] } @@ -92,6 +94,7 @@ jemalloc-ctl = { version = "0.5.0", optional = true } [features] default = ["jemalloc"] +#default = ["jemalloc", "optimism"] jemalloc = ["dep:jemallocator", "dep:jemalloc-ctl"] jemalloc-prof = ["jemalloc", "jemallocator?/profiling"] min-error-logs = ["tracing/release_max_level_error"] @@ -99,6 +102,17 @@ min-warn-logs = ["tracing/release_max_level_warn"] min-info-logs = ["tracing/release_max_level_info"] min-debug-logs = ["tracing/release_max_level_debug"] min-trace-logs = ["tracing/release_max_level_trace"] +optimism = [ + "reth-primitives/optimism", + "reth-revm/optimism", + "reth-interfaces/optimism", + "reth-rpc/optimism", + "reth-rpc-engine-api/optimism", + "reth-transaction-pool/optimism", + "reth-provider/optimism", + "reth-beacon-consensus/optimism", + "reth-basic-payload-builder/optimism", +] [build-dependencies] vergen = { version = "8.0.0", features = ["build", "cargo", "git", "gitcl"] } diff --git a/bin/reth/src/args/mod.rs b/bin/reth/src/args/mod.rs index dd4dd83d0b6..b3bd5bf2b6d 100644 --- a/bin/reth/src/args/mod.rs +++ b/bin/reth/src/args/mod.rs @@ -39,4 +39,10 @@ pub use txpool_args::TxPoolArgs; mod dev_args; pub use dev_args::DevArgs; +/// RollupArgs for configuring the op-reth rollup +#[cfg(feature = "optimism")] +mod rollup_args; +#[cfg(feature = "optimism")] +pub use rollup_args::RollupArgs; + pub mod utils; diff --git a/bin/reth/src/args/rollup_args.rs b/bin/reth/src/args/rollup_args.rs new file mode 100644 index 00000000000..887d04a1046 --- /dev/null +++ b/bin/reth/src/args/rollup_args.rs @@ -0,0 +1,14 @@ +//! clap [Args](clap::Args) for op-reth rollup configuration + +/// Parameters for rollup configuration +#[derive(Debug, clap::Args)] +#[command(next_help_heading = "Rollup")] +pub struct RollupArgs { + /// HTTP endpoint for the sequencer mempool + #[arg(long = "rollup.sequencer-http", value_name = "HTTP_URL")] + pub sequencer_http: Option, + + /// Disable transaction pool gossip + #[arg(long = "rollup.disable-tx-pool-gossip")] + pub disable_txpool_gossip: bool, +} diff --git a/bin/reth/src/args/rpc_server_args.rs b/bin/reth/src/args/rpc_server_args.rs index d05ce42b431..13e9f201673 100644 --- a/bin/reth/src/args/rpc_server_args.rs +++ b/bin/reth/src/args/rpc_server_args.rs @@ -1,6 +1,9 @@ //! clap [Args](clap::Args) for RPC related arguments. -use crate::args::GasPriceOracleArgs; +use crate::{ + args::GasPriceOracleArgs, + cli::ext::{NoopArgsExt, RethRpcConfig, RethRpcServerArgsExt}, +}; use clap::{ builder::{PossibleValue, RangedU64ValueParser, TypedValueParser}, Arg, Args, Command, @@ -52,20 +55,20 @@ pub(crate) const RPC_DEFAULT_MAX_CONNECTIONS: u32 = 100; pub(crate) const RPC_DEFAULT_MAX_TRACING_REQUESTS: u32 = 25; /// Parameters for configuring the rpc more granularity via CLI -#[derive(Debug, Args, PartialEq, Eq, Default)] +#[derive(Debug, Args)] #[command(next_help_heading = "RPC")] -pub struct RpcServerArgs { +pub struct RpcServerArgs { /// Enable the HTTP-RPC server #[arg(long, default_value_if("dev", "true", "true"))] pub http: bool, /// Http server address to listen on - #[arg(long = "http.addr")] - pub http_addr: Option, + #[arg(long = "http.addr", default_value_t = IpAddr::V4(Ipv4Addr::LOCALHOST))] + pub http_addr: IpAddr, /// Http server port to listen on - #[arg(long = "http.port")] - pub http_port: Option, + #[arg(long = "http.port", default_value_t = constants::DEFAULT_HTTP_RPC_PORT)] + pub http_port: u16, /// Rpc Modules to be configured for the HTTP server #[arg(long = "http.api", value_parser = RpcModuleSelectionValueParser::default())] @@ -80,12 +83,12 @@ pub struct RpcServerArgs { pub ws: bool, /// Ws server address to listen on - #[arg(long = "ws.addr")] - pub ws_addr: Option, + #[arg(long = "ws.addr", default_value_t = IpAddr::V4(Ipv4Addr::LOCALHOST))] + pub ws_addr: IpAddr, /// Ws server port to listen on - #[arg(long = "ws.port")] - pub ws_port: Option, + #[arg(long = "ws.port", default_value_t = constants::DEFAULT_WS_RPC_PORT)] + pub ws_port: u16, /// Origins from which to accept WebSocket requests #[arg(long = "ws.origins", name = "ws.origins")] @@ -104,12 +107,12 @@ pub struct RpcServerArgs { pub ipcpath: Option, /// Auth server address to listen on - #[arg(long = "authrpc.addr")] - pub auth_addr: Option, + #[arg(long = "authrpc.addr", default_value_t = IpAddr::V4(Ipv4Addr::LOCALHOST))] + pub auth_addr: IpAddr, /// Auth server port to listen on - #[arg(long = "authrpc.port")] - pub auth_port: Option, + #[arg(long = "authrpc.port", default_value_t = constants::DEFAULT_AUTH_PORT)] + pub auth_port: u16, /// Path to a JWT secret to use for authenticated RPC endpoints #[arg(long = "authrpc.jwtsecret", value_name = "PATH", global = true, required = false)] @@ -160,9 +163,13 @@ pub struct RpcServerArgs { /// Maximum number of env cache entries. #[arg(long, default_value_t = DEFAULT_ENV_CACHE_MAX_LEN)] pub env_cache_len: u32, + + /// Additional arguments for rpc. + #[clap(flatten)] + pub ext: Ext, } -impl RpcServerArgs { +impl RpcServerArgs { /// Returns the max request size in bytes. pub fn rpc_max_request_size_bytes(&self) -> u32 { self.rpc_max_request_size * 1024 * 1024 @@ -183,21 +190,6 @@ impl RpcServerArgs { ) } - /// Extracts the [EthConfig] from the args. - pub fn eth_config(&self) -> EthConfig { - EthConfig::default() - .max_tracing_requests(self.rpc_max_tracing_requests) - .rpc_gas_cap(self.rpc_gas_cap) - .gpo_config(self.gas_price_oracle_config()) - } - - /// Convenience function that returns whether ipc is enabled - /// - /// By default IPC is enabled therefor it is enabled if the `ipcdisable` is false. - fn is_ipc_enabled(&self) -> bool { - !self.ipcdisable - } - /// The execution layer and consensus layer clients SHOULD accept a configuration parameter: /// jwt-secret, which designates a file containing the hex-encoded 256 bit secret key to be used /// for verifying/generating JWT tokens. @@ -244,7 +236,7 @@ impl RpcServerArgs { events: Events, engine_api: Engine, jwt_secret: JwtSecret, - ) -> Result<(RpcServerHandle, AuthServerHandle), RpcError> + ) -> eyre::Result<(RpcServerHandle, AuthServerHandle)> where Provider: BlockReaderIdExt + HeaderProvider @@ -266,7 +258,7 @@ impl RpcServerArgs { let module_config = self.transport_rpc_module_config(); debug!(target: "reth::cli", http=?module_config.http(), ws=?module_config.ws(), "Using RPC module config"); - let (rpc_modules, auth_module) = RpcModuleBuilder::default() + let (mut rpc_modules, auth_module, mut registry) = RpcModuleBuilder::default() .with_provider(provider) .with_pool(pool) .with_network(network) @@ -274,6 +266,9 @@ impl RpcServerArgs { .with_executor(executor) .build_with_auth_server(module_config, engine_api); + // apply configured customization + self.ext.extend_rpc_modules(self, &mut registry, &mut rpc_modules)?; + let server_config = self.rpc_server_config(); let launch_rpc = rpc_modules.start_server(server_config).map_ok(|handle| { if let Some(url) = handle.ipc_endpoint() { @@ -295,7 +290,7 @@ impl RpcServerArgs { }); // launch servers concurrently - futures::future::try_join(launch_rpc, launch_auth).await + Ok(futures::future::try_join(launch_rpc, launch_auth).await?) } /// Convenience function for starting a rpc server with configs which extracted from cli args. @@ -356,10 +351,7 @@ impl RpcServerArgs { Network: NetworkInfo + Peers + Clone + 'static, Tasks: TaskSpawner + Clone + 'static, { - let socket_address = SocketAddr::new( - self.auth_addr.unwrap_or(IpAddr::V4(Ipv4Addr::LOCALHOST)), - self.auth_port.unwrap_or(constants::DEFAULT_AUTH_PORT), - ); + let socket_address = SocketAddr::new(self.auth_addr, self.auth_port); reth_rpc_builder::auth::launch( provider, @@ -427,10 +419,7 @@ impl RpcServerArgs { let mut config = RpcServerConfig::default(); if self.http { - let socket_address = SocketAddr::new( - self.http_addr.unwrap_or(IpAddr::V4(Ipv4Addr::LOCALHOST)), - self.http_port.unwrap_or(constants::DEFAULT_HTTP_RPC_PORT), - ); + let socket_address = SocketAddr::new(self.http_addr, self.http_port); config = config .with_http_address(socket_address) .with_http(self.http_ws_server_builder()) @@ -439,10 +428,7 @@ impl RpcServerArgs { } if self.ws { - let socket_address = SocketAddr::new( - self.ws_addr.unwrap_or(IpAddr::V4(Ipv4Addr::LOCALHOST)), - self.ws_port.unwrap_or(constants::DEFAULT_WS_RPC_PORT), - ); + let socket_address = SocketAddr::new(self.ws_addr, self.ws_port); config = config.with_ws_address(socket_address).with_ws(self.http_ws_server_builder()); } @@ -457,15 +443,26 @@ impl RpcServerArgs { /// Creates the [AuthServerConfig] from cli args. fn auth_server_config(&self, jwt_secret: JwtSecret) -> Result { - let address = SocketAddr::new( - self.auth_addr.unwrap_or(IpAddr::V4(Ipv4Addr::LOCALHOST)), - self.auth_port.unwrap_or(constants::DEFAULT_AUTH_PORT), - ); + let address = SocketAddr::new(self.auth_addr, self.auth_port); Ok(AuthServerConfig::builder(jwt_secret).socket_addr(address).build()) } } +impl RethRpcConfig for RpcServerArgs { + fn is_ipc_enabled(&self) -> bool { + // By default IPC is enabled therefor it is enabled if the `ipcdisable` is false. + !self.ipcdisable + } + + fn eth_config(&self) -> EthConfig { + EthConfig::default() + .max_tracing_requests(self.rpc_max_tracing_requests) + .rpc_gas_cap(self.rpc_gas_cap) + .gpo_config(self.gas_price_oracle_config()) + } +} + /// clap value parser for [RpcModuleSelection]. #[derive(Clone, Debug, Default)] #[non_exhaustive] diff --git a/bin/reth/src/args/txpool_args.rs b/bin/reth/src/args/txpool_args.rs index d1bd2326b69..2edfb20af69 100644 --- a/bin/reth/src/args/txpool_args.rs +++ b/bin/reth/src/args/txpool_args.rs @@ -2,7 +2,7 @@ use clap::Args; use reth_transaction_pool::{ - PoolConfig, SubPoolLimit, TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER, + PoolConfig, SubPoolLimit, DEFAULT_PRICE_BUMP, TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER, TXPOOL_SUBPOOL_MAX_SIZE_MB_DEFAULT, TXPOOL_SUBPOOL_MAX_TXS_DEFAULT, }; @@ -33,6 +33,10 @@ pub struct TxPoolArgs { /// Max number of executable transaction slots guaranteed per account #[arg(long = "txpool.max_account_slots", help_heading = "TxPool", default_value_t = TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER)] pub max_account_slots: usize, + + /// Price bump (in %) for the transaction pool underpriced check. + #[arg(long = "txpool.price_bump", help_heading = "TxPool", default_value_t = DEFAULT_PRICE_BUMP)] + pub price_bump: u128, } impl TxPoolArgs { @@ -52,6 +56,7 @@ impl TxPoolArgs { max_size: self.queued_max_size * 1024 * 1024, }, max_account_slots: self.max_account_slots, + price_bump: self.price_bump, } } } diff --git a/bin/reth/src/cli/ext.rs b/bin/reth/src/cli/ext.rs new file mode 100644 index 00000000000..11db2ca347d --- /dev/null +++ b/bin/reth/src/cli/ext.rs @@ -0,0 +1,94 @@ +//! Support for integrating customizations into the CLI. + +use clap::Args; +use reth_network_api::{NetworkInfo, Peers}; +use reth_provider::{ + BlockReaderIdExt, CanonStateSubscriptions, ChainSpecProvider, ChangeSetReader, EvmEnvProvider, + StateProviderFactory, +}; +use reth_rpc_builder::{EthConfig, RethModuleRegistry, TransportRpcModules}; +use reth_tasks::TaskSpawner; +use reth_transaction_pool::TransactionPool; +use std::fmt; + +/// A trait that allows for extending parts of the CLI with additional functionality. +pub trait RethCliExt { + /// Extends the rpc arguments for the node + type RpcExt: RethRpcServerArgsExt; +} + +impl RethCliExt for () { + type RpcExt = NoopArgsExt; +} + +/// An [Args] extension that does nothing. +#[derive(Debug, Clone, Copy, Default, Args)] +pub struct NoopArgsExt; + +/// A trait that provides configured RPC server. +/// +/// This provides all basic config values for the RPC server and is implemented by the +/// [RpcServerArgs](crate::args::RpcServerArgs) type. +pub trait RethRpcConfig { + /// Returns whether ipc is enabled. + fn is_ipc_enabled(&self) -> bool; + + /// The configured ethereum RPC settings. + fn eth_config(&self) -> EthConfig; + + // TODO extract more functions from RpcServerArgs +} + +/// A trait that allows further customization of the RPC server via CLI. +pub trait RethRpcServerArgsExt: fmt::Debug + clap::Args { + /// Allows for registering additional RPC modules for the transports. + /// + /// This is expected to call the merge functions of [TransportRpcModules], for example + /// [TransportRpcModules::merge_configured] + fn extend_rpc_modules( + &self, + config: &Conf, + registry: &mut RethModuleRegistry, + modules: &mut TransportRpcModules<()>, + ) -> eyre::Result<()> + where + Conf: RethRpcConfig, + Provider: BlockReaderIdExt + + StateProviderFactory + + EvmEnvProvider + + ChainSpecProvider + + ChangeSetReader + + Clone + + Unpin + + 'static, + Pool: TransactionPool + Clone + 'static, + Network: NetworkInfo + Peers + Clone + 'static, + Tasks: TaskSpawner + Clone + 'static, + Events: CanonStateSubscriptions + Clone + 'static; +} + +impl RethRpcServerArgsExt for NoopArgsExt { + fn extend_rpc_modules( + &self, + _config: &Conf, + _registry: &mut RethModuleRegistry, + _modules: &mut TransportRpcModules<()>, + ) -> eyre::Result<()> + where + Conf: RethRpcConfig, + Provider: BlockReaderIdExt + + StateProviderFactory + + EvmEnvProvider + + ChainSpecProvider + + ChangeSetReader + + Clone + + Unpin + + 'static, + Pool: TransactionPool + Clone + 'static, + Network: NetworkInfo + Peers + Clone + 'static, + Tasks: TaskSpawner + Clone + 'static, + Events: CanonStateSubscriptions + Clone + 'static, + { + Ok(()) + } +} diff --git a/bin/reth/src/cli.rs b/bin/reth/src/cli/mod.rs similarity index 58% rename from bin/reth/src/cli.rs rename to bin/reth/src/cli/mod.rs index c32b1ae17ed..79a1f7b9241 100644 --- a/bin/reth/src/cli.rs +++ b/bin/reth/src/cli/mod.rs @@ -1,6 +1,9 @@ //! CLI definition and entrypoint to executable use crate::{ - chain, config, db, debug_cmd, + args::utils::genesis_value_parser, + chain, + cli::ext::RethCliExt, + config, db, debug_cmd, dirs::{LogsDir, PlatformPath}, node, p2p, runner::CliRunner, @@ -8,45 +11,102 @@ use crate::{ version::{LONG_VERSION, SHORT_VERSION}, }; use clap::{ArgAction, Args, Parser, Subcommand}; +use reth_primitives::ChainSpec; use reth_tracing::{ tracing::{metadata::LevelFilter, Level, Subscriber}, tracing_subscriber::{filter::Directive, registry::LookupSpan, EnvFilter}, BoxedLayer, FileWorkerGuard, }; +use std::sync::Arc; -/// Parse CLI options, set up logging and run the chosen command. -pub fn run() -> eyre::Result<()> { - let opt = Cli::parse(); - - let mut layers = vec![reth_tracing::stdout(opt.verbosity.directive())]; - let _guard = opt.logs.layer()?.map(|(layer, guard)| { - layers.push(layer); - guard - }); - - reth_tracing::init(layers); - - let runner = CliRunner::default(); - - match opt.command { - Commands::Node(command) => runner.run_command_until_exit(|ctx| command.execute(ctx)), - Commands::Init(command) => runner.run_blocking_until_ctrl_c(command.execute()), - Commands::Import(command) => runner.run_blocking_until_ctrl_c(command.execute()), - Commands::Db(command) => runner.run_blocking_until_ctrl_c(command.execute()), - Commands::Stage(command) => runner.run_blocking_until_ctrl_c(command.execute()), - Commands::P2P(command) => runner.run_until_ctrl_c(command.execute()), - Commands::TestVectors(command) => runner.run_until_ctrl_c(command.execute()), - Commands::Config(command) => runner.run_until_ctrl_c(command.execute()), - Commands::Debug(command) => runner.run_command_until_exit(|ctx| command.execute(ctx)), +pub mod ext; + +/// The main reth cli interface. +/// +/// This is the entrypoint to the executable. +#[derive(Debug, Parser)] +#[command(author, version = SHORT_VERSION, long_version = LONG_VERSION, about = "Reth", long_about = None)] +pub struct Cli { + /// The command to run + #[clap(subcommand)] + command: Commands, + + /// The chain this node is running. + /// + /// Possible values are either a built-in chain or the path to a chain specification file. + /// + /// Built-in chains: + /// - mainnet + /// - goerli + /// - sepolia + #[arg( + long, + value_name = "CHAIN_OR_PATH", + global = true, + verbatim_doc_comment, + default_value = "mainnet", + value_parser = genesis_value_parser, + global = true, + )] + chain: Arc, + + #[clap(flatten)] + logs: Logs, + + #[clap(flatten)] + verbosity: Verbosity, +} + +impl Cli { + /// Execute the configured cli command. + pub fn run(mut self) -> eyre::Result<()> { + // add network name to logs dir + self.logs.log_directory = self.logs.log_directory.join(self.chain.chain.to_string()); + + let _guard = self.init_tracing()?; + + let runner = CliRunner::default(); + match self.command { + Commands::Node(command) => runner.run_command_until_exit(|ctx| command.execute(ctx)), + Commands::Init(command) => runner.run_blocking_until_ctrl_c(command.execute()), + Commands::Import(command) => runner.run_blocking_until_ctrl_c(command.execute()), + Commands::Db(command) => runner.run_blocking_until_ctrl_c(command.execute()), + Commands::Stage(command) => runner.run_blocking_until_ctrl_c(command.execute()), + Commands::P2P(command) => runner.run_until_ctrl_c(command.execute()), + Commands::TestVectors(command) => runner.run_until_ctrl_c(command.execute()), + Commands::Config(command) => runner.run_until_ctrl_c(command.execute()), + Commands::Debug(command) => runner.run_command_until_exit(|ctx| command.execute(ctx)), + } } + + /// Initializes tracing with the configured options. + /// + /// If file logging is enabled, this function returns a guard that must be kept alive to ensure + /// that all logs are flushed to disk. + pub fn init_tracing(&self) -> eyre::Result> { + let mut layers = vec![reth_tracing::stdout(self.verbosity.directive())]; + let guard = self.logs.layer()?.map(|(layer, guard)| { + layers.push(layer); + guard + }); + + reth_tracing::init(layers); + Ok(guard.flatten()) + } +} + +/// Convenience function for parsing CLI options, set up logging and run the chosen command. +#[inline] +pub fn run() -> eyre::Result<()> { + Cli::parse().run() } /// Commands to be executed #[derive(Debug, Subcommand)] -pub enum Commands { +pub enum Commands { /// Start the node #[command(name = "node")] - Node(node::Command), + Node(node::Command), /// Initialize the database from a genesis file. #[command(name = "init")] Init(chain::InitCommand), @@ -73,20 +133,6 @@ pub enum Commands { Debug(debug_cmd::Command), } -#[derive(Debug, Parser)] -#[command(author, version = SHORT_VERSION, long_version = LONG_VERSION, about = "Reth", long_about = None)] -struct Cli { - /// The command to run - #[clap(subcommand)] - command: Commands, - - #[clap(flatten)] - logs: Logs, - - #[clap(flatten)] - verbosity: Verbosity, -} - /// The log configuration. #[derive(Debug, Args)] #[command(next_help_heading = "Logging")] @@ -183,9 +229,9 @@ mod tests { /// runtime #[test] fn test_parse_help_all_subcommands() { - let reth = Cli::command(); + let reth = Cli::<()>::command(); for sub_command in reth.get_subcommands() { - let err = Cli::try_parse_from(["reth", sub_command.get_name(), "--help"]) + let err = Cli::<()>::try_parse_from(["reth", sub_command.get_name(), "--help"]) .err() .unwrap_or_else(|| { panic!("Failed to parse help message {}", sub_command.get_name()) @@ -196,4 +242,21 @@ mod tests { assert_eq!(err.kind(), clap::error::ErrorKind::DisplayHelp); } } + + /// Tests that the log directory is parsed correctly. It's always tied to the specific chain's + /// name + #[test] + fn parse_logs_path() { + let mut reth = Cli::<()>::try_parse_from(["reth", "node", "--log.persistent"]).unwrap(); + reth.logs.log_directory = reth.logs.log_directory.join(reth.chain.chain.to_string()); + let log_dir = reth.logs.log_directory; + assert!(log_dir.as_ref().ends_with("reth/logs/mainnet"), "{:?}", log_dir); + + let mut reth = + Cli::<()>::try_parse_from(["reth", "node", "--chain", "sepolia", "--log.persistent"]) + .unwrap(); + reth.logs.log_directory = reth.logs.log_directory.join(reth.chain.chain.to_string()); + let log_dir = reth.logs.log_directory; + assert!(log_dir.as_ref().ends_with("reth/logs/sepolia"), "{:?}", log_dir); + } } diff --git a/bin/reth/src/db/clear.rs b/bin/reth/src/db/clear.rs new file mode 100644 index 00000000000..0bd816db8b4 --- /dev/null +++ b/bin/reth/src/db/clear.rs @@ -0,0 +1,41 @@ +use clap::Parser; + +use reth_db::{ + database::Database, + table::Table, + transaction::{DbTx, DbTxMut}, + TableViewer, Tables, +}; + +/// The arguments for the `reth db clear` command +#[derive(Parser, Debug)] +pub struct Command { + /// Table name + #[arg()] + pub table: Tables, +} + +impl Command { + /// Execute `db clear` command + pub fn execute(self, db: &DB) -> eyre::Result<()> { + self.table.view(&ClearViewer { db })?; + + Ok(()) + } +} + +struct ClearViewer<'a, DB: Database> { + db: &'a DB, +} + +impl TableViewer<()> for ClearViewer<'_, DB> { + type Error = eyre::Report; + + fn view(&self) -> Result<(), Self::Error> { + let tx = self.db.tx_mut()?; + tx.clear::()?; + tx.commit()?; + + Ok(()) + } +} diff --git a/bin/reth/src/db/diff.rs b/bin/reth/src/db/diff.rs new file mode 100644 index 00000000000..b7527b0ab18 --- /dev/null +++ b/bin/reth/src/db/diff.rs @@ -0,0 +1,410 @@ +use std::{ + collections::HashMap, + fmt::Debug, + fs::{self, File}, + hash::Hash, + io::Write, + path::{Path, PathBuf}, +}; + +use crate::{ + args::DatabaseArgs, + dirs::{DataDirPath, PlatformPath}, + utils::DbTool, +}; +use clap::Parser; + +use reth_db::{ + cursor::DbCursorRO, database::Database, open_db_read_only, table::Table, transaction::DbTx, + AccountChangeSet, AccountHistory, AccountsTrie, BlockBodyIndices, BlockOmmers, + BlockWithdrawals, Bytecodes, CanonicalHeaders, DatabaseEnvRO, HashedAccount, HashedStorage, + HeaderNumbers, HeaderTD, Headers, PlainAccountState, PlainStorageState, PruneCheckpoints, + Receipts, StorageChangeSet, StorageHistory, StoragesTrie, SyncStage, SyncStageProgress, Tables, + TransactionBlock, Transactions, TxHashNumber, TxSenders, +}; +use tracing::info; + +#[derive(Parser, Debug)] +/// The arguments for the `reth db diff` command +pub struct Command { + /// The path to the data dir for all reth files and subdirectories. + #[arg(long, verbatim_doc_comment)] + secondary_datadir: PlatformPath, + + /// Arguments for the second database + #[clap(flatten)] + second_db: DatabaseArgs, + + /// The table name to diff. If not specified, all tables are diffed. + #[arg(long, verbatim_doc_comment)] + table: Option, + + /// The output directory for the diff report. + #[arg(long, verbatim_doc_comment)] + output: PlatformPath, +} + +impl Command { + /// Execute the `db diff` command. + /// + /// This first opens the `db/` folder from the secondary datadir, where the second database is + /// opened read-only. + /// + /// The tool will then iterate through all key-value pairs for the primary and secondary + /// databases. The value for each key will be compared with its corresponding value in the + /// other database. If the values are different, a discrepancy will be recorded in-memory. If + /// one key is present in one database but not the other, this will be recorded as an "extra + /// element" for that database. + /// + /// The discrepancies and extra elements, along with a brief summary of the diff results are + /// then written to a file in the output directory. + pub fn execute(self, tool: &DbTool<'_, DatabaseEnvRO>) -> eyre::Result<()> { + // open second db + let second_db_path: PathBuf = self.secondary_datadir.join("db").into(); + let second_db = open_db_read_only(&second_db_path, self.second_db.log_level)?; + + let tables = match self.table { + Some(table) => vec![table], + None => Tables::ALL.to_vec(), + }; + + for table in tables { + let primary_tx = tool.db.tx()?; + let secondary_tx = second_db.tx()?; + + let output_dir = self.output.clone(); + match table { + Tables::CanonicalHeaders => { + find_diffs::(primary_tx, secondary_tx, output_dir)? + } + Tables::HeaderTD => find_diffs::(primary_tx, secondary_tx, output_dir)?, + Tables::HeaderNumbers => { + find_diffs::(primary_tx, secondary_tx, output_dir)? + } + Tables::Headers => find_diffs::(primary_tx, secondary_tx, output_dir)?, + Tables::BlockBodyIndices => { + find_diffs::(primary_tx, secondary_tx, output_dir)? + } + Tables::BlockOmmers => { + find_diffs::(primary_tx, secondary_tx, output_dir)? + } + Tables::BlockWithdrawals => { + find_diffs::(primary_tx, secondary_tx, output_dir)? + } + Tables::TransactionBlock => { + find_diffs::(primary_tx, secondary_tx, output_dir)? + } + Tables::Transactions => { + find_diffs::(primary_tx, secondary_tx, output_dir)? + } + Tables::TxHashNumber => { + find_diffs::(primary_tx, secondary_tx, output_dir)? + } + Tables::Receipts => find_diffs::(primary_tx, secondary_tx, output_dir)?, + Tables::PlainAccountState => { + find_diffs::(primary_tx, secondary_tx, output_dir)? + } + Tables::PlainStorageState => { + find_diffs::(primary_tx, secondary_tx, output_dir)? + } + Tables::Bytecodes => find_diffs::(primary_tx, secondary_tx, output_dir)?, + Tables::AccountHistory => { + find_diffs::(primary_tx, secondary_tx, output_dir)? + } + Tables::StorageHistory => { + find_diffs::(primary_tx, secondary_tx, output_dir)? + } + Tables::AccountChangeSet => { + find_diffs::(primary_tx, secondary_tx, output_dir)? + } + Tables::StorageChangeSet => { + find_diffs::(primary_tx, secondary_tx, output_dir)? + } + Tables::HashedAccount => { + find_diffs::(primary_tx, secondary_tx, output_dir)? + } + Tables::HashedStorage => { + find_diffs::(primary_tx, secondary_tx, output_dir)? + } + Tables::AccountsTrie => { + find_diffs::(primary_tx, secondary_tx, output_dir)? + } + Tables::StoragesTrie => { + find_diffs::(primary_tx, secondary_tx, output_dir)? + } + Tables::TxSenders => find_diffs::(primary_tx, secondary_tx, output_dir)?, + Tables::SyncStage => find_diffs::(primary_tx, secondary_tx, output_dir)?, + Tables::SyncStageProgress => { + find_diffs::(primary_tx, secondary_tx, output_dir)? + } + Tables::PruneCheckpoints => { + find_diffs::(primary_tx, secondary_tx, output_dir)? + } + }; + } + + Ok(()) + } +} + +/// Find diffs for a table, then analyzing the result +fn find_diffs<'a, T: Table>( + primary_tx: impl DbTx<'a>, + secondary_tx: impl DbTx<'a>, + output_dir: impl AsRef, +) -> eyre::Result<()> +where + T::Key: Hash, + T::Value: PartialEq, +{ + let table_name = T::NAME; + + info!("Analyzing table {table_name}..."); + let result = find_diffs_advanced::(&primary_tx, &secondary_tx)?; + info!("Done analyzing table {table_name}!"); + + // Pretty info summary header: newline then header + info!(""); + info!("Diff results for {table_name}:"); + + // create directory and open file + fs::create_dir_all(output_dir.as_ref())?; + let file_name = format!("{table_name}.txt"); + let mut file = File::create(output_dir.as_ref().join(file_name.clone()))?; + + // analyze the result and print some stats + let discrepancies = result.discrepancies.len(); + let extra_elements = result.extra_elements.len(); + + // Make a pretty summary header for the table + writeln!(file, "Diff results for {table_name}")?; + + if discrepancies > 0 { + // write to file + writeln!(file, "Found {discrepancies} discrepancies in table {table_name}")?; + + // also print to info + info!("Found {discrepancies} discrepancies in table {table_name}"); + } else { + // write to file + writeln!(file, "No discrepancies found in table {table_name}")?; + + // also print to info + info!("No discrepancies found in table {table_name}"); + } + + if extra_elements > 0 { + // write to file + writeln!(file, "Found {extra_elements} extra elements in table {table_name}")?; + + // also print to info + info!("Found {extra_elements} extra elements in table {table_name}"); + } else { + writeln!(file, "No extra elements found in table {table_name}")?; + + // also print to info + info!("No extra elements found in table {table_name}"); + } + + info!("Writing diff results for {table_name} to {file_name}..."); + + if discrepancies > 0 { + writeln!(file, "Discrepancies:")?; + } + + for discrepancy in result.discrepancies.values() { + writeln!(file, "{discrepancy:?}")?; + } + + if extra_elements > 0 { + writeln!(file, "Extra elements:")?; + } + + for extra_element in result.extra_elements.values() { + writeln!(file, "{extra_element:?}")?; + } + + let full_file_name = output_dir.as_ref().join(file_name); + info!("Done writing diff results for {table_name} to {}", full_file_name.display()); + Ok(()) +} + +/// This diff algorithm is slightly different, it will walk _each_ table, cross-checking for the +/// element in the other table. +fn find_diffs_advanced<'a, T: Table>( + primary_tx: &impl DbTx<'a>, + secondary_tx: &impl DbTx<'a>, +) -> eyre::Result> +where + T::Value: PartialEq, + T::Key: Hash, +{ + // initialize the zipped walker + let mut primary_zip_cursor = + primary_tx.cursor_read::().expect("Was not able to obtain a cursor."); + let primary_walker = primary_zip_cursor.walk(None)?; + + let mut secondary_zip_cursor = + secondary_tx.cursor_read::().expect("Was not able to obtain a cursor."); + let secondary_walker = secondary_zip_cursor.walk(None)?; + let zipped_cursor = primary_walker.zip(secondary_walker); + + // initialize the cursors for seeking when we are cross checking elements + let mut primary_cursor = + primary_tx.cursor_read::().expect("Was not able to obtain a cursor."); + + let mut secondary_cursor = + secondary_tx.cursor_read::().expect("Was not able to obtain a cursor."); + + let mut result = TableDiffResult::::default(); + + // this loop will walk both tables, cross-checking for the element in the other table. + // it basically just loops through both tables at the same time. if the keys are different, it + // will check each key in the other table. if the keys are the same, it will compare the + // values + for (primary_entry, secondary_entry) in zipped_cursor { + let (primary_key, primary_value) = primary_entry?; + let (secondary_key, secondary_value) = secondary_entry?; + + if primary_key != secondary_key { + // if the keys are different, we need to check if the key is in the other table + let crossed_secondary = + secondary_cursor.seek_exact(primary_key.clone())?.map(|(_, value)| value); + result.try_push_discrepancy( + primary_key.clone(), + Some(primary_value), + crossed_secondary, + ); + + // now do the same for the primary table + let crossed_primary = + primary_cursor.seek_exact(secondary_key.clone())?.map(|(_, value)| value); + result.try_push_discrepancy( + secondary_key.clone(), + crossed_primary, + Some(secondary_value), + ); + } else { + // the keys are the same, so we need to compare the values + result.try_push_discrepancy(primary_key, Some(primary_value), Some(secondary_value)); + } + } + + Ok(result) +} + +/// Includes a table element between two databases with the same key, but different values +#[derive(Debug)] +struct TableDiffElement { + /// The key for the element + key: T::Key, + + /// The element from the first table + #[allow(dead_code)] + first: T::Value, + + /// The element from the second table + #[allow(dead_code)] + second: T::Value, +} + +/// The diff result for an entire table. If the tables had the same number of elements, there will +/// be no extra elements. +struct TableDiffResult +where + T::Key: Hash, +{ + /// All elements of the database that are different + discrepancies: HashMap>, + + /// Any extra elements, and the table they are in + extra_elements: HashMap>, +} + +impl Default for TableDiffResult +where + T: Table, + T::Key: Hash, +{ + fn default() -> Self { + Self { discrepancies: HashMap::new(), extra_elements: HashMap::new() } + } +} + +impl TableDiffResult +where + T::Key: Hash, +{ + /// Push a diff result into the discrepancies set. + fn push_discrepancy(&mut self, discrepancy: TableDiffElement) { + self.discrepancies.insert(discrepancy.key.clone(), discrepancy); + } + + /// Push an extra element into the extra elements set. + fn push_extra_element(&mut self, element: ExtraTableElement) { + self.extra_elements.insert(element.key().clone(), element); + } +} + +impl TableDiffResult +where + T: Table, + T::Key: Hash, + T::Value: PartialEq, +{ + /// Try to push a diff result into the discrepancy set, only pushing if the given elements are + /// different, and the discrepancy does not exist anywhere already. + fn try_push_discrepancy( + &mut self, + key: T::Key, + first: Option, + second: Option, + ) { + // do not bother comparing if the key is already in the discrepancies map + if self.discrepancies.contains_key(&key) { + return + } + + // do not bother comparing if the key is already in the extra elements map + if self.extra_elements.contains_key(&key) { + return + } + + match (first, second) { + (Some(first), Some(second)) => { + if first != second { + self.push_discrepancy(TableDiffElement { key, first, second }); + } + } + (Some(first), None) => { + self.push_extra_element(ExtraTableElement::First { key, value: first }); + } + (None, Some(second)) => { + self.push_extra_element(ExtraTableElement::Second { key, value: second }); + } + (None, None) => {} + } + } +} + +/// A single extra element from a table +#[derive(Debug)] +enum ExtraTableElement { + /// The extra element that is in the first table + #[allow(dead_code)] + First { key: T::Key, value: T::Value }, + + /// The extra element that is in the second table + #[allow(dead_code)] + Second { key: T::Key, value: T::Value }, +} + +impl ExtraTableElement { + /// Return the key for the extra element + fn key(&self) -> &T::Key { + match self { + Self::First { key, .. } => key, + Self::Second { key, .. } => key, + } + } +} diff --git a/bin/reth/src/db/mod.rs b/bin/reth/src/db/mod.rs index 4670683c43d..a56ff9d9a1f 100644 --- a/bin/reth/src/db/mod.rs +++ b/bin/reth/src/db/mod.rs @@ -17,6 +17,8 @@ use reth_db::{ use reth_primitives::ChainSpec; use std::sync::Arc; +mod clear; +mod diff; mod get; mod list; /// DB List TUI @@ -67,10 +69,14 @@ pub enum Subcommands { Stats, /// Lists the contents of a table List(list::Command), + /// Create a diff between two database tables or two entire databases. + Diff(diff::Command), /// Gets the content of a table for the given key Get(get::Command), /// Deletes all database entries Drop, + /// Deletes all table entries + Clear(clear::Command), /// Lists current and local database versions Version, /// Returns the full database path @@ -162,6 +168,11 @@ impl Command { let tool = DbTool::new(&db, self.chain.clone())?; command.execute(&tool)?; } + Subcommands::Diff(command) => { + let db = open_db_read_only(&db_path, self.db.log_level)?; + let tool = DbTool::new(&db, self.chain.clone())?; + command.execute(&tool)?; + } Subcommands::Get(command) => { let db = open_db_read_only(&db_path, self.db.log_level)?; let tool = DbTool::new(&db, self.chain.clone())?; @@ -172,6 +183,10 @@ impl Command { let mut tool = DbTool::new(&db, self.chain.clone())?; tool.drop(db_path)?; } + Subcommands::Clear(command) => { + let db = open_db(&db_path, self.db.log_level)?; + command.execute(&db)?; + } Subcommands::Version => { let local_db_version = match get_db_version(&db_path) { Ok(version) => Some(version), diff --git a/bin/reth/src/debug_cmd/execution.rs b/bin/reth/src/debug_cmd/execution.rs index 88481b1cc14..82de9e055f4 100644 --- a/bin/reth/src/debug_cmd/execution.rs +++ b/bin/reth/src/debug_cmd/execution.rs @@ -42,7 +42,7 @@ use std::{ use tokio::sync::watch; use tracing::*; -/// `reth execution-debug` command +/// `reth debug execution` command #[derive(Debug, Parser)] pub struct Command { /// The path to the data dir for all reth files and subdirectories. diff --git a/bin/reth/src/debug_cmd/in_memory_merkle.rs b/bin/reth/src/debug_cmd/in_memory_merkle.rs new file mode 100644 index 00000000000..701b2196fa5 --- /dev/null +++ b/bin/reth/src/debug_cmd/in_memory_merkle.rs @@ -0,0 +1,257 @@ +//! Command for debugging in-memory merkle trie calculation. +use crate::{ + args::{get_secret_key, utils::genesis_value_parser, DatabaseArgs, NetworkArgs}, + dirs::{DataDirPath, MaybePlatformPath}, + runner::CliContext, + utils::{get_single_body, get_single_header}, +}; +use backon::{ConstantBuilder, Retryable}; +use clap::Parser; +use reth_config::Config; +use reth_db::{init_db, DatabaseEnv}; +use reth_discv4::DEFAULT_DISCOVERY_PORT; +use reth_network::NetworkHandle; +use reth_network_api::NetworkInfo; +use reth_primitives::{fs, stage::StageId, BlockHashOrNumber, ChainSpec}; +use reth_provider::{ + AccountExtReader, BlockExecutor, BlockWriter, ExecutorFactory, HashingWriter, HeaderProvider, + LatestStateProviderRef, ProviderFactory, StageCheckpointReader, StorageReader, +}; +use reth_tasks::TaskExecutor; +use reth_trie::{hashed_cursor::HashedPostStateCursorFactory, updates::TrieKey, StateRoot}; +use std::{ + net::{Ipv4Addr, SocketAddr, SocketAddrV4}, + path::PathBuf, + sync::Arc, +}; +use tracing::*; + +/// `reth debug in-memory-merkle` command +/// This debug routine requires that the node is positioned at the block before the target. +/// The script will then download the block from p2p network and attempt to calculate and verify +/// merkle root for it. +#[derive(Debug, Parser)] +pub struct Command { + /// The path to the data dir for all reth files and subdirectories. + /// + /// Defaults to the OS-specific data directory: + /// + /// - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + /// - Windows: `{FOLDERID_RoamingAppData}/reth/` + /// - macOS: `$HOME/Library/Application Support/reth/` + #[arg(long, value_name = "DATA_DIR", verbatim_doc_comment, default_value_t)] + datadir: MaybePlatformPath, + + /// The chain this node is running. + /// + /// Possible values are either a built-in chain or the path to a chain specification file. + /// + /// Built-in chains: + /// - mainnet + /// - goerli + /// - sepolia + #[arg( + long, + value_name = "CHAIN_OR_PATH", + verbatim_doc_comment, + default_value = "mainnet", + value_parser = genesis_value_parser + )] + chain: Arc, + + #[clap(flatten)] + db: DatabaseArgs, + + #[clap(flatten)] + network: NetworkArgs, + + /// The number of retries per request + #[arg(long, default_value = "5")] + retries: usize, + + /// The depth after which we should start comparing branch nodes + #[arg(long)] + skip_node_depth: Option, +} + +impl Command { + async fn build_network( + &self, + config: &Config, + task_executor: TaskExecutor, + db: Arc, + network_secret_path: PathBuf, + default_peers_path: PathBuf, + ) -> eyre::Result { + let secret_key = get_secret_key(&network_secret_path)?; + let network = self + .network + .network_config(config, self.chain.clone(), secret_key, default_peers_path) + .with_task_executor(Box::new(task_executor)) + .listener_addr(SocketAddr::V4(SocketAddrV4::new( + Ipv4Addr::UNSPECIFIED, + self.network.port.unwrap_or(DEFAULT_DISCOVERY_PORT), + ))) + .discovery_addr(SocketAddr::V4(SocketAddrV4::new( + Ipv4Addr::UNSPECIFIED, + self.network.discovery.port.unwrap_or(DEFAULT_DISCOVERY_PORT), + ))) + .build(ProviderFactory::new(db, self.chain.clone())) + .start_network() + .await?; + info!(target: "reth::cli", peer_id = %network.peer_id(), local_addr = %network.local_addr(), "Connected to P2P network"); + debug!(target: "reth::cli", peer_id = ?network.peer_id(), "Full peer ID"); + Ok(network) + } + + /// Execute `debug in-memory-merkle` command + pub async fn execute(self, ctx: CliContext) -> eyre::Result<()> { + let config = Config::default(); + + // add network name to data dir + let data_dir = self.datadir.unwrap_or_chain_default(self.chain.chain); + let db_path = data_dir.db_path(); + fs::create_dir_all(&db_path)?; + + // initialize the database + let db = Arc::new(init_db(db_path, self.db.log_level)?); + let factory = ProviderFactory::new(&db, self.chain.clone()); + let provider = factory.provider()?; + + // Look up merkle checkpoint + let merkle_checkpoint = provider + .get_stage_checkpoint(StageId::MerkleExecute)? + .expect("merkle checkpoint exists"); + + let merkle_block_number = merkle_checkpoint.block_number; + + // Configure and build network + let network_secret_path = + self.network.p2p_secret_key.clone().unwrap_or_else(|| data_dir.p2p_secret_path()); + let network = self + .build_network( + &config, + ctx.task_executor.clone(), + db.clone(), + network_secret_path, + data_dir.known_peers_path(), + ) + .await?; + + let target_block_number = merkle_block_number + 1; + + info!(target: "reth::cli", target_block_number, "Downloading full block"); + let fetch_client = network.fetch_client().await?; + + let retries = self.retries.max(1); + let backoff = ConstantBuilder::default().with_max_times(retries); + + let client = fetch_client.clone(); + let header = (move || { + get_single_header(client.clone(), BlockHashOrNumber::Number(target_block_number)) + }) + .retry(&backoff) + .notify(|err, _| warn!(target: "reth::cli", "Error requesting header: {err}. Retrying...")) + .await?; + + let client = fetch_client.clone(); + let chain = Arc::clone(&self.chain); + let block = (move || get_single_body(client.clone(), Arc::clone(&chain), header.clone())) + .retry(&backoff) + .notify( + |err, _| warn!(target: "reth::cli", "Error requesting body: {err}. Retrying..."), + ) + .await?; + + let executor_factory = reth_revm::Factory::new(self.chain.clone()); + let mut executor = executor_factory.with_sp(LatestStateProviderRef::new(provider.tx_ref())); + + let merkle_block_td = + provider.header_td_by_number(merkle_block_number)?.unwrap_or_default(); + let block_state = executor.execute_and_verify_receipt( + &block.clone().unseal(), + merkle_block_td + block.difficulty, + None, + )?; + + // Unpacked `PostState::state_root_slow` function + let hashed_post_state = block_state.hash_state_slow().sorted(); + let (account_prefix_set, storage_prefix_set) = hashed_post_state.construct_prefix_sets(); + let tx = provider.tx_ref(); + let hashed_cursor_factory = HashedPostStateCursorFactory::new(tx, &hashed_post_state); + let (in_memory_state_root, in_memory_updates) = StateRoot::new(tx) + .with_hashed_cursor_factory(&hashed_cursor_factory) + .with_changed_account_prefixes(account_prefix_set) + .with_changed_storage_prefixes(storage_prefix_set) + .root_with_updates()?; + + if in_memory_state_root == block.state_root { + info!(target: "reth::cli", state_root = ?in_memory_state_root, "Computed in-memory state root matches"); + return Ok(()) + } + + let provider_rw = factory.provider_rw()?; + + // Insert block, state and hashes + provider_rw.insert_block(block.clone(), None)?; + block_state.write_to_db(provider_rw.tx_ref(), block.number)?; + let storage_lists = provider_rw.changed_storages_with_range(block.number..=block.number)?; + let storages = provider_rw.plainstate_storages(storage_lists)?; + provider_rw.insert_storage_for_hashing(storages)?; + let account_lists = provider_rw.changed_accounts_with_range(block.number..=block.number)?; + let accounts = provider_rw.basic_accounts(account_lists)?; + provider_rw.insert_account_for_hashing(accounts)?; + + let (state_root, incremental_trie_updates) = StateRoot::incremental_root_with_updates( + provider_rw.tx_ref(), + block.number..=block.number, + )?; + if state_root != block.state_root { + eyre::bail!( + "Computed incremental state root mismatch. Expected: {:?}. Got: {:?}", + block.state_root, + state_root + ); + } + + // Compare updates + let mut in_mem_mismatched = Vec::new(); + let mut incremental_mismatched = Vec::new(); + let mut in_mem_updates_iter = in_memory_updates.into_iter().peekable(); + let mut incremental_updates_iter = incremental_trie_updates.into_iter().peekable(); + + while in_mem_updates_iter.peek().is_some() || incremental_updates_iter.peek().is_some() { + match (in_mem_updates_iter.next(), incremental_updates_iter.next()) { + (Some(in_mem), Some(incr)) => { + pretty_assertions::assert_eq!(in_mem.0, incr.0, "Nibbles don't match"); + if in_mem.1 != incr.1 && + matches!(in_mem.0, TrieKey::AccountNode(ref nibbles) if nibbles.inner.len() > self.skip_node_depth.unwrap_or_default()) + { + in_mem_mismatched.push(in_mem); + incremental_mismatched.push(incr); + } + } + (Some(in_mem), None) => { + warn!(target: "reth::cli", next = ?in_mem, "In-memory trie updates have more entries"); + } + (None, Some(incr)) => { + tracing::warn!(target: "reth::cli", next = ?incr, "Incremental trie updates have more entries"); + } + (None, None) => { + tracing::info!(target: "reth::cli", "Exhausted all trie updates entries"); + } + } + } + + pretty_assertions::assert_eq!( + incremental_mismatched, + in_mem_mismatched, + "Mismatched trie updates" + ); + + // Drop without comitting. + drop(provider_rw); + + Ok(()) + } +} diff --git a/bin/reth/src/debug_cmd/mod.rs b/bin/reth/src/debug_cmd/mod.rs index 463c423508b..e624307f68c 100644 --- a/bin/reth/src/debug_cmd/mod.rs +++ b/bin/reth/src/debug_cmd/mod.rs @@ -4,6 +4,7 @@ use clap::{Parser, Subcommand}; use crate::runner::CliContext; mod execution; +mod in_memory_merkle; mod merkle; /// `reth debug` command @@ -20,6 +21,8 @@ pub enum Subcommands { Execution(execution::Command), /// Debug the clean & incremental state root calculations. Merkle(merkle::Command), + /// Debug in-memory state root calculation. + InMemoryMerkle(in_memory_merkle::Command), } impl Command { @@ -28,6 +31,7 @@ impl Command { match self.command { Subcommands::Execution(command) => command.execute(ctx).await, Subcommands::Merkle(command) => command.execute().await, + Subcommands::InMemoryMerkle(command) => command.execute(ctx).await, } } } diff --git a/bin/reth/src/dirs.rs b/bin/reth/src/dirs.rs index 796377b6a16..3305e8ab713 100644 --- a/bin/reth/src/dirs.rs +++ b/bin/reth/src/dirs.rs @@ -171,6 +171,11 @@ impl PlatformPath { let platform_path = PlatformPath::(path, std::marker::PhantomData); ChainPath::new(platform_path, chain) } + + /// Map the inner path to a new type `T`. + pub fn map_to(&self) -> PlatformPath { + PlatformPath(self.0.clone(), std::marker::PhantomData) + } } /// An Optional wrapper type around [PlatformPath]. diff --git a/bin/reth/src/init.rs b/bin/reth/src/init.rs index a70949dbbe9..33eb2d5c336 100644 --- a/bin/reth/src/init.rs +++ b/bin/reth/src/init.rs @@ -269,9 +269,11 @@ mod tests { ..Default::default() }, hardforks: BTreeMap::default(), - fork_timestamps: ForkTimestamps { shanghai: None }, + fork_timestamps: ForkTimestamps::default(), genesis_hash: None, paris_block_and_final_difficulty: None, + #[cfg(feature = "optimism")] + optimism: None, }); let db = create_test_rw_db(); diff --git a/bin/reth/src/node/events.rs b/bin/reth/src/node/events.rs index 4f0c673e097..cc8fe657b54 100644 --- a/bin/reth/src/node/events.rs +++ b/bin/reth/src/node/events.rs @@ -290,9 +290,10 @@ impl Eta { let elapsed = last_checkpoint_time.elapsed(); let per_second = processed_since_last as f64 / elapsed.as_secs_f64(); - self.eta = Some(Duration::from_secs_f64( - (current.total - current.processed) as f64 / per_second, - )); + self.eta = Duration::try_from_secs_f64( + ((current.total - current.processed) as f64) / per_second, + ) + .ok(); } self.last_checkpoint = current; diff --git a/bin/reth/src/node/mod.rs b/bin/reth/src/node/mod.rs index 98fa3b11306..386e365aa00 100644 --- a/bin/reth/src/node/mod.rs +++ b/bin/reth/src/node/mod.rs @@ -2,9 +2,16 @@ //! //! Starts the client use crate::{ - args::{get_secret_key, DebugArgs, DevArgs, NetworkArgs, RpcServerArgs, TxPoolArgs}, - dirs::DataDirPath, + args::{ + get_secret_key, + utils::{genesis_value_parser, parse_socket_address}, + DatabaseArgs, DebugArgs, DevArgs, NetworkArgs, PayloadBuilderArgs, RpcServerArgs, + TxPoolArgs, + }, + cli::ext::RethCliExt, + dirs::{DataDirPath, MaybePlatformPath}, init::init_genesis, + node::cl_events::ConsensusLayerHealthEvents, prometheus_exporter, runner::CliContext, utils::get_single_header, @@ -32,26 +39,30 @@ use reth_interfaces::{ p2p::{ bodies::{client::BodiesClient, downloader::BodyDownloader}, either::EitherDownloader, - headers::downloader::HeaderDownloader, + headers::{client::HeadersClient, downloader::HeaderDownloader}, }, }; use reth_network::{error::NetworkError, NetworkConfig, NetworkHandle, NetworkManager}; use reth_network_api::NetworkInfo; +use reth_payload_builder::PayloadBuilderService; use reth_primitives::{ - stage::StageId, BlockHashOrNumber, BlockNumber, ChainSpec, Head, SealedHeader, H256, + stage::StageId, BlockHashOrNumber, BlockNumber, ChainSpec, DisplayHardforks, Head, + SealedHeader, H256, }; use reth_provider::{ - BlockHashReader, BlockReader, CanonStateSubscriptions, HeaderProvider, ProviderFactory, - StageCheckpointReader, + providers::BlockchainProvider, BlockHashReader, BlockReader, CanonStateSubscriptions, + HeaderProvider, ProviderFactory, StageCheckpointReader, }; +use reth_prune::BatchSizes; use reth_revm::Factory; use reth_revm_inspectors::stack::Hook; use reth_rpc_engine_api::EngineApi; use reth_stages::{ prelude::*, stages::{ - ExecutionStage, ExecutionStageThresholds, HeaderSyncMode, SenderRecoveryStage, - TotalDifficultyStage, + AccountHashingStage, ExecutionStage, ExecutionStageThresholds, HeaderSyncMode, + IndexAccountHistoryStage, IndexStorageHistoryStage, MerkleStage, SenderRecoveryStage, + StorageHashingStage, TotalDifficultyStage, TransactionLookupStage, }, MetricEventsSender, MetricsListener, }; @@ -66,30 +77,12 @@ use std::{ use tokio::sync::{mpsc::unbounded_channel, oneshot, watch}; use tracing::*; -use crate::{ - args::{ - utils::{genesis_value_parser, parse_socket_address}, - DatabaseArgs, PayloadBuilderArgs, - }, - dirs::MaybePlatformPath, - node::cl_events::ConsensusLayerHealthEvents, -}; -use reth_interfaces::p2p::headers::client::HeadersClient; -use reth_payload_builder::PayloadBuilderService; -use reth_primitives::DisplayHardforks; -use reth_provider::providers::BlockchainProvider; -use reth_prune::BatchSizes; -use reth_stages::stages::{ - AccountHashingStage, IndexAccountHistoryStage, IndexStorageHistoryStage, MerkleStage, - StorageHashingStage, TransactionLookupStage, -}; - pub mod cl_events; pub mod events; /// Start the node #[derive(Debug, Parser)] -pub struct Command { +pub struct Command { /// The path to the data dir for all reth files and subdirectories. /// /// Defaults to the OS-specific data directory: @@ -134,7 +127,7 @@ pub struct Command { network: NetworkArgs, #[clap(flatten)] - rpc: RpcServerArgs, + rpc: RpcServerArgs, #[clap(flatten)] txpool: TxPoolArgs, @@ -150,6 +143,10 @@ pub struct Command { #[clap(flatten)] dev: DevArgs, + + #[cfg(feature = "optimism")] + #[clap(flatten)] + rollup: crate::args::RollupArgs, } impl Command { @@ -760,12 +757,17 @@ impl Command { .set(AccountHashingStage::new( stage_config.account_hashing.clean_threshold, stage_config.account_hashing.commit_threshold, + config.prune.map(|prune| prune.parts).unwrap_or_default(), )) .set(StorageHashingStage::new( stage_config.storage_hashing.clean_threshold, stage_config.storage_hashing.commit_threshold, + config.prune.map(|prune| prune.parts).unwrap_or_default(), + )) + .set(MerkleStage::new_execution( + stage_config.merkle.clean_threshold, + config.prune.map(|prune| prune.parts).unwrap_or_default(), )) - .set(MerkleStage::new_execution(stage_config.merkle.clean_threshold)) .set(TransactionLookupStage::new(stage_config.transaction_lookup.commit_threshold)) .set(IndexAccountHistoryStage::new( stage_config.index_account_history.commit_threshold, @@ -815,60 +817,61 @@ async fn run_network_until_shutdown( #[cfg(test)] mod tests { - use reth_primitives::DEV; - use super::*; + use reth_primitives::DEV; use std::{net::IpAddr, path::Path}; #[test] fn parse_help_node_command() { - let err = Command::try_parse_from(["reth", "--help"]).unwrap_err(); + let err = Command::<()>::try_parse_from(["reth", "--help"]).unwrap_err(); assert_eq!(err.kind(), clap::error::ErrorKind::DisplayHelp); } #[test] fn parse_common_node_command_chain_args() { for chain in ["mainnet", "sepolia", "goerli"] { - let args: Command = Command::parse_from(["reth", "--chain", chain]); + let args: Command = Command::<()>::parse_from(["reth", "--chain", chain]); assert_eq!(args.chain.chain, chain.parse().unwrap()); } } #[test] fn parse_discovery_port() { - let cmd = Command::try_parse_from(["reth", "--discovery.port", "300"]).unwrap(); + let cmd = Command::<()>::try_parse_from(["reth", "--discovery.port", "300"]).unwrap(); assert_eq!(cmd.network.discovery.port, Some(300)); } #[test] fn parse_port() { let cmd = - Command::try_parse_from(["reth", "--discovery.port", "300", "--port", "99"]).unwrap(); + Command::<()>::try_parse_from(["reth", "--discovery.port", "300", "--port", "99"]) + .unwrap(); assert_eq!(cmd.network.discovery.port, Some(300)); assert_eq!(cmd.network.port, Some(99)); } #[test] fn parse_metrics_port() { - let cmd = Command::try_parse_from(["reth", "--metrics", "9001"]).unwrap(); + let cmd = Command::<()>::try_parse_from(["reth", "--metrics", "9001"]).unwrap(); assert_eq!(cmd.metrics, Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 9001))); - let cmd = Command::try_parse_from(["reth", "--metrics", ":9001"]).unwrap(); + let cmd = Command::<()>::try_parse_from(["reth", "--metrics", ":9001"]).unwrap(); assert_eq!(cmd.metrics, Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 9001))); - let cmd = Command::try_parse_from(["reth", "--metrics", "localhost:9001"]).unwrap(); + let cmd = Command::<()>::try_parse_from(["reth", "--metrics", "localhost:9001"]).unwrap(); assert_eq!(cmd.metrics, Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 9001))); } #[test] fn parse_config_path() { - let cmd = Command::try_parse_from(["reth", "--config", "my/path/to/reth.toml"]).unwrap(); + let cmd = + Command::<()>::try_parse_from(["reth", "--config", "my/path/to/reth.toml"]).unwrap(); // always store reth.toml in the data dir, not the chain specific data dir let data_dir = cmd.datadir.unwrap_or_chain_default(cmd.chain.chain); let config_path = cmd.config.unwrap_or(data_dir.config_path()); assert_eq!(config_path, Path::new("my/path/to/reth.toml")); - let cmd = Command::try_parse_from(["reth"]).unwrap(); + let cmd = Command::<()>::try_parse_from(["reth"]).unwrap(); // always store reth.toml in the data dir, not the chain specific data dir let data_dir = cmd.datadir.unwrap_or_chain_default(cmd.chain.chain); @@ -878,12 +881,12 @@ mod tests { #[test] fn parse_db_path() { - let cmd = Command::try_parse_from(["reth"]).unwrap(); + let cmd = Command::<()>::try_parse_from(["reth"]).unwrap(); let data_dir = cmd.datadir.unwrap_or_chain_default(cmd.chain.chain); let db_path = data_dir.db_path(); assert!(db_path.ends_with("reth/mainnet/db"), "{:?}", cmd.config); - let cmd = Command::try_parse_from(["reth", "--datadir", "my/custom/path"]).unwrap(); + let cmd = Command::<()>::try_parse_from(["reth", "--datadir", "my/custom/path"]).unwrap(); let data_dir = cmd.datadir.unwrap_or_chain_default(cmd.chain.chain); let db_path = data_dir.db_path(); assert_eq!(db_path, Path::new("my/custom/path/db")); @@ -891,7 +894,7 @@ mod tests { #[test] fn parse_dev() { - let cmd = Command::parse_from(["reth", "--dev"]); + let cmd = Command::<()>::parse_from(["reth", "--dev"]); let chain = DEV.clone(); assert_eq!(cmd.chain.chain, chain.chain); assert_eq!(cmd.chain.genesis_hash, chain.genesis_hash); diff --git a/bin/reth/src/stage/dump/merkle.rs b/bin/reth/src/stage/dump/merkle.rs index 69b39234bd1..b5416876878 100644 --- a/bin/reth/src/stage/dump/merkle.rs +++ b/bin/reth/src/stage/dump/merkle.rs @@ -86,14 +86,22 @@ async fn unwind_and_copy( // Bring hashes to TO - AccountHashingStage { clean_threshold: u64::MAX, commit_threshold: u64::MAX } - .execute(&provider, execute_input) - .await - .unwrap(); - StorageHashingStage { clean_threshold: u64::MAX, commit_threshold: u64::MAX } - .execute(&provider, execute_input) - .await - .unwrap(); + AccountHashingStage { + clean_threshold: u64::MAX, + commit_threshold: u64::MAX, + prune_modes: PruneModes::none(), + } + .execute(&provider, execute_input) + .await + .unwrap(); + StorageHashingStage { + clean_threshold: u64::MAX, + commit_threshold: u64::MAX, + prune_modes: PruneModes::none(), + } + .execute(&provider, execute_input) + .await + .unwrap(); let unwind_inner_tx = provider.into_tx(); @@ -124,6 +132,7 @@ async fn dry_run( clean_threshold: u64::MAX, /* Forces updating the root instead of calculating * from * scratch */ + prune_modes: Default::default(), } .execute( &provider, diff --git a/bin/reth/src/stage/run.rs b/bin/reth/src/stage/run.rs index f53412c3dbb..9953bfc8a0e 100644 --- a/bin/reth/src/stage/run.rs +++ b/bin/reth/src/stage/run.rs @@ -208,12 +208,22 @@ impl Command { ) } StageEnum::TxLookup => (Box::new(TransactionLookupStage::new(batch_size)), None), - StageEnum::AccountHashing => { - (Box::new(AccountHashingStage::new(1, batch_size)), None) - } - StageEnum::StorageHashing => { - (Box::new(StorageHashingStage::new(1, batch_size)), None) - } + StageEnum::AccountHashing => ( + Box::new(AccountHashingStage::new( + 1, + batch_size, + config.prune.map(|prune| prune.parts).unwrap_or_default(), + )), + None, + ), + StageEnum::StorageHashing => ( + Box::new(StorageHashingStage::new( + 1, + batch_size, + config.prune.map(|prune| prune.parts).unwrap_or_default(), + )), + None, + ), StageEnum::Merkle => ( Box::new(MerkleStage::default_execution()), Some(Box::new(MerkleStage::default_unwind())), diff --git a/bin/reth/src/utils.rs b/bin/reth/src/utils.rs index 1ac4f4d427f..32cd073ce07 100644 --- a/bin/reth/src/utils.rs +++ b/bin/reth/src/utils.rs @@ -1,6 +1,7 @@ //! Common CLI utility functions. use eyre::Result; +use reth_consensus_common::validation::validate_block_standalone; use reth_db::{ cursor::DbCursorRO, database::Database, @@ -8,10 +9,13 @@ use reth_db::{ transaction::{DbTx, DbTxMut}, }; use reth_interfaces::p2p::{ + bodies::client::BodiesClient, headers::client::{HeadersClient, HeadersRequest}, priority::Priority, }; -use reth_primitives::{fs, BlockHashOrNumber, ChainSpec, HeadersDirection, SealedHeader}; +use reth_primitives::{ + fs, BlockHashOrNumber, ChainSpec, HeadersDirection, SealedBlock, SealedHeader, +}; use std::{ env::VarError, path::{Path, PathBuf}, @@ -56,6 +60,35 @@ where Ok(header) } +/// Get a body from network based on header +pub async fn get_single_body( + client: Client, + chain_spec: Arc, + header: SealedHeader, +) -> Result +where + Client: BodiesClient, +{ + let (peer_id, response) = client.get_block_body(header.hash).await?.split(); + + if response.is_none() { + client.report_bad_message(peer_id); + eyre::bail!("Invalid number of bodies received. Expected: 1. Received: 0") + } + + let block = response.unwrap(); + let block = SealedBlock { + header, + body: block.transactions, + ommers: block.ommers, + withdrawals: block.withdrawals, + }; + + validate_block_standalone(&block, &chain_spec)?; + + Ok(block) +} + /// Wrapper over DB that implements many useful DB queries. pub struct DbTool<'a, DB: Database> { pub(crate) db: &'a DB, diff --git a/book/cli/db.md b/book/cli/db.md index 95627871c42..5772fe612cf 100644 --- a/book/cli/db.md +++ b/book/cli/db.md @@ -16,6 +16,8 @@ Commands: Gets the content of a table for the given key drop Deletes all database entries + clear + Deletes all table entries version Lists current and local database versions path @@ -95,14 +97,85 @@ Display: Silence all log output ``` -## `reth db drop` +## `reth db stats` -Deletes all database entries +Lists all the tables, their entry count and their size ```bash -$ reth db drop --help +$ reth db stats --help -Usage: reth db drop [OPTIONS] +Usage: reth db stats [OPTIONS] + +Options: + --datadir + The path to the data dir for all reth files and subdirectories. + + Defaults to the OS-specific data directory: + + - Linux: `$XDG_DATA_HOME/reth/` or `$HOME/.local/share/reth/` + - Windows: `{FOLDERID_RoamingAppData}/reth/` + - macOS: `$HOME/Library/Application Support/reth/` + + [default: default] + + --chain + The chain this node is running. + + Possible values are either a built-in chain or the path to a chain specification file. + + Built-in chains: + - mainnet + - goerli + - sepolia + + [default: mainnet] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.persistent + The flag to enable persistent logs + + --log.directory + The path to put log files in + + [default: /reth/logs] + + --log.journald + Log events to journald + + --log.filter + The filter to use for logs written to the log file + + [default: error] + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` + +## `reth db list` + +Lists the contents of a table + +```bash +$ reth db list --help + +Usage: reth db list [OPTIONS] + +Arguments: +
+ The table name Options: --datadir @@ -116,6 +189,11 @@ Options: [default: default] + -s, --skip + Skip first N entries + + [default: 0] + --chain The chain this node is running. @@ -128,6 +206,17 @@ Options: [default: mainnet] + -r, --reverse + Reverse the order of the entries. If enabled last table entries are read + + -l, --len + How many items to take from the walker + + [default: 5] + + -j, --json + Dump as JSON instead of using TUI + -h, --help Print help (see a summary with '-h') @@ -238,18 +327,14 @@ Display: Silence all log output ``` -## `reth db list` +## `reth db drop` -Lists the contents of a table +Deletes all database entries ```bash -$ reth db list --help - -Usage: reth db list [OPTIONS]
+$ reth db drop --help -Arguments: -
- The table name +Usage: reth db drop [OPTIONS] Options: --datadir @@ -263,11 +348,6 @@ Options: [default: default] - -s, --skip - Skip first N entries - - [default: 0] - --chain The chain this node is running. @@ -280,17 +360,6 @@ Options: [default: mainnet] - -r, --reverse - Reverse the order of the entries. If enabled last table entries are read - - -l, --len - How many items to take from the walker - - [default: 5] - - -j, --json - Dump as JSON instead of using TUI - -h, --help Print help (see a summary with '-h') @@ -325,14 +394,18 @@ Display: Silence all log output ``` -## `reth db path` +## `reth db clear` -Returns the full database path +Deletes all table entries ```bash -$ reth db path --help +$ reth db clear --help -Usage: reth db path [OPTIONS] +Usage: reth db clear [OPTIONS]
+ +Arguments: +
+ Table name Options: --datadir @@ -392,14 +465,14 @@ Display: Silence all log output ``` -## `reth db stats` +## `reth db version` -Lists all the tables, their entry count and their size +Lists current and local database versions ```bash -$ reth db stats --help +$ reth db version --help -Usage: reth db stats [OPTIONS] +Usage: reth db version [OPTIONS] Options: --datadir @@ -459,14 +532,14 @@ Display: Silence all log output ``` -## `reth db version` +## `reth db path` -Lists current and local database versions +Returns the full database path ```bash -$ reth db version --help +$ reth db path --help -Usage: reth db version [OPTIONS] +Usage: reth db path [OPTIONS] Options: --datadir diff --git a/crates/blockchain-tree/src/blockchain_tree.rs b/crates/blockchain-tree/src/blockchain_tree.rs index ce92f48b283..8edee773bad 100644 --- a/crates/blockchain-tree/src/blockchain_tree.rs +++ b/crates/blockchain-tree/src/blockchain_tree.rs @@ -1001,9 +1001,12 @@ impl BlockchainTree old: Arc::new(old_canon_chain.clone()), new: Arc::new(new_canon_chain.clone()), }; + let reorg_depth = old_canon_chain.len(); + // insert old canon chain self.insert_chain(AppendableChain::new(old_canon_chain)); - self.metrics.reorgs.increment(1); + + self.update_reorg_metrics(reorg_depth as f64); } else { // error here to confirm that we are reverting nothing from db. error!(target: "blockchain_tree", "Reverting nothing from db on block: #{:?}", block_hash); @@ -1094,6 +1097,11 @@ impl BlockchainTree } } + fn update_reorg_metrics(&mut self, reorg_depth: f64) { + self.metrics.reorgs.increment(1); + self.metrics.latest_reorg_depth.set(reorg_depth); + } + /// Update blockchain tree chains (canonical and sidechains) and sync metrics. /// /// NOTE: this method should not be called during the pipeline sync, because otherwise the sync diff --git a/crates/blockchain-tree/src/metrics.rs b/crates/blockchain-tree/src/metrics.rs index fd48307d4be..b49ad3c5b92 100644 --- a/crates/blockchain-tree/src/metrics.rs +++ b/crates/blockchain-tree/src/metrics.rs @@ -13,6 +13,8 @@ pub struct TreeMetrics { pub canonical_chain_height: Gauge, /// The number of reorgs pub reorgs: Counter, + /// The latest reorg depth + pub latest_reorg_depth: Gauge, /// Longest sidechain height pub longest_sidechain_height: Gauge, } diff --git a/crates/consensus/auto-seal/src/lib.rs b/crates/consensus/auto-seal/src/lib.rs index 668a2117f6b..29cb6f49177 100644 --- a/crates/consensus/auto-seal/src/lib.rs +++ b/crates/consensus/auto-seal/src/lib.rs @@ -269,6 +269,8 @@ impl StorageInner { nonce: 0, base_fee_per_gas, extra_data: Default::default(), + blob_gas_used: None, + excess_blob_gas: None, }; header.transactions_root = if transactions.is_empty() { diff --git a/crates/consensus/beacon/Cargo.toml b/crates/consensus/beacon/Cargo.toml index 6b23c39642b..76737b20e48 100644 --- a/crates/consensus/beacon/Cargo.toml +++ b/crates/consensus/beacon/Cargo.toml @@ -44,3 +44,12 @@ reth-revm = { path = "../../revm" } reth-downloaders = { path = "../../net/downloaders" } assert_matches = "1.5" + +[features] +optimism = [ + "reth-consensus-common/optimism", + "reth-primitives/optimism", + "reth-interfaces/optimism", + "reth-provider/optimism", + "reth-rpc-types/optimism", +] diff --git a/crates/consensus/beacon/src/engine/message.rs b/crates/consensus/beacon/src/engine/message.rs index 2e5e542aab1..76808558595 100644 --- a/crates/consensus/beacon/src/engine/message.rs +++ b/crates/consensus/beacon/src/engine/message.rs @@ -145,6 +145,7 @@ impl Future for PendingPayloadId { /// A message for the beacon engine from other components of the node (engine RPC API invoked by the /// consensus layer). #[derive(Debug)] +#[allow(clippy::large_enum_variant)] pub enum BeaconEngineMessage { /// Message with new payload. NewPayload { diff --git a/crates/consensus/common/Cargo.toml b/crates/consensus/common/Cargo.toml index 8611aa2317f..72d057744cc 100644 --- a/crates/consensus/common/Cargo.toml +++ b/crates/consensus/common/Cargo.toml @@ -18,3 +18,6 @@ reth-interfaces = { workspace = true, features = ["test-utils"] } reth-provider = { workspace = true, features = ["test-utils"] } assert_matches = "1.5.0" mockall = "0.11.3" + +[features] +optimism = ["reth-primitives/optimism"] diff --git a/crates/consensus/common/src/validation.rs b/crates/consensus/common/src/validation.rs index 485d6146737..3aea32b5b45 100644 --- a/crates/consensus/common/src/validation.rs +++ b/crates/consensus/common/src/validation.rs @@ -1,12 +1,20 @@ //! Collection of methods for block validation. use reth_interfaces::{consensus::ConsensusError, Result as RethResult}; use reth_primitives::{ - constants, BlockNumber, ChainSpec, Hardfork, Header, InvalidTransactionError, SealedBlock, - SealedHeader, Transaction, TransactionSignedEcRecovered, TxEip1559, TxEip2930, TxLegacy, + blobfee::calculate_excess_blob_gas, + constants::{ + self, + eip4844::{DATA_GAS_PER_BLOB, MAX_DATA_GAS_PER_BLOCK}, + }, + BlockNumber, ChainSpec, Hardfork, Header, InvalidTransactionError, SealedBlock, SealedHeader, + Transaction, TransactionSignedEcRecovered, TxEip1559, TxEip2930, TxLegacy, }; use reth_provider::{AccountReader, HeaderProvider, WithdrawalsProvider}; use std::collections::{hash_map::Entry, HashMap}; +#[cfg(feature = "optimism")] +use reth_primitives::TxDeposit; + /// Validate header standalone pub fn validate_header_standalone( header: &SealedHeader, @@ -38,6 +46,15 @@ pub fn validate_header_standalone( return Err(ConsensusError::WithdrawalsRootUnexpected) } + // Ensures that EIP-4844 fields are valid once cancun is active. + if chain_spec.fork(Hardfork::Cancun).active_at_timestamp(header.timestamp) { + validate_4844_header_standalone(header)?; + } else if header.blob_gas_used.is_some() { + return Err(ConsensusError::BlobGasUsedUnexpected) + } else if header.excess_blob_gas.is_some() { + return Err(ConsensusError::ExcessBlobGasUnexpected) + } + Ok(()) } @@ -86,6 +103,8 @@ pub fn validate_transaction_regarding_header( Some(*chain_id) } + #[cfg(feature = "optimism")] + Transaction::Deposit(TxDeposit { .. }) => None, }; if let Some(chain_id) = chain_id { if chain_id != chain_spec.chain().id() { @@ -252,27 +271,32 @@ pub fn validate_header_regarding_parent( // TODO Check difficulty increment between parent and child // Ace age did increment it by some formula that we need to follow. - let mut parent_gas_limit = parent.gas_limit; + // Check gas limit, max diff between child/parent gas_limit should be max_diff=parent_gas/1024 + // On Optimism, the gas limit can adjust instantly, so we skip this check if the optimism + // feature flag is enabled. + #[cfg(not(feature = "optimism"))] + { + let mut parent_gas_limit = parent.gas_limit; - // By consensus, gas_limit is multiplied by elasticity (*2) on - // on exact block that hardfork happens. - if chain_spec.fork(Hardfork::London).transitions_at_block(child.number) { - parent_gas_limit = parent.gas_limit * constants::EIP1559_ELASTICITY_MULTIPLIER; - } + // By consensus, gas_limit is multiplied by elasticity (*2) on + // on exact block that hardfork happens. + if chain_spec.fork(Hardfork::London).transitions_at_block(child.number) { + parent_gas_limit = parent.gas_limit * constants::EIP1559_ELASTICITY_MULTIPLIER; + } - // Check gas limit, max diff between child/parent gas_limit should be max_diff=parent_gas/1024 - if child.gas_limit > parent_gas_limit { - if child.gas_limit - parent_gas_limit >= parent_gas_limit / 1024 { - return Err(ConsensusError::GasLimitInvalidIncrease { + if child.gas_limit > parent_gas_limit { + if child.gas_limit - parent_gas_limit >= parent_gas_limit / 1024 { + return Err(ConsensusError::GasLimitInvalidIncrease { + parent_gas_limit, + child_gas_limit: child.gas_limit, + }) + } + } else if parent_gas_limit - child.gas_limit >= parent_gas_limit / 1024 { + return Err(ConsensusError::GasLimitInvalidDecrease { parent_gas_limit, child_gas_limit: child.gas_limit, }) } - } else if parent_gas_limit - child.gas_limit >= parent_gas_limit / 1024 { - return Err(ConsensusError::GasLimitInvalidDecrease { - parent_gas_limit, - child_gas_limit: child.gas_limit, - }) } // EIP-1559 check base fee @@ -291,6 +315,11 @@ pub fn validate_header_regarding_parent( } } + // ensure that the blob gas fields for this block + if chain_spec.fork(Hardfork::Cancun).active_at_timestamp(child.timestamp) { + validate_4844_header_with_parent(parent, child)?; + } + Ok(()) } @@ -376,6 +405,72 @@ pub fn full_validation Result<(), ConsensusError> { + // From [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844#header-extension): + // + // > For the first post-fork block, both parent.blob_gas_used and parent.excess_blob_gas + // > are evaluated as 0. + // + // This means in the first post-fork block, calculate_excess_blob_gas will return 0. + let parent_blob_gas_used = parent.blob_gas_used.unwrap_or(0); + let parent_excess_blob_gas = parent.excess_blob_gas.unwrap_or(0); + + if child.blob_gas_used.is_none() { + return Err(ConsensusError::BlobGasUsedMissing) + } + let excess_blob_gas = child.excess_blob_gas.ok_or(ConsensusError::ExcessBlobGasMissing)?; + + let expected_excess_blob_gas = + calculate_excess_blob_gas(parent_excess_blob_gas, parent_blob_gas_used); + if expected_excess_blob_gas != excess_blob_gas { + return Err(ConsensusError::ExcessBlobGasDiff { + expected: expected_excess_blob_gas, + got: excess_blob_gas, + parent_excess_blob_gas, + parent_blob_gas_used, + }) + } + + Ok(()) +} + +/// Validates that the EIP-4844 header fields exist and conform to the spec. This ensures that: +/// +/// * `blob_gas_used` exists as a header field +/// * `excess_blob_gas` exists as a header field +/// * `blob_gas_used` is less than or equal to `MAX_DATA_GAS_PER_BLOCK` +/// * `blob_gas_used` is a multiple of `DATA_GAS_PER_BLOB` +pub fn validate_4844_header_standalone(header: &SealedHeader) -> Result<(), ConsensusError> { + let blob_gas_used = header.blob_gas_used.ok_or(ConsensusError::BlobGasUsedMissing)?; + + if header.excess_blob_gas.is_none() { + return Err(ConsensusError::ExcessBlobGasMissing) + } + + if blob_gas_used > MAX_DATA_GAS_PER_BLOCK { + return Err(ConsensusError::BlobGasUsedExceedsMaxBlobGasPerBlock { + blob_gas_used, + max_blob_gas_per_block: MAX_DATA_GAS_PER_BLOCK, + }) + } + + if blob_gas_used % DATA_GAS_PER_BLOB != 0 { + return Err(ConsensusError::BlobGasUsedNotMultipleOfBlobGasPerBlob { + blob_gas_used, + blob_gas_per_blob: DATA_GAS_PER_BLOB, + }) + } + + Ok(()) +} + #[cfg(test)] mod tests { use super::*; @@ -530,6 +625,8 @@ mod tests { nonce: 0x0000000000000000, base_fee_per_gas: 0x28f0001df.into(), withdrawals_root: None, + blob_gas_used: None, + excess_blob_gas: None, }; // size: 0x9b5 diff --git a/crates/interfaces/Cargo.toml b/crates/interfaces/Cargo.toml index c0525a77841..57ab3a7e9e2 100644 --- a/crates/interfaces/Cargo.toml +++ b/crates/interfaces/Cargo.toml @@ -54,4 +54,5 @@ secp256k1 = { workspace = true, features = [ [features] test-utils = ["tokio-stream/sync", "secp256k1", "rand/std_rng"] -cli = ["clap"] \ No newline at end of file +cli = ["clap"] +optimism = ["reth-eth-wire/optimism"] diff --git a/crates/interfaces/src/blockchain_tree/error.rs b/crates/interfaces/src/blockchain_tree/error.rs index edcdb569bae..c2e463a7328 100644 --- a/crates/interfaces/src/blockchain_tree/error.rs +++ b/crates/interfaces/src/blockchain_tree/error.rs @@ -194,6 +194,9 @@ impl InsertBlockErrorKind { BlockExecutionError::BlockHashNotFoundInChain { .. } | BlockExecutionError::AppendChainDoesntConnect { .. } | BlockExecutionError::UnavailableForTest => false, + #[cfg(feature = "optimism")] + BlockExecutionError::L1BlockInfoError { .. } | + BlockExecutionError::InsufficientFundsForL1Cost { .. } => false, } } InsertBlockErrorKind::Tree(err) => { diff --git a/crates/interfaces/src/consensus.rs b/crates/interfaces/src/consensus.rs index 16f4d6c07ad..76c2175c9af 100644 --- a/crates/interfaces/src/consensus.rs +++ b/crates/interfaces/src/consensus.rs @@ -113,6 +113,27 @@ pub enum ConsensusError { WithdrawalIndexInvalid { got: u64, expected: u64 }, #[error("Missing withdrawals")] BodyWithdrawalsMissing, + #[error("Missing blob gas used")] + BlobGasUsedMissing, + #[error("Unexpected blob gas used")] + BlobGasUsedUnexpected, + #[error("Missing excess blob gas")] + ExcessBlobGasMissing, + #[error("Unexpected excess blob gas")] + ExcessBlobGasUnexpected, + #[error("Blob gas used {blob_gas_used} exceeds maximum allowance {max_blob_gas_per_block}")] + BlobGasUsedExceedsMaxBlobGasPerBlock { blob_gas_used: u64, max_blob_gas_per_block: u64 }, + #[error( + "Blob gas used {blob_gas_used} is not a multiple of blob gas per blob {blob_gas_per_blob}" + )] + BlobGasUsedNotMultipleOfBlobGasPerBlob { blob_gas_used: u64, blob_gas_per_blob: u64 }, + #[error("Invalid excess blob gas. Expected: {expected}, got: {got}. Parent excess blob gas: {parent_excess_blob_gas}, parent blob gas used: {parent_blob_gas_used}.")] + ExcessBlobGasDiff { + expected: u64, + got: u64, + parent_excess_blob_gas: u64, + parent_blob_gas_used: u64, + }, /// Error for a transaction that violates consensus. #[error(transparent)] InvalidTransaction(#[from] InvalidTransactionError), diff --git a/crates/interfaces/src/executor.rs b/crates/interfaces/src/executor.rs index cc747e29fdb..7f315688a35 100644 --- a/crates/interfaces/src/executor.rs +++ b/crates/interfaces/src/executor.rs @@ -42,7 +42,6 @@ pub enum BlockExecutionError { CanonicalRevert { inner: String }, #[error("Transaction error on commit: {inner:?}")] CanonicalCommit { inner: String }, - // === tree errors === // TODO(mattsse): move this to tree error #[error("Block hash {block_hash} not found in blockchain tree chain")] @@ -57,6 +56,13 @@ pub enum BlockExecutionError { /// Note: this is not feature gated for convenience. #[error("Execution unavailable for tests")] UnavailableForTest, + + #[cfg(feature = "optimism")] + #[error("Could not get L1 block info from L2 block: {message:?}")] + L1BlockInfoError { message: String }, + #[cfg(feature = "optimism")] + #[error("Insufficient funds to cover transaction L1 cost: want {want}, have {have}")] + InsufficientFundsForL1Cost { want: u64, have: u64 }, } impl BlockExecutionError { diff --git a/crates/interfaces/src/test_utils/generators.rs b/crates/interfaces/src/test_utils/generators.rs index fbbdf42a212..d9399f8f814 100644 --- a/crates/interfaces/src/test_utils/generators.rs +++ b/crates/interfaces/src/test_utils/generators.rs @@ -349,6 +349,8 @@ pub fn random_receipt( } else { vec![] }, + #[cfg(feature = "optimism")] + deposit_nonce: None, } } diff --git a/crates/net/downloaders/Cargo.toml b/crates/net/downloaders/Cargo.toml index b5ca3d6b5b0..281dbfc6c78 100644 --- a/crates/net/downloaders/Cargo.toml +++ b/crates/net/downloaders/Cargo.toml @@ -32,7 +32,7 @@ thiserror.workspace = true # optional deps for the test-utils feature reth-rlp = { workspace = true, optional = true } tempfile = { version = "3.3", optional = true } -itertools = { version = "0.10", optional = true } +itertools = { workspace = true, optional = true } [dev-dependencies] reth-db = { path = "../../storage/db", features = ["test-utils"] } @@ -42,7 +42,7 @@ reth-tracing = { path = "../../tracing" } assert_matches = "1.5.0" tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } reth-rlp.workspace = true -itertools = "0.10" +itertools.workspace = true tempfile = "3.3" diff --git a/crates/net/eth-wire/Cargo.toml b/crates/net/eth-wire/Cargo.toml index 19a78317c8a..f9477ca6074 100644 --- a/crates/net/eth-wire/Cargo.toml +++ b/crates/net/eth-wire/Cargo.toml @@ -64,6 +64,7 @@ proptest-derive = "0.3" default = ["serde"] serde = ["dep:serde", "smol_str/serde"] arbitrary = ["reth-primitives/arbitrary", "dep:arbitrary", "dep:proptest", "dep:proptest-derive"] +optimism = [] [[test]] name = "fuzz_roundtrip" diff --git a/crates/net/eth-wire/src/types/blocks.rs b/crates/net/eth-wire/src/types/blocks.rs index 47777cd71b1..808c8f4a460 100644 --- a/crates/net/eth-wire/src/types/blocks.rs +++ b/crates/net/eth-wire/src/types/blocks.rs @@ -258,6 +258,8 @@ mod test { nonce: 0x0000000000000000u64, base_fee_per_gas: None, withdrawals_root: None, + blob_gas_used: None, + excess_blob_gas: None, }, ]), }.encode(&mut data); @@ -289,6 +291,8 @@ mod test { nonce: 0x0000000000000000u64, base_fee_per_gas: None, withdrawals_root: None, + blob_gas_used: None, + excess_blob_gas: None, }, ]), }; @@ -401,6 +405,8 @@ mod test { nonce: 0x0000000000000000u64, base_fee_per_gas: None, withdrawals_root: None, + blob_gas_used: None, + excess_blob_gas: None, }, ], withdrawals: None, @@ -485,6 +491,8 @@ mod test { nonce: 0x0000000000000000u64, base_fee_per_gas: None, withdrawals_root: None, + blob_gas_used: None, + excess_blob_gas: None, }, ], withdrawals: None, diff --git a/crates/net/eth-wire/src/types/receipts.rs b/crates/net/eth-wire/src/types/receipts.rs index f1bb3bc1209..af40108aa99 100644 --- a/crates/net/eth-wire/src/types/receipts.rs +++ b/crates/net/eth-wire/src/types/receipts.rs @@ -49,6 +49,8 @@ mod test { success: false, cumulative_gas_used: 0, logs: vec![], + #[cfg(feature = "optimism")] + deposit_nonce: None, }, bloom: Default::default(), }]]); @@ -102,10 +104,10 @@ mod test { let mut data = vec![]; let request = RequestPair:: { request_id: 1111, - message: Receipts(vec![ - vec![ - ReceiptWithBloom { - receipt: Receipt {tx_type: TxType::Legacy, + message: Receipts(vec![vec![ + ReceiptWithBloom { + receipt: Receipt { + tx_type: TxType::Legacy, cumulative_gas_used: 0x1u64, logs: vec![ Log { @@ -118,10 +120,12 @@ mod test { }, ], success: false, - },bloom: hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into(), - } - ], - ]), + #[cfg(feature = "optimism")] + deposit_nonce: None, + }, + bloom: hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into(), + }, + ]]), }; request.encode(&mut data); assert_eq!(data, expected); @@ -153,6 +157,8 @@ mod test { }, ], success: false, + #[cfg(feature = "optimism")] + deposit_nonce: None, }, bloom: hex!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into(), }, diff --git a/crates/net/network/src/eth_requests.rs b/crates/net/network/src/eth_requests.rs index 7cb4db72b37..10b53f61701 100644 --- a/crates/net/network/src/eth_requests.rs +++ b/crates/net/network/src/eth_requests.rs @@ -8,7 +8,7 @@ use reth_eth_wire::{ }; use reth_interfaces::p2p::error::RequestResult; use reth_primitives::{BlockBody, BlockHashOrNumber, Header, HeadersDirection, PeerId}; -use reth_provider::{BlockReader, HeaderProvider}; +use reth_provider::{BlockReader, HeaderProvider, ReceiptProvider}; use std::{ borrow::Borrow, future::Future, @@ -21,6 +21,11 @@ use tokio_stream::wrappers::ReceiverStream; // Limits: +/// Maximum number of receipts to serve. +/// +/// Used to limit lookups. +const MAX_RECEIPTS_SERVE: usize = 1024; + /// Maximum number of block headers to serve. /// /// Used to limit lookups. @@ -32,6 +37,9 @@ const MAX_HEADERS_SERVE: usize = 1024; /// SOFT_RESPONSE_LIMIT. const MAX_BODIES_SERVE: usize = 1024; +/// Estimated size in bytes of an RLP encoded receipt. +const APPROX_RECEIPT_SIZE: usize = 24 * 1024; + /// Estimated size in bytes of an RLP encoded body. // TODO: check 24kb blocksize assumption const APPROX_BODY_SIZE: usize = 24 * 1024; @@ -70,7 +78,7 @@ impl EthRequestHandler { impl EthRequestHandler where - C: BlockReader + HeaderProvider, + C: BlockReader + HeaderProvider + ReceiptProvider, { /// Returns the list of requested headers fn get_headers_response(&self, request: GetBlockHeaders) -> Vec
{ @@ -185,6 +193,44 @@ where let _ = response.send(Ok(BlockBodies(bodies))); } + + fn on_receipts_request( + &mut self, + _peer_id: PeerId, + request: GetReceipts, + response: oneshot::Sender>, + ) { + let mut receipts = Vec::new(); + + let mut total_bytes = APPROX_RECEIPT_SIZE; + + for hash in request.0 { + if let Some(receipts_by_block) = + self.client.receipts_by_block(BlockHashOrNumber::Hash(hash)).unwrap_or_default() + { + receipts.push( + receipts_by_block + .into_iter() + .map(|receipt| receipt.with_bloom()) + .collect::>(), + ); + + total_bytes += APPROX_RECEIPT_SIZE; + + if total_bytes > SOFT_RESPONSE_LIMIT { + break + } + + if receipts.len() >= MAX_RECEIPTS_SERVE { + break + } + } else { + break + } + } + + let _ = response.send(Ok(Receipts(receipts))); + } } /// An endless future. @@ -211,7 +257,9 @@ where this.on_bodies_request(peer_id, request, response) } IncomingEthRequest::GetNodeData { .. } => {} - IncomingEthRequest::GetReceipts { .. } => {} + IncomingEthRequest::GetReceipts { peer_id, request, response } => { + this.on_receipts_request(peer_id, request, response) + } }, } } diff --git a/crates/payload/basic/Cargo.toml b/crates/payload/basic/Cargo.toml index 874c9842d83..fd56911d338 100644 --- a/crates/payload/basic/Cargo.toml +++ b/crates/payload/basic/Cargo.toml @@ -29,3 +29,11 @@ futures-util.workspace = true ## misc tracing.workspace = true + +[features] +optimism = [ + "reth-primitives/optimism", + "reth-revm/optimism", + "reth-transaction-pool/optimism", + "reth-provider/optimism" +] diff --git a/crates/payload/basic/src/lib.rs b/crates/payload/basic/src/lib.rs index 84da180060d..3ed0a6873ce 100644 --- a/crates/payload/basic/src/lib.rs +++ b/crates/payload/basic/src/lib.rs @@ -659,6 +659,8 @@ fn build_payload( success: result.is_success(), cumulative_gas_used, logs: result.logs().into_iter().map(into_reth_log).collect(), + #[cfg(feature = "optimism")] + deposit_nonce: None, }, ); @@ -714,6 +716,8 @@ fn build_payload( difficulty: U256::ZERO, gas_used: cumulative_gas_used, extra_data: extra_data.into(), + blob_gas_used: None, + excess_blob_gas: None, }; // seal the block @@ -785,6 +789,8 @@ where difficulty: U256::ZERO, gas_used: 0, extra_data: extra_data.into(), + blob_gas_used: None, + excess_blob_gas: None, }; let block = Block { header, body: vec![], ommers: vec![], withdrawals }; diff --git a/crates/payload/builder/Cargo.toml b/crates/payload/builder/Cargo.toml index 664fae7039a..f243daee751 100644 --- a/crates/payload/builder/Cargo.toml +++ b/crates/payload/builder/Cargo.toml @@ -32,3 +32,9 @@ tracing.workspace = true [features] test-utils = [] +optimism = [ + "reth-primitives/optimism", + "reth-rpc-types/optimism", + "reth-interfaces/optimism", + "reth-revm-primitives/optimism", +] diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index cd4913698e2..bfdfcc9c846 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -22,7 +22,7 @@ crunchy = { version = "0.2.2", default-features = false, features = ["limit_256" ruint = { version = "1.9.0", features = ["primitive-types", "rlp"] } # Bloom -fixed-hash = { version = "0.8", default-features = false, features = ["rustc-hex"] } + fixed-hash = { version = "0.8", default-features = false, features = ["rustc-hex"] } # crypto secp256k1 = { workspace = true, default-features = false, features = [ @@ -92,6 +92,7 @@ pprof = { version = "0.12", features = ["flamegraph", "frame-pointer", "criterio default = [] arbitrary = ["revm-primitives/arbitrary", "dep:arbitrary", "dep:proptest", "dep:proptest-derive"] test-utils = [] +optimism = [] [[bench]] name = "recover_ecdsa_crit" diff --git a/crates/primitives/res/genesis/goerli_op.json b/crates/primitives/res/genesis/goerli_op.json new file mode 100644 index 00000000000..0ea4638d942 --- /dev/null +++ b/crates/primitives/res/genesis/goerli_op.json @@ -0,0 +1,92 @@ +{ + "nonce": "0x00", + "timestamp": "0x00", + "extraData": "0x000000000000000000000000000000000000000000000000000000000000000027770a9694e4B4b1E130Ab91Bc327C36855f612E0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "difficulty": "0x01", + "gasLimit": "15000000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x0000000000000000000000000000000000000000", + "gasUsed": "0x00", + "number": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "stateRoot": "0x9e6b478a1cd331a979c39e4bddf42c676bcf5a63382f898dc441fe3fe5eb0837", + "alloc": { + "0x4200000000000000000000000000000000000000": { + "balance": "00", + "storage": {}, + "code": "0x608060405234801561001057600080fd5b50600436106100365760003560e01c806382e3702d1461003b578063cafa81dc14610072575b600080fd5b61005e610049366004610112565b60006020819052908152604090205460ff1681565b604051901515815260200160405180910390f35b61008561008036600461015a565b610087565b005b6001600080833360405160200161009f929190610229565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815291815281516020928301208352908201929092520160002080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001691151591909117905550565b60006020828403121561012457600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60006020828403121561016c57600080fd5b813567ffffffffffffffff8082111561018457600080fd5b818401915084601f83011261019857600080fd5b8135818111156101aa576101aa61012b565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156101f0576101f061012b565b8160405282815287602084870101111561020957600080fd5b826020860160208301376000928101602001929092525095945050505050565b6000835160005b8181101561024a5760208187018101518583015201610230565b81811115610259576000828501525b5060609390931b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016919092019081526014019291505056fea26469706673582212209ffc0b44ce8a27c46cae74a3b3b620a72f10aaea97ed37c15b5d36792abd2aa464736f6c63430008090033" + }, + "0x4200000000000000000000000000000000000002": { + "balance": "00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000000" + }, + "code": "0x608060405234801561001057600080fd5b50600436106100725760003560e01c80639b19251a116100505780639b19251a146100e9578063b1540a011461011c578063bdc7b54f1461012f57600080fd5b806308fd63221461007757806313af40351461008c5780638da5cb5b1461009f575b600080fd5b61008a610085366004610614565b610137565b005b61008a61009a366004610650565b610271565b6000546100bf9073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b61010c6100f7366004610650565b60016020526000908152604090205460ff1681565b60405190151581526020016100e0565b61010c61012a366004610650565b61047c565b61008a6104cd565b60005473ffffffffffffffffffffffffffffffffffffffff1633146101e3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f46756e6374696f6e2063616e206f6e6c792062652063616c6c6564206279207460448201527f6865206f776e6572206f66207468697320636f6e74726163742e00000000000060648201526084015b60405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff821660008181526001602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168515159081179091558251938452908301527f8daaf060c3306c38e068a75c054bf96ecd85a3db1252712c4d93632744c42e0d910160405180910390a15050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610318576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f46756e6374696f6e2063616e206f6e6c792062652063616c6c6564206279207460448201527f6865206f776e6572206f66207468697320636f6e74726163742e00000000000060648201526084016101da565b73ffffffffffffffffffffffffffffffffffffffff81166103e1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152605160248201527f4f564d5f4465706c6f79657257686974656c6973743a2063616e206f6e6c792060448201527f62652064697361626c65642076696120656e61626c654172626974726172794360648201527f6f6e74726163744465706c6f796d656e74000000000000000000000000000000608482015260a4016101da565b6000546040805173ffffffffffffffffffffffffffffffffffffffff928316815291831660208301527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c910160405180910390a1600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b6000805473ffffffffffffffffffffffffffffffffffffffff1615806104c7575073ffffffffffffffffffffffffffffffffffffffff821660009081526001602052604090205460ff165b92915050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610574576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f46756e6374696f6e2063616e206f6e6c792062652063616c6c6564206279207460448201527f6865206f776e6572206f66207468697320636f6e74726163742e00000000000060648201526084016101da565b60005460405173ffffffffffffffffffffffffffffffffffffffff90911681527fc0e106cf568e50698fdbde1eff56f5a5c966cc7958e37e276918e9e4ccdf8cd49060200160405180910390a1600080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b803573ffffffffffffffffffffffffffffffffffffffff8116811461060f57600080fd5b919050565b6000806040838503121561062757600080fd5b610630836105eb565b91506020830135801515811461064557600080fd5b809150509250929050565b60006020828403121561066257600080fd5b61066b826105eb565b939250505056fea264697066735822122045a02b3906eca00a51b37c2965ab13be381f71f60af681951849865fb2daa75f64736f6c63430008090033" + }, + "0x4200000000000000000000000000000000000007": { + "balance": "00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000004": "0x000000000000000000000000000000000000000000000000000000000000dead", + "0x0000000000000000000000000000000000000000000000000000000000000005": "0x0000000000000000000000005086d1eef304eb5284a0f6720f79403b4e9be294", + "0x0000000000000000000000000000000000000000000000000000000000000003": "0x00000000000000000000000000000000000000000000000000000000000186a0" + }, + "code": "0x608060405234801561001057600080fd5b50600436106100885760003560e01c8063a71198691161005b578063a71198691461012a578063b1b1b2091461014a578063cbd4ece91461016d578063ecc704281461018057600080fd5b806321d800ec1461008d5780633dbb202b146100c55780636e296e45146100da57806382e3702d14610107575b600080fd5b6100b061009b366004610826565b60006020819052908152604090205460ff1681565b60405190151581526020015b60405180910390f35b6100d86100d3366004610942565b610197565b005b6100e26102e2565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100bc565b6100b0610115366004610826565b60026020526000908152604090205460ff1681565b6005546100e29073ffffffffffffffffffffffffffffffffffffffff1681565b6100b0610158366004610826565b60016020526000908152604090205460ff1681565b6100d861017b3660046109ad565b61038b565b61018960035481565b6040519081526020016100bc565b60006101a784338560035461078d565b80516020808301919091206000908152600290915260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055517fcafa81dc0000000000000000000000000000000000000000000000000000000081529091507342000000000000000000000000000000000000009063cafa81dc9061023c908490600401610a89565b600060405180830381600087803b15801561025657600080fd5b505af115801561026a573d6000803e3d6000fd5b505050508373ffffffffffffffffffffffffffffffffffffffff167fcb0f7ffd78f9aee47a248fae8db181db6eee833039123e026dcbff529522e52a3385600354866040516102bc9493929190610aa3565b60405180910390a26001600360008282546102d79190610aef565b909155505050505050565b60045460009073ffffffffffffffffffffffffffffffffffffffff1661dead141561036e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f78446f6d61696e4d65737361676553656e646572206973206e6f74207365740060448201526064015b60405180910390fd5b5060045473ffffffffffffffffffffffffffffffffffffffff1690565b60055473ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffffeeeeffffffffffffffffffffffffffffffffeeef330173ffffffffffffffffffffffffffffffffffffffff161461046a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f50726f7669646564206d65737361676520636f756c64206e6f7420626520766560448201527f7269666965642e000000000000000000000000000000000000000000000000006064820152608401610365565b60006104788585858561078d565b8051602080830191909120600081815260019092526040909120549192509060ff1615610527576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f50726f7669646564206d6573736167652068617320616c72656164792062656560448201527f6e2072656365697665642e0000000000000000000000000000000000000000006064820152608401610365565b73ffffffffffffffffffffffffffffffffffffffff8616734200000000000000000000000000000000000000141561059957600090815260016020819052604090912080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016909117905550610787565b600480547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff878116919091179091556040516000918816906105f2908790610b2e565b6000604051808303816000865af19150503d806000811461062f576040519150601f19603f3d011682016040523d82523d6000602084013e610634565b606091505b5050600480547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead1790559050801515600114156106d557600082815260016020819052604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169092179091555183917f4641df4a962071e12719d8c8c8e5ac7fc4d97b927346a3d7a335b1f7517e133c91a2610701565b60405182907f99d0e048484baa1b1540b1367cb128acd7ab2946d1ed91ec10e3c85e4bf51b8f90600090a25b600083334360405160200161071893929190610b4a565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181528151602092830120600090815291829052902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055505050505b50505050565b6060848484846040516024016107a69493929190610b9c565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fcbd4ece9000000000000000000000000000000000000000000000000000000001790529050949350505050565b60006020828403121561083857600080fd5b5035919050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461086357600080fd5b919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f8301126108a857600080fd5b813567ffffffffffffffff808211156108c3576108c3610868565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190828211818310171561090957610909610868565b8160405283815286602085880101111561092257600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008060006060848603121561095757600080fd5b6109608461083f565b9250602084013567ffffffffffffffff81111561097c57600080fd5b61098886828701610897565b925050604084013563ffffffff811681146109a257600080fd5b809150509250925092565b600080600080608085870312156109c357600080fd5b6109cc8561083f565b93506109da6020860161083f565b9250604085013567ffffffffffffffff8111156109f657600080fd5b610a0287828801610897565b949793965093946060013593505050565b60005b83811015610a2e578181015183820152602001610a16565b838111156107875750506000910152565b60008151808452610a57816020860160208601610a13565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000610a9c6020830184610a3f565b9392505050565b73ffffffffffffffffffffffffffffffffffffffff85168152608060208201526000610ad26080830186610a3f565b905083604083015263ffffffff8316606083015295945050505050565b60008219821115610b29577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b500190565b60008251610b40818460208701610a13565b9190910192915050565b60008451610b5c818460208901610a13565b60609490941b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001691909301908152601481019190915260340192915050565b600073ffffffffffffffffffffffffffffffffffffffff808716835280861660208401525060806040830152610bd56080830185610a3f565b90508260608301529594505050505056fea26469706673582212200afb9537c52292543adecf37773b8e4d795c984adf4e80970731e434679b4ec264736f6c63430008090033" + }, + "0x420000000000000000000000000000000000000F": { + "balance": "00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000a693b8f8207ff043f6bbc2e2120bbe4c2251efe9", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000003": "0x0000000000000000000000000000000000000000000000000000000000000abe", + "0x0000000000000000000000000000000000000000000000000000000000000004": "0x000000000000000000000000000000000000000000000000000000000016e360", + "0x0000000000000000000000000000000000000000000000000000000000000005": "0x0000000000000000000000000000000000000000000000000000000000000006" + }, + "code": "0x608060405234801561001057600080fd5b50600436106100f55760003560e01c80638c8885c811610097578063de26c4a111610066578063de26c4a1146101cc578063f2fde38b146101df578063f45e65d8146101f2578063fe173b97146101fb57600080fd5b80638c8885c81461016b5780638da5cb5b1461017e578063bede39b5146101a6578063bf1fe420146101b957600080fd5b806349948e0e116100d357806349948e0e14610134578063519b4bd3146101475780637046559714610150578063715018a61461016357600080fd5b80630c18c162146100fa578063313ce567146101165780633577afc51461011f575b600080fd5b61010360035481565b6040519081526020015b60405180910390f35b61010360055481565b61013261012d3660046108d0565b610204565b005b610103610142366004610918565b6102c6565b61010360025481565b61013261015e3660046108d0565b610322565b6101326103d8565b6101326101793660046108d0565b610465565b60005460405173ffffffffffffffffffffffffffffffffffffffff909116815260200161010d565b6101326101b43660046108d0565b61051b565b6101326101c73660046108d0565b6105d1565b6101036101da366004610918565b610687565b6101326101ed3660046109e7565b61072b565b61010360045481565b61010360015481565b60005473ffffffffffffffffffffffffffffffffffffffff16331461028a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064015b60405180910390fd5b60038190556040518181527f32740b35c0ea213650f60d44366b4fb211c9033b50714e4a1d34e65d5beb9bb4906020015b60405180910390a150565b6000806102d283610687565b90506000600254826102e49190610a53565b90506000600554600a6102f79190610bb2565b90506000600454836103099190610a53565b905060006103178383610bbe565b979650505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff1633146103a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610281565b60048190556040518181527f3336cd9708eaf2769a0f0dc0679f30e80f15dcd88d1921b5a16858e8b85c591a906020016102bb565b60005473ffffffffffffffffffffffffffffffffffffffff163314610459576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610281565b610463600061085b565b565b60005473ffffffffffffffffffffffffffffffffffffffff1633146104e6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610281565b60058190556040518181527fd68112a8707e326d08be3656b528c1bcc5bbbfc47f4177e2179b14d8640838c1906020016102bb565b60005473ffffffffffffffffffffffffffffffffffffffff16331461059c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610281565b60028190556040518181527f351fb23757bb5ea0546c85b7996ddd7155f96b939ebaa5ff7bc49c75f27f2c44906020016102bb565b60005473ffffffffffffffffffffffffffffffffffffffff163314610652576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610281565b60018190556040518181527ffcdccc6074c6c42e4bd578aa9870c697dc976a270968452d2b8c8dc369fae396906020016102bb565b600080805b8351811015610704578381815181106106a7576106a7610bf9565b01602001517fff00000000000000000000000000000000000000000000000000000000000000166106e4576106dd600483610c28565b91506106f2565b6106ef601083610c28565b91505b806106fc81610c40565b91505061068c565b506000600354826107159190610c28565b905061072381610440610c28565b949350505050565b60005473ffffffffffffffffffffffffffffffffffffffff1633146107ac576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610281565b73ffffffffffffffffffffffffffffffffffffffff811661084f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610281565b6108588161085b565b50565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000602082840312156108e257600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60006020828403121561092a57600080fd5b813567ffffffffffffffff8082111561094257600080fd5b818401915084601f83011261095657600080fd5b813581811115610968576109686108e9565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156109ae576109ae6108e9565b816040528281528760208487010111156109c757600080fd5b826020860160208301376000928101602001929092525095945050505050565b6000602082840312156109f957600080fd5b813573ffffffffffffffffffffffffffffffffffffffff81168114610a1d57600080fd5b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615610a8b57610a8b610a24565b500290565b600181815b80851115610ae957817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115610acf57610acf610a24565b80851615610adc57918102915b93841c9390800290610a95565b509250929050565b600082610b0057506001610bac565b81610b0d57506000610bac565b8160018114610b235760028114610b2d57610b49565b6001915050610bac565b60ff841115610b3e57610b3e610a24565b50506001821b610bac565b5060208310610133831016604e8410600b8410161715610b6c575081810a610bac565b610b768383610a90565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115610ba857610ba8610a24565b0290505b92915050565b6000610a1d8383610af1565b600082610bf4577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008219821115610c3b57610c3b610a24565b500190565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415610c7257610c72610a24565b506001019056fea2646970667358221220b949ef5f9defd6c0aab6259672d00d239cb8854c9972ba1866af1c6ec6433d4c64736f6c63430008090033" + }, + "0x4200000000000000000000000000000000000010": { + "balance": "00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x000000000000000000000000636af16bf2f682dd3109e60102b8e1a089fedaa8", + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000004200000000000000000000000000000000000007" + }, + "code": "0x608060405234801561001057600080fd5b50600436106100675760003560e01c80633cb747bf116100505780633cb747bf146100ca578063662a633a146100ea578063a3a79548146100fd57600080fd5b806332b7006d1461006c57806336c717c114610081575b600080fd5b61007f61007a366004610d0f565b610110565b005b6001546100a19073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b6000546100a19073ffffffffffffffffffffffffffffffffffffffff1681565b61007f6100f8366004610d80565b610126565b61007f61010b366004610e18565b6106c1565b61011f853333878787876106d8565b5050505050565b60015473ffffffffffffffffffffffffffffffffffffffff1661015e60005473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461021d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f4f564d5f58434841494e3a206d657373656e67657220636f6e7472616374207560448201527f6e61757468656e7469636174656400000000000000000000000000000000000060648201526084015b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff1661025360005473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff16636e296e456040518163ffffffff1660e01b815260040160206040518083038186803b15801561029857600080fd5b505afa1580156102ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102d09190610e9b565b73ffffffffffffffffffffffffffffffffffffffff1614610373576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603060248201527f4f564d5f58434841494e3a2077726f6e672073656e646572206f662063726f7360448201527f732d646f6d61696e206d657373616765000000000000000000000000000000006064820152608401610214565b61039d877f1d1d8b6300000000000000000000000000000000000000000000000000000000610a32565b801561045357508673ffffffffffffffffffffffffffffffffffffffff1663c01e1bd66040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156103ec57600080fd5b505af1158015610400573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104249190610e9b565b73ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff16145b15610567576040517f40c10f1900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8681166004830152602482018690528816906340c10f1990604401600060405180830381600087803b1580156104c857600080fd5b505af11580156104dc573d6000803e3d6000fd5b505050508573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff167fb0444523268717a02698be47d0803aa7468c00acbed2f8bd93a0459cde61dd898888888860405161055a9493929190610f08565b60405180910390a46106b7565b600063a9f9e67560e01b8989888a89898960405160240161058e9796959493929190610f3e565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526001549091506106339073ffffffffffffffffffffffffffffffffffffffff16600083610a57565b8673ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff168a73ffffffffffffffffffffffffffffffffffffffff167f7ea89a4591614515571c2b51f5ea06494056f261c10ab1ed8c03c7590d87bce0898989896040516106ad9493929190610f08565b60405180910390a4505b5050505050505050565b6106d0863387878787876106d8565b505050505050565b6040517f9dc29fac0000000000000000000000000000000000000000000000000000000081523360048201526024810185905273ffffffffffffffffffffffffffffffffffffffff881690639dc29fac90604401600060405180830381600087803b15801561074657600080fd5b505af115801561075a573d6000803e3d6000fd5b5050505060008773ffffffffffffffffffffffffffffffffffffffff1663c01e1bd66040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156107a857600080fd5b505af11580156107bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107e09190610e9b565b9050606073ffffffffffffffffffffffffffffffffffffffff891673deaddeaddeaddeaddeaddeaddeaddeaddead000014156108d5576040517f1532ec340000000000000000000000000000000000000000000000000000000090610851908a908a908a9089908990602401610f9b565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091529050610994565b6040517fa9f9e67500000000000000000000000000000000000000000000000000000000906109149084908c908c908c908c908b908b90602401610f3e565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915290505b6001546109b89073ffffffffffffffffffffffffffffffffffffffff168683610a57565b3373ffffffffffffffffffffffffffffffffffffffff168973ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f73d170910aba9e6d50b102db522b1dbcd796216f5128b445aa2135272886497e8a8a89896040516106ad9493929190610f08565b6000610a3d83610ae8565b8015610a4e5750610a4e8383610b4c565b90505b92915050565b6000546040517f3dbb202b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690633dbb202b90610ab190869085908790600401611016565b600060405180830381600087803b158015610acb57600080fd5b505af1158015610adf573d6000803e3d6000fd5b50505050505050565b6000610b14827f01ffc9a700000000000000000000000000000000000000000000000000000000610b4c565b8015610a515750610b45827fffffffff00000000000000000000000000000000000000000000000000000000610b4c565b1592915050565b604080517fffffffff00000000000000000000000000000000000000000000000000000000831660248083019190915282518083039091018152604490910182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01ffc9a7000000000000000000000000000000000000000000000000000000001790529051600091908290819073ffffffffffffffffffffffffffffffffffffffff87169061753090610c06908690611092565b6000604051808303818686fa925050503d8060008114610c42576040519150601f19603f3d011682016040523d82523d6000602084013e610c47565b606091505b5091509150602081511015610c625760009350505050610a51565b818015610c7e575080806020019051810190610c7e91906110ae565b9695505050505050565b73ffffffffffffffffffffffffffffffffffffffff81168114610caa57600080fd5b50565b803563ffffffff81168114610cc157600080fd5b919050565b60008083601f840112610cd857600080fd5b50813567ffffffffffffffff811115610cf057600080fd5b602083019150836020828501011115610d0857600080fd5b9250929050565b600080600080600060808688031215610d2757600080fd5b8535610d3281610c88565b945060208601359350610d4760408701610cad565b9250606086013567ffffffffffffffff811115610d6357600080fd5b610d6f88828901610cc6565b969995985093965092949392505050565b600080600080600080600060c0888a031215610d9b57600080fd5b8735610da681610c88565b96506020880135610db681610c88565b95506040880135610dc681610c88565b94506060880135610dd681610c88565b93506080880135925060a088013567ffffffffffffffff811115610df957600080fd5b610e058a828b01610cc6565b989b979a50959850939692959293505050565b60008060008060008060a08789031215610e3157600080fd5b8635610e3c81610c88565b95506020870135610e4c81610c88565b945060408701359350610e6160608801610cad565b9250608087013567ffffffffffffffff811115610e7d57600080fd5b610e8989828a01610cc6565b979a9699509497509295939492505050565b600060208284031215610ead57600080fd5b8151610eb881610c88565b9392505050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b73ffffffffffffffffffffffffffffffffffffffff85168152836020820152606060408201526000610c7e606083018486610ebf565b600073ffffffffffffffffffffffffffffffffffffffff808a1683528089166020840152808816604084015280871660608401525084608083015260c060a0830152610f8e60c083018486610ebf565b9998505050505050505050565b600073ffffffffffffffffffffffffffffffffffffffff808816835280871660208401525084604083015260806060830152610fdb608083018486610ebf565b979650505050505050565b60005b83811015611001578181015183820152602001610fe9565b83811115611010576000848401525b50505050565b73ffffffffffffffffffffffffffffffffffffffff841681526060602082015260008351806060840152611051816080850160208801610fe6565b63ffffffff93909316604083015250601f919091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160160800192915050565b600082516110a4818460208701610fe6565b9190910192915050565b6000602082840312156110c057600080fd5b81518015158114610eb857600080fdfea2646970667358221220df892c82e9ba0fc965240aa38614ff1bd6af4d227ce2c0e9933d7f50711a886264736f6c63430008090033" + }, + "0x4200000000000000000000000000000000000011": { + "balance": "00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000fd1d2e729ae8eee2e146c033bf4400fe75284301" + }, + "code": "0x6080604052600436106100385760003560e01c80633ccfd60b14610044578063d3e5792b1461005b578063d4ff92181461008a57600080fd5b3661003f57005b600080fd5b34801561005057600080fd5b506100596100dc565b005b34801561006757600080fd5b5061007767d02ab486cedc000081565b6040519081526020015b60405180910390f35b34801561009657600080fd5b506000546100b79073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610081565b67d02ab486cedc000047101561019e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152605760248201527f4f564d5f53657175656e6365724665655661756c743a2077697468647261776160448201527f6c20616d6f756e74206d7573742062652067726561746572207468616e206d6960648201527f6e696d756d207769746864726177616c20616d6f756e74000000000000000000608482015260a40160405180910390fd5b600080546040805160208101825283815290517fa3a795480000000000000000000000000000000000000000000000000000000081527342000000000000000000000000000000000000109363a3a79548936102309373deaddeaddeaddeaddeaddeaddeaddeaddead00009373ffffffffffffffffffffffffffffffffffffffff909216924792909190600401610264565b600060405180830381600087803b15801561024a57600080fd5b505af115801561025e573d6000803e3d6000fd5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff808816835260208188168185015286604085015263ffffffff8616606085015260a06080850152845191508160a085015260005b828110156102cb5785810182015185820160c0015281016102af565b828111156102dd57600060c084870101525b5050601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160c001969550505050505056fea2646970667358221220387a6116dde263ea48767352a397053c8cffa776aecb43cded2f25a4a9cfbdc264736f6c63430008090033" + }, + "0x4200000000000000000000000000000000000012": { + "balance": "00", + "storage": {}, + "code": "0x608060405234801561001057600080fd5b506004361061002b5760003560e01c8063896f93d114610030575b600080fd5b61004361003e36600461025f565b610045565b005b73ffffffffffffffffffffffffffffffffffffffff83166100c6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4d7573742070726f76696465204c3120746f6b656e2061646472657373000000604482015260640160405180910390fd5b60007342000000000000000000000000000000000000108484846040516100ec90610178565b6100f99493929190610359565b604051809103906000f080158015610115573d6000803e3d6000fd5b5090508073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fceeb8e7d520d7f3b65fc11a262b91066940193b05d4f93df07cfdced0eb551cf60405160405180910390a350505050565b6113d7806103b083390190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f8301126101c557600080fd5b813567ffffffffffffffff808211156101e0576101e0610185565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190828211818310171561022657610226610185565b8160405283815286602085880101111561023f57600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008060006060848603121561027457600080fd5b833573ffffffffffffffffffffffffffffffffffffffff8116811461029857600080fd5b9250602084013567ffffffffffffffff808211156102b557600080fd5b6102c1878388016101b4565b935060408601359150808211156102d757600080fd5b506102e4868287016101b4565b9150509250925092565b6000815180845260005b81811015610314576020818501810151868301820152016102f8565b81811115610326576000602083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b600073ffffffffffffffffffffffffffffffffffffffff80871683528086166020840152506080604083015261039260808301856102ee565b82810360608401526103a481856102ee565b97965050505050505056fe60806040523480156200001157600080fd5b50604051620013d7380380620013d783398101604081905262000034916200022f565b8151829082906200004d9060039060208501906200009f565b508051620000639060049060208401906200009f565b5050600580546001600160a01b039586166001600160a01b031991821617909155600680549690951695169490941790925550620002fc915050565b828054620000ad90620002bf565b90600052602060002090601f016020900481019282620000d157600085556200011c565b82601f10620000ec57805160ff19168380011785556200011c565b828001600101855582156200011c579182015b828111156200011c578251825591602001919060010190620000ff565b506200012a9291506200012e565b5090565b5b808211156200012a57600081556001016200012f565b80516001600160a01b03811681146200015d57600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126200018a57600080fd5b81516001600160401b0380821115620001a757620001a762000162565b604051601f8301601f19908116603f01168101908282118183101715620001d257620001d262000162565b81604052838152602092508683858801011115620001ef57600080fd5b600091505b83821015620002135785820183015181830184015290820190620001f4565b83821115620002255760008385830101525b9695505050505050565b600080600080608085870312156200024657600080fd5b620002518562000145565b9350620002616020860162000145565b60408601519093506001600160401b03808211156200027f57600080fd5b6200028d8883890162000178565b93506060870151915080821115620002a457600080fd5b50620002b38782880162000178565b91505092959194509250565b600181811c90821680620002d457607f821691505b60208210811415620002f657634e487b7160e01b600052602260045260246000fd5b50919050565b6110cb806200030c6000396000f3fe608060405234801561001057600080fd5b50600436106101005760003560e01c806370a0823111610097578063a9059cbb11610066578063a9059cbb14610215578063ae1f6aaf14610228578063c01e1bd61461026d578063dd62ed3e1461028d57600080fd5b806370a08231146101b157806395d89b41146101e75780639dc29fac146101ef578063a457c2d71461020257600080fd5b806323b872dd116100d357806323b872dd14610167578063313ce5671461017a578063395093511461018957806340c10f191461019c57600080fd5b806301ffc9a71461010557806306fdde031461012d578063095ea7b31461014257806318160ddd14610155575b600080fd5b610118610113366004610e4a565b6102d3565b60405190151581526020015b60405180910390f35b610135610393565b6040516101249190610e93565b610118610150366004610f2f565b610425565b6002545b604051908152602001610124565b610118610175366004610f59565b61043b565b60405160128152602001610124565b610118610197366004610f2f565b61050c565b6101af6101aa366004610f2f565b610555565b005b6101596101bf366004610f95565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205490565b61013561061a565b6101af6101fd366004610f2f565b610629565b610118610210366004610f2f565b6106e2565b610118610223366004610f2f565b6107a0565b6006546102489073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610124565b6005546102489073ffffffffffffffffffffffffffffffffffffffff1681565b61015961029b366004610fb0565b73ffffffffffffffffffffffffffffffffffffffff918216600090815260016020908152604080832093909416825291909152205490565b60007f01ffc9a7a5cef8baa21ed3c5c0d7e23accb804b619e9333b597f47a0d84076e27f1d1d8b63000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000084167f01ffc9a700000000000000000000000000000000000000000000000000000000148061038b57507fffffffff00000000000000000000000000000000000000000000000000000000848116908216145b949350505050565b6060600380546103a290610fe3565b80601f01602080910402602001604051908101604052809291908181526020018280546103ce90610fe3565b801561041b5780601f106103f05761010080835404028352916020019161041b565b820191906000526020600020905b8154815290600101906020018083116103fe57829003601f168201915b5050505050905090565b60006104323384846107ad565b50600192915050565b600061044884848461092d565b73ffffffffffffffffffffffffffffffffffffffff84166000908152600160209081526040808320338452909152902054828110156104f45760405162461bcd60e51b815260206004820152602860248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206160448201527f6c6c6f77616e636500000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b61050185338584036107ad565b506001949350505050565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff871684529091528120549091610432918590610550908690611066565b6107ad565b60065473ffffffffffffffffffffffffffffffffffffffff1633146105bc5760405162461bcd60e51b815260206004820181905260248201527f4f6e6c79204c32204272696467652063616e206d696e7420616e64206275726e60448201526064016104eb565b6105c68282610b93565b8173ffffffffffffffffffffffffffffffffffffffff167f0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d41213968858260405161060e91815260200190565b60405180910390a25050565b6060600480546103a290610fe3565b60065473ffffffffffffffffffffffffffffffffffffffff1633146106905760405162461bcd60e51b815260206004820181905260248201527f4f6e6c79204c32204272696467652063616e206d696e7420616e64206275726e60448201526064016104eb565b61069a8282610c99565b8173ffffffffffffffffffffffffffffffffffffffff167fcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca58260405161060e91815260200190565b33600090815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff86168452909152812054828110156107895760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760448201527f207a65726f00000000000000000000000000000000000000000000000000000060648201526084016104eb565b61079633858584036107ad565b5060019392505050565b600061043233848461092d565b73ffffffffffffffffffffffffffffffffffffffff83166108355760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f726573730000000000000000000000000000000000000000000000000000000060648201526084016104eb565b73ffffffffffffffffffffffffffffffffffffffff82166108be5760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f737300000000000000000000000000000000000000000000000000000000000060648201526084016104eb565b73ffffffffffffffffffffffffffffffffffffffff83811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b73ffffffffffffffffffffffffffffffffffffffff83166109b65760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f647265737300000000000000000000000000000000000000000000000000000060648201526084016104eb565b73ffffffffffffffffffffffffffffffffffffffff8216610a3f5760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f657373000000000000000000000000000000000000000000000000000000000060648201526084016104eb565b73ffffffffffffffffffffffffffffffffffffffff831660009081526020819052604090205481811015610adb5760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e6365000000000000000000000000000000000000000000000000000060648201526084016104eb565b73ffffffffffffffffffffffffffffffffffffffff808516600090815260208190526040808220858503905591851681529081208054849290610b1f908490611066565b925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610b8591815260200190565b60405180910390a350505050565b73ffffffffffffffffffffffffffffffffffffffff8216610bf65760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f20616464726573730060448201526064016104eb565b8060026000828254610c089190611066565b909155505073ffffffffffffffffffffffffffffffffffffffff821660009081526020819052604081208054839290610c42908490611066565b909155505060405181815273ffffffffffffffffffffffffffffffffffffffff8316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b73ffffffffffffffffffffffffffffffffffffffff8216610d225760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360448201527f730000000000000000000000000000000000000000000000000000000000000060648201526084016104eb565b73ffffffffffffffffffffffffffffffffffffffff821660009081526020819052604090205481811015610dbe5760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60448201527f636500000000000000000000000000000000000000000000000000000000000060648201526084016104eb565b73ffffffffffffffffffffffffffffffffffffffff83166000908152602081905260408120838303905560028054849290610dfa90849061107e565b909155505060405182815260009073ffffffffffffffffffffffffffffffffffffffff8516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602001610920565b600060208284031215610e5c57600080fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114610e8c57600080fd5b9392505050565b600060208083528351808285015260005b81811015610ec057858101830151858201604001528201610ea4565b81811115610ed2576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610f2a57600080fd5b919050565b60008060408385031215610f4257600080fd5b610f4b83610f06565b946020939093013593505050565b600080600060608486031215610f6e57600080fd5b610f7784610f06565b9250610f8560208501610f06565b9150604084013590509250925092565b600060208284031215610fa757600080fd5b610e8c82610f06565b60008060408385031215610fc357600080fd5b610fcc83610f06565b9150610fda60208401610f06565b90509250929050565b600181811c90821680610ff757607f821691505b60208210811415611031577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000821982111561107957611079611037565b500190565b60008282101561109057611090611037565b50039056fea264697066735822122069a2d33039157f3f2f180571262ca2a5d0a3a24d33bf9448f3b7c2ce9ff757f964736f6c63430008090033a2646970667358221220d2e13f28319115807ec7308d1cd88642a8542d0b838e00b8769f8a85d696f26764736f6c63430008090033" + }, + "0x4200000000000000000000000000000000000013": { + "balance": "00", + "storage": {}, + "code": "0x4B60005260206000F3" + }, + "0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000": { + "balance": "00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000006": "0x0000000000000000000000004200000000000000000000000000000000000010", + "0x0000000000000000000000000000000000000000000000000000000000000005": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000003": "0x457468657200000000000000000000000000000000000000000000000000000a", + "0x0000000000000000000000000000000000000000000000000000000000000004": "0x4554480000000000000000000000000000000000000000000000000000000006" + }, + "code": "0x608060405234801561001057600080fd5b50600436106101005760003560e01c806370a0823111610097578063a9059cbb11610066578063a9059cbb14610215578063ae1f6aaf14610228578063c01e1bd61461026d578063dd62ed3e1461028d57600080fd5b806370a08231146101b157806395d89b41146101e75780639dc29fac146101ef578063a457c2d71461020257600080fd5b806323b872dd116100d357806323b872dd14610167578063313ce5671461017a578063395093511461018957806340c10f191461019c57600080fd5b806301ffc9a71461010557806306fdde031461012d578063095ea7b31461014257806318160ddd14610155575b600080fd5b610118610113366004610c6d565b6102d3565b60405190151581526020015b60405180910390f35b610135610393565b6040516101249190610cb6565b610118610150366004610d52565b610425565b6002545b604051908152602001610124565b610118610175366004610d7c565b6104db565b60405160128152602001610124565b610118610197366004610d52565b61058c565b6101af6101aa366004610d52565b61063d565b005b6101596101bf366004610db8565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205490565b61013561071c565b6101af6101fd366004610d52565b61072b565b610118610210366004610d52565b6107fe565b610118610223366004610d52565b6108af565b6006546102489073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610124565b6005546102489073ffffffffffffffffffffffffffffffffffffffff1681565b61015961029b366004610dd3565b73ffffffffffffffffffffffffffffffffffffffff918216600090815260016020908152604080832093909416825291909152205490565b60007f01ffc9a7a5cef8baa21ed3c5c0d7e23accb804b619e9333b597f47a0d84076e27f1d1d8b63000000000000000000000000000000000000000000000000000000007fffffffff0000000000000000000000000000000000000000000000000000000084167f01ffc9a700000000000000000000000000000000000000000000000000000000148061038b57507fffffffff00000000000000000000000000000000000000000000000000000000848116908216145b949350505050565b6060600380546103a290610e06565b80601f01602080910402602001604051908101604052809291908181526020018280546103ce90610e06565b801561041b5780601f106103f05761010080835404028352916020019161041b565b820191906000526020600020905b8154815290600101906020018083116103fe57829003601f168201915b5050505050905090565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604260248201527f4f564d5f4554483a20617070726f76652069732064697361626c65642070656e60448201527f64696e67206675727468657220636f6d6d756e6974792064697363757373696f60648201527f6e2e000000000000000000000000000000000000000000000000000000000000608482015260009060a4015b60405180910390fd5b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604760248201527f4f564d5f4554483a207472616e7366657246726f6d2069732064697361626c6560448201527f642070656e64696e67206675727468657220636f6d6d756e697479206469736360648201527f757373696f6e2e00000000000000000000000000000000000000000000000000608482015260009060a4016104d2565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604c60248201527f4f564d5f4554483a20696e637265617365416c6c6f77616e636520697320646960448201527f7361626c65642070656e64696e67206675727468657220636f6d6d756e69747960648201527f2064697363757373696f6e2e0000000000000000000000000000000000000000608482015260009060a4016104d2565b60065473ffffffffffffffffffffffffffffffffffffffff1633146106be576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f6e6c79204c32204272696467652063616e206d696e7420616e64206275726e60448201526064016104d2565b6106c88282610960565b8173ffffffffffffffffffffffffffffffffffffffff167f0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d41213968858260405161071091815260200190565b60405180910390a25050565b6060600480546103a290610e06565b60065473ffffffffffffffffffffffffffffffffffffffff1633146107ac576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f6e6c79204c32204272696467652063616e206d696e7420616e64206275726e60448201526064016104d2565b6107b68282610a80565b8173ffffffffffffffffffffffffffffffffffffffff167fcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca58260405161071091815260200190565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604c60248201527f4f564d5f4554483a206465637265617365416c6c6f77616e636520697320646960448201527f7361626c65642070656e64696e67206675727468657220636f6d6d756e69747960648201527f2064697363757373696f6e2e0000000000000000000000000000000000000000608482015260009060a4016104d2565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604360248201527f4f564d5f4554483a207472616e736665722069732064697361626c656420706560448201527f6e64696e67206675727468657220636f6d6d756e69747920646973637573736960648201527f6f6e2e0000000000000000000000000000000000000000000000000000000000608482015260009060a4016104d2565b73ffffffffffffffffffffffffffffffffffffffff82166109dd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f20616464726573730060448201526064016104d2565b80600260008282546109ef9190610e89565b909155505073ffffffffffffffffffffffffffffffffffffffff821660009081526020819052604081208054839290610a29908490610e89565b909155505060405181815273ffffffffffffffffffffffffffffffffffffffff8316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b73ffffffffffffffffffffffffffffffffffffffff8216610b23576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360448201527f730000000000000000000000000000000000000000000000000000000000000060648201526084016104d2565b73ffffffffffffffffffffffffffffffffffffffff821660009081526020819052604090205481811015610bd9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60448201527f636500000000000000000000000000000000000000000000000000000000000060648201526084016104d2565b73ffffffffffffffffffffffffffffffffffffffff83166000908152602081905260408120838303905560028054849290610c15908490610ea1565b909155505060405182815260009073ffffffffffffffffffffffffffffffffffffffff8516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a3505050565b600060208284031215610c7f57600080fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114610caf57600080fd5b9392505050565b600060208083528351808285015260005b81811015610ce357858101830151858201604001528201610cc7565b81811115610cf5576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610d4d57600080fd5b919050565b60008060408385031215610d6557600080fd5b610d6e83610d29565b946020939093013593505050565b600080600060608486031215610d9157600080fd5b610d9a84610d29565b9250610da860208501610d29565b9150604084013590509250925092565b600060208284031215610dca57600080fd5b610caf82610d29565b60008060408385031215610de657600080fd5b610def83610d29565b9150610dfd60208401610d29565b90509250929050565b600181811c90821680610e1a57607f821691505b60208210811415610e54577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008219821115610e9c57610e9c610e5a565b500190565b600082821015610eb357610eb3610e5a565b50039056fea2646970667358221220b71535a5111461b42945e5d842957b3a5926f7ed07d271872f6da21952b5f8b464736f6c63430008090033" + }, + "0x4200000000000000000000000000000000000006": { + "balance": "00", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x577261707065642045746865720000000000000000000000000000000000001a", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x5745544800000000000000000000000000000000000000000000000000000008", + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000000000000000000000000000000000000000000012" + }, + "code": "0x6080604052600436106100bc5760003560e01c8063313ce56711610074578063a9059cbb1161004e578063a9059cbb146102cb578063d0e30db0146100bc578063dd62ed3e14610311576100bc565b8063313ce5671461024b57806370a082311461027657806395d89b41146102b6576100bc565b806318160ddd116100a557806318160ddd146101aa57806323b872dd146101d15780632e1a7d4d14610221576100bc565b806306fdde03146100c6578063095ea7b314610150575b6100c4610359565b005b3480156100d257600080fd5b506100db6103a8565b6040805160208082528351818301528351919283929083019185019080838360005b838110156101155781810151838201526020016100fd565b50505050905090810190601f1680156101425780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561015c57600080fd5b506101966004803603604081101561017357600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135169060200135610454565b604080519115158252519081900360200190f35b3480156101b657600080fd5b506101bf6104c7565b60408051918252519081900360200190f35b3480156101dd57600080fd5b50610196600480360360608110156101f457600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135811691602081013590911690604001356104cb565b34801561022d57600080fd5b506100c46004803603602081101561024457600080fd5b503561066b565b34801561025757600080fd5b50610260610700565b6040805160ff9092168252519081900360200190f35b34801561028257600080fd5b506101bf6004803603602081101561029957600080fd5b503573ffffffffffffffffffffffffffffffffffffffff16610709565b3480156102c257600080fd5b506100db61071b565b3480156102d757600080fd5b50610196600480360360408110156102ee57600080fd5b5073ffffffffffffffffffffffffffffffffffffffff8135169060200135610793565b34801561031d57600080fd5b506101bf6004803603604081101561033457600080fd5b5073ffffffffffffffffffffffffffffffffffffffff813581169160200135166107a7565b33600081815260036020908152604091829020805434908101909155825190815291517fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c9281900390910190a2565b6000805460408051602060026001851615610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190941693909304601f8101849004840282018401909252818152929183018282801561044c5780601f106104215761010080835404028352916020019161044c565b820191906000526020600020905b81548152906001019060200180831161042f57829003601f168201915b505050505081565b33600081815260046020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552908352818420869055815186815291519394909390927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925928290030190a350600192915050565b4790565b73ffffffffffffffffffffffffffffffffffffffff83166000908152600360205260408120548211156104fd57600080fd5b73ffffffffffffffffffffffffffffffffffffffff84163314801590610573575073ffffffffffffffffffffffffffffffffffffffff841660009081526004602090815260408083203384529091529020547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff14155b156105ed5773ffffffffffffffffffffffffffffffffffffffff841660009081526004602090815260408083203384529091529020548211156105b557600080fd5b73ffffffffffffffffffffffffffffffffffffffff841660009081526004602090815260408083203384529091529020805483900390555b73ffffffffffffffffffffffffffffffffffffffff808516600081815260036020908152604080832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a35060019392505050565b3360009081526003602052604090205481111561068757600080fd5b33600081815260036020526040808220805485900390555183156108fc0291849190818181858888f193505050501580156106c6573d6000803e3d6000fd5b5060408051828152905133917f7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65919081900360200190a250565b60025460ff1681565b60036020526000908152604090205481565b60018054604080516020600284861615610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190941693909304601f8101849004840282018401909252818152929183018282801561044c5780601f106104215761010080835404028352916020019161044c565b60006107a03384846104cb565b9392505050565b60046020908152600092835260408084209091529082529020548156fea265627a7a7231582091c18790e0cca5011d2518024840ee00fecc67e11f56fd746f2cf84d5b583e0064736f6c63430005110032" + } + } +} diff --git a/crates/primitives/src/blobfee.rs b/crates/primitives/src/blobfee.rs new file mode 100644 index 00000000000..e82b5d2f8c6 --- /dev/null +++ b/crates/primitives/src/blobfee.rs @@ -0,0 +1,12 @@ +//! Helpers for working with EIP-4844 blob fee + +use crate::constants::eip4844::TARGET_DATA_GAS_PER_BLOCK; + +/// Calculates the excess data gas for the next block, after applying the current set of blobs on +/// top of the excess data gas. +/// +/// Specified in [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844#header-extension) +pub fn calculate_excess_blob_gas(parent_excess_blob_gas: u64, parent_blob_gas_used: u64) -> u64 { + let excess_blob_gas = parent_excess_blob_gas + parent_blob_gas_used; + excess_blob_gas.saturating_sub(TARGET_DATA_GAS_PER_BLOCK) +} diff --git a/crates/primitives/src/chain/mod.rs b/crates/primitives/src/chain/mod.rs index b46519f3256..c8368a50227 100644 --- a/crates/primitives/src/chain/mod.rs +++ b/crates/primitives/src/chain/mod.rs @@ -14,6 +14,9 @@ pub use spec::{ ForkTimestamps, DEV, GOERLI, MAINNET, SEPOLIA, }; +#[cfg(feature = "optimism")] +pub use spec::OP_GOERLI; + // The chain info module. mod info; pub use info::ChainInfo; @@ -44,6 +47,18 @@ impl Chain { Chain::Named(ethers_core::types::Chain::Sepolia) } + /// Returns the optimism goerli chain. + #[cfg(feature = "optimism")] + pub const fn optimism_goerli() -> Self { + Chain::Named(ethers_core::types::Chain::OptimismGoerli) + } + + /// Returns the optimism mainnet chain. + #[cfg(feature = "optimism")] + pub const fn optimism_mainnet() -> Self { + Chain::Named(ethers_core::types::Chain::Optimism) + } + /// Returns the dev chain. pub const fn dev() -> Self { Chain::Named(ethers_core::types::Chain::Dev) diff --git a/crates/primitives/src/chain/spec.rs b/crates/primitives/src/chain/spec.rs index 356c9409b7e..552c1017df2 100644 --- a/crates/primitives/src/chain/spec.rs +++ b/crates/primitives/src/chain/spec.rs @@ -54,6 +54,8 @@ pub static MAINNET: Lazy> = Lazy::new(|| { ), (Hardfork::Shanghai, ForkCondition::Timestamp(1681338455)), ]), + #[cfg(feature = "optimism")] + optimism: None, } .into() }); @@ -88,6 +90,8 @@ pub static GOERLI: Lazy> = Lazy::new(|| { ), (Hardfork::Shanghai, ForkCondition::Timestamp(1678832736)), ]), + #[cfg(feature = "optimism")] + optimism: None, } .into() }); @@ -126,6 +130,8 @@ pub static SEPOLIA: Lazy> = Lazy::new(|| { ), (Hardfork::Shanghai, ForkCondition::Timestamp(1677557088)), ]), + #[cfg(feature = "optimism")] + optimism: None, } .into() }); @@ -163,6 +169,39 @@ pub static DEV: Lazy> = Lazy::new(|| { ), (Hardfork::Shanghai, ForkCondition::Timestamp(0)), ]), + #[cfg(feature = "optimism")] + optimism: None, + } + .into() +}); + +/// The Optimism Goerli spec +#[cfg(feature = "optimism")] +pub static OP_GOERLI: Lazy> = Lazy::new(|| { + ChainSpec { + chain: Chain::optimism_goerli(), + genesis: serde_json::from_str(include_str!("../../res/genesis/goerli_op.json")) + .expect("Can't deserialize Optimism Goerli genesis json"), + genesis_hash: Some(H256(hex!( + "c1fc15cd51159b1f1e5cbc4b82e85c1447ddfa33c52cf1d98d14fba0d6354be1" + ))), + fork_timestamps: ForkTimestamps::default(), // TODO(clabby): update this + paris_block_and_final_difficulty: Some((0, U256::from(0))), + hardforks: BTreeMap::from([ + (Hardfork::Byzantium, ForkCondition::Block(0)), + (Hardfork::Constantinople, ForkCondition::Block(0)), + (Hardfork::Petersburg, ForkCondition::Block(0)), + (Hardfork::Istanbul, ForkCondition::Block(0)), + (Hardfork::MuirGlacier, ForkCondition::Block(0)), + (Hardfork::Berlin, ForkCondition::Block(0)), + (Hardfork::London, ForkCondition::Block(0)), + ( + Hardfork::Paris, + ForkCondition::TTD { fork_block: Some(0), total_difficulty: U256::from(0) }, + ), + (Hardfork::Regolith, ForkCondition::Timestamp(1679079600)), + ]), + optimism: Some(OptimismConfig { eip_1559_elasticity: 10, eip_1559_denominator: 50 }), } .into() }); @@ -201,6 +240,20 @@ pub struct ChainSpec { /// The active hard forks and their activation conditions pub hardforks: BTreeMap, + + /// Optimism configuration + #[cfg(feature = "optimism")] + pub optimism: Option, +} + +/// Optimism configuration. +#[cfg(feature = "optimism")] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct OptimismConfig { + /// Elasticity multiplier as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) + pub eip_1559_elasticity: u64, + /// Base fee max change denominator as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) + pub eip_1559_denominator: u64, } impl ChainSpec { @@ -306,6 +359,15 @@ impl ChainSpec { .unwrap_or_else(|| self.is_fork_active_at_timestamp(Hardfork::Shanghai, timestamp)) } + /// Convenience method to check if [Hardfork::Cancun] is active at a given timestamp. + #[inline] + pub fn is_cancun_activated_at_timestamp(&self, timestamp: u64) -> bool { + self.fork_timestamps + .cancun + .map(|cancun| timestamp >= cancun) + .unwrap_or_else(|| self.is_fork_active_at_timestamp(Hardfork::Cancun, timestamp)) + } + /// Creates a [`ForkFilter`](crate::ForkFilter) for the block described by [Head]. pub fn fork_filter(&self, head: Head) -> ForkFilter { let forks = self.forks_iter().filter_map(|(_, condition)| { @@ -424,6 +486,8 @@ impl From for ChainSpec { fork_timestamps: ForkTimestamps::from_hardforks(&hardforks), hardforks, paris_block_and_final_difficulty: None, + #[cfg(feature = "optimism")] + optimism: None, } } } @@ -433,6 +497,8 @@ impl From for ChainSpec { pub struct ForkTimestamps { /// The timestamp of the shanghai fork pub shanghai: Option, + /// The timestamp of the cancun fork + pub cancun: Option, } impl ForkTimestamps { @@ -442,6 +508,9 @@ impl ForkTimestamps { if let Some(shanghai) = forks.get(&Hardfork::Shanghai).and_then(|f| f.as_timestamp()) { timestamps = timestamps.shanghai(shanghai); } + if let Some(cancun) = forks.get(&Hardfork::Cancun).and_then(|f| f.as_timestamp()) { + timestamps = timestamps.cancun(cancun); + } timestamps } @@ -450,6 +519,12 @@ impl ForkTimestamps { self.shanghai = Some(shanghai); self } + + /// Sets the given cancun timestamp + pub fn cancun(mut self, cancun: u64) -> Self { + self.cancun = Some(cancun); + self + } } /// A helper type for compatibility with geth's config @@ -495,6 +570,8 @@ pub struct ChainSpecBuilder { chain: Option, genesis: Option, hardforks: BTreeMap, + #[cfg(feature = "optimism")] + optimism: Option, } impl ChainSpecBuilder { @@ -504,6 +581,8 @@ impl ChainSpecBuilder { chain: Some(MAINNET.chain), genesis: Some(MAINNET.genesis.clone()), hardforks: MAINNET.hardforks.clone(), + #[cfg(feature = "optimism")] + optimism: None, } } @@ -614,6 +693,29 @@ impl ChainSpecBuilder { self } + /// Enable Cancun at genesis. + pub fn cancun_activated(mut self) -> Self { + self = self.paris_activated(); + self.hardforks.insert(Hardfork::Cancun, ForkCondition::Timestamp(0)); + self + } + + /// Enable Bedrock at genesis + #[cfg(feature = "optimism")] + pub fn bedrock_activated(mut self) -> Self { + self = self.london_activated(); + self.hardforks.insert(Hardfork::Bedrock, ForkCondition::Block(0)); + self + } + + /// Enable Regolith at genesis + #[cfg(feature = "optimism")] + pub fn regolith_activated(mut self) -> Self { + self = self.bedrock_activated(); + self.hardforks.insert(Hardfork::Regolith, ForkCondition::Block(0)); + self + } + /// Build the resulting [`ChainSpec`]. /// /// # Panics @@ -628,6 +730,8 @@ impl ChainSpecBuilder { fork_timestamps: ForkTimestamps::from_hardforks(&self.hardforks), hardforks: self.hardforks, paris_block_and_final_difficulty: None, + #[cfg(feature = "optimism")] + optimism: self.optimism, } } } @@ -638,6 +742,8 @@ impl From<&Arc> for ChainSpecBuilder { chain: Some(value.chain), genesis: Some(value.genesis.clone()), hardforks: value.hardforks.clone(), + #[cfg(feature = "optimism")] + optimism: value.optimism.clone(), } } } @@ -930,6 +1036,10 @@ mod tests { use bytes::BytesMut; use ethers_core::types as EtherType; use reth_rlp::Encodable; + + #[cfg(feature = "optimism")] + use crate::OP_GOERLI; + fn test_fork_ids(spec: &ChainSpec, cases: &[(Head, ForkId)]) { for (block, expected_id) in cases { let computed_id = spec.fork_id(block); @@ -1236,6 +1346,28 @@ Post-merge hard forks (timestamp based): ) } + #[cfg(feature = "optimism")] + #[test] + fn optimism_goerli_forkids() { + test_fork_ids( + &OP_GOERLI, + &[ + ( + Head { number: 0, ..Default::default() }, + ForkId { hash: ForkHash([0x6d, 0x63, 0x76, 0xbe]), next: 1679079600 }, + ), + ( + Head { number: 4061224, timestamp: 1679079599, ..Default::default() }, + ForkId { hash: ForkHash([0x6d, 0x63, 0x76, 0xbe]), next: 1679079600 }, + ), + ( + Head { number: 4061224, timestamp: 1679079600, ..Default::default() }, + ForkId { hash: ForkHash([0x8e, 0x32, 0xcc, 0x21]), next: 0 }, + ), + ], + ); + } + /// Checks that time-based forks work /// /// This is based off of the test vectors here: https://github.com/ethereum/go-ethereum/blob/5c8cc10d1e05c23ff1108022f4150749e73c0ca1/core/forkid/forkid_test.go#L155-L188 diff --git a/crates/primitives/src/constants/mod.rs b/crates/primitives/src/constants/mod.rs index 96c55cbbe11..6a5c5fefbeb 100644 --- a/crates/primitives/src/constants/mod.rs +++ b/crates/primitives/src/constants/mod.rs @@ -51,11 +51,21 @@ pub const MIN_PROTOCOL_BASE_FEE_U256: U256 = U256::from_limbs([7u64, 0, 0, 0]); pub const EIP1559_INITIAL_BASE_FEE: u64 = 1_000_000_000; /// Base fee max change denominator as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) +#[cfg(not(feature = "optimism"))] pub const EIP1559_BASE_FEE_MAX_CHANGE_DENOMINATOR: u64 = 8; /// Elasticity multiplier as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) +#[cfg(not(feature = "optimism"))] pub const EIP1559_ELASTICITY_MULTIPLIER: u64 = 2; +/// Base fee max change denominator for Optimism. +#[cfg(feature = "optimism")] +pub const EIP1559_BASE_FEE_MAX_CHANGE_DENOMINATOR: u64 = 50; + +/// Elasticity multiplier for Optimism. +#[cfg(feature = "optimism")] +pub const EIP1559_ELASTICITY_MULTIPLIER: u64 = 10; + /// Multiplier for converting gwei to wei. pub const GWEI_TO_WEI: u64 = 1_000_000_000; @@ -84,6 +94,11 @@ pub const SEPOLIA_GENESIS: H256 = pub const DEV_GENESIS: H256 = H256(hex!("2f980576711e3617a5e4d83dd539548ec0f7792007d505a3d2e9674833af2d7c")); +/// Optimism goerli genesis hash. +#[cfg(feature = "optimism")] +pub const GOERLI_OP_GENESIS: H256 = + H256(hex!("c1fc15cd51159b1f1e5cbc4b82e85c1447ddfa33c52cf1d98d14fba0d6354be1")); + /// Keccak256 over empty array. pub const KECCAK_EMPTY: H256 = H256(hex!("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")); diff --git a/crates/primitives/src/hardfork.rs b/crates/primitives/src/hardfork.rs index f26ffbcc403..b0f5dfde04a 100644 --- a/crates/primitives/src/hardfork.rs +++ b/crates/primitives/src/hardfork.rs @@ -39,6 +39,14 @@ pub enum Hardfork { Paris, /// Shanghai. Shanghai, + /// Cancun. + Cancun, + /// Bedrock. + #[cfg(feature = "optimism")] + Bedrock, + /// Regolith + #[cfg(feature = "optimism")] + Regolith, } impl Hardfork { @@ -82,6 +90,11 @@ impl FromStr for Hardfork { "grayglacier" => Hardfork::GrayGlacier, "paris" => Hardfork::Paris, "shanghai" => Hardfork::Shanghai, + "cancun" => Hardfork::Cancun, + #[cfg(feature = "optimism")] + "bedrock" => Hardfork::Bedrock, + #[cfg(feature = "optimism")] + "regolith" => Hardfork::Regolith, _ => return Err(format!("Unknown hardfork: {s}")), }; Ok(hardfork) @@ -97,7 +110,6 @@ impl Display for Hardfork { #[cfg(test)] mod tests { use super::*; - use crate::{Chain, Genesis}; use std::collections::BTreeMap; @@ -120,6 +132,7 @@ mod tests { "grayglacier", "PARIS", "ShAnGhAI", + "CaNcUn", ]; let expected_hardforks = [ Hardfork::Frontier, @@ -138,6 +151,7 @@ mod tests { Hardfork::GrayGlacier, Hardfork::Paris, Hardfork::Shanghai, + Hardfork::Cancun, ]; let hardforks: Vec = @@ -146,6 +160,18 @@ mod tests { assert_eq!(hardforks, expected_hardforks); } + #[test] + #[cfg(feature = "optimism")] + fn check_op_hardfork_from_str() { + let hardfork_str = ["beDrOck", "rEgOlITH"]; + let expected_hardforks = [Hardfork::Bedrock, Hardfork::Regolith]; + + let hardforks: Vec = + hardfork_str.iter().map(|h| Hardfork::from_str(h).unwrap()).collect(); + + assert_eq!(hardforks, expected_hardforks); + } + #[test] fn check_nonexistent_hardfork_from_str() { assert!(Hardfork::from_str("not a hardfork").is_err()); @@ -160,6 +186,8 @@ mod tests { hardforks: BTreeMap::from([(Hardfork::Frontier, ForkCondition::Never)]), fork_timestamps: Default::default(), paris_block_and_final_difficulty: None, + #[cfg(feature = "optimism")] + optimism: None, }; assert_eq!(Hardfork::Frontier.fork_id(&spec), None); @@ -174,6 +202,8 @@ mod tests { hardforks: BTreeMap::from([(Hardfork::Shanghai, ForkCondition::Never)]), fork_timestamps: Default::default(), paris_block_and_final_difficulty: None, + #[cfg(feature = "optimism")] + optimism: None, }; assert_eq!(Hardfork::Shanghai.fork_filter(&spec), None); diff --git a/crates/primitives/src/header.rs b/crates/primitives/src/header.rs index ba8e8f31deb..e39c6a8287a 100644 --- a/crates/primitives/src/header.rs +++ b/crates/primitives/src/header.rs @@ -1,5 +1,6 @@ use crate::{ basefee::calculate_next_block_base_fee, + blobfee::calculate_excess_blob_gas, keccak256, proofs::{EMPTY_LIST_HASH, EMPTY_ROOT}, BlockBodyRoots, BlockHash, BlockNumHash, BlockNumber, Bloom, Bytes, H160, H256, H64, U256, @@ -7,7 +8,7 @@ use crate::{ use bytes::{Buf, BufMut, BytesMut}; use reth_codecs::{add_arbitrary_tests, derive_arbitrary, main_codec, Compact}; -use reth_rlp::{length_of_length, Decodable, Encodable, EMPTY_STRING_CODE}; +use reth_rlp::{length_of_length, Decodable, Encodable, EMPTY_LIST_CODE, EMPTY_STRING_CODE}; use serde::{Deserialize, Serialize}; use std::{ mem, @@ -91,6 +92,13 @@ pub struct Header { /// above the gas target, and decreasing when blocks are below the gas target. The base fee per /// gas is burned. pub base_fee_per_gas: Option, + /// The total amount of blob gas consumed by the transactions within the block, added in + /// EIP-4844. + pub blob_gas_used: Option, + /// A running total of blob gas consumed in excess of the target, prior to the block. Blocks + /// with above-target blob gas consumption increase this value, blocks with below-target blob + /// gas consumption decrease it (bounded at 0). This was added in EIP-4844. + pub excess_blob_gas: Option, /// An arbitrary byte array containing data relevant to this block. This must be 32 bytes or /// fewer; formally Hx. pub extra_data: Bytes, @@ -116,6 +124,8 @@ impl Default for Header { nonce: 0, base_fee_per_gas: None, withdrawals_root: None, + blob_gas_used: None, + excess_blob_gas: None, } } } @@ -170,6 +180,13 @@ impl Header { Some(calculate_next_block_base_fee(self.gas_used, self.gas_limit, self.base_fee_per_gas?)) } + /// Calculate excess blob gas for the next block according to the EIP-4844 spec. + /// + /// Returns a `None` if no excess blob gas is set, no EIP-4844 support + pub fn next_block_excess_blob_gas(&self) -> Option { + Some(calculate_excess_blob_gas(self.excess_blob_gas?, self.blob_gas_used?)) + } + /// Seal the header with a known hash. /// /// WARNING: This method does not perform validation whether the hash is correct. @@ -202,6 +219,8 @@ impl Header { mem::size_of::() + // mix hash mem::size_of::() + // nonce mem::size_of::>() + // base fee per gas + mem::size_of::>() + // blob gas used + mem::size_of::>() + // excess blob gas self.extra_data.len() // extra data } @@ -225,11 +244,34 @@ impl Header { if let Some(base_fee) = self.base_fee_per_gas { length += U256::from(base_fee).length(); - } else if self.withdrawals_root.is_some() { - length += 1; // EMTY STRING CODE + } else if self.withdrawals_root.is_some() || + self.blob_gas_used.is_some() || + self.excess_blob_gas.is_some() + { + length += 1; // EMPTY STRING CODE } + if let Some(root) = self.withdrawals_root { length += root.length(); + } else if self.blob_gas_used.is_some() || self.excess_blob_gas.is_some() { + length += 1; // EMPTY STRING CODE + } + + if let Some(blob_gas_used) = self.blob_gas_used { + length += U256::from(blob_gas_used).length(); + } else if self.excess_blob_gas.is_some() { + length += 1; // EMPTY STRING CODE + } + + // Encode excess blob gas length. If new fields are added, the above pattern will need to + // be repeated and placeholder length added. Otherwise, it's impossible to tell _which_ + // fields are missing. This is mainly relevant for contrived cases where a header is + // created at random, for example: + // * A header is created with a withdrawals root, but no base fee. Shanghai blocks are + // post-London, so this is technically not valid. However, a tool like proptest would + // generate a block like this. + if let Some(excess_blob_gas) = self.excess_blob_gas { + length += U256::from(excess_blob_gas).length(); } length @@ -261,12 +303,38 @@ impl Encodable for Header { // but withdrawals root is present. if let Some(ref base_fee) = self.base_fee_per_gas { U256::from(*base_fee).encode(out); - } else if self.withdrawals_root.is_some() { + } else if self.withdrawals_root.is_some() || + self.blob_gas_used.is_some() || + self.excess_blob_gas.is_some() + { out.put_u8(EMPTY_STRING_CODE); } + // Encode withdrawals root. Put empty string if withdrawals root is missing, + // but blob gas used is present. if let Some(ref root) = self.withdrawals_root { root.encode(out); + } else if self.blob_gas_used.is_some() || self.excess_blob_gas.is_some() { + out.put_u8(EMPTY_STRING_CODE); + } + + // Encode blob gas used. Put empty string if blob gas used is missing, + // but excess blob gas is present. + if let Some(ref blob_gas_used) = self.blob_gas_used { + U256::from(*blob_gas_used).encode(out); + } else if self.excess_blob_gas.is_some() { + out.put_u8(EMPTY_LIST_CODE); + } + + // Encode excess blob gas. If new fields are added, the above pattern will need to be + // repeated and placeholders added. Otherwise, it's impossible to tell _which_ fields + // are missing. This is mainly relevant for contrived cases where a header is created + // at random, for example: + // * A header is created with a withdrawals root, but no base fee. Shanghai blocks are + // post-London, so this is technically not valid. However, a tool like proptest would + // generate a block like this. + if let Some(ref excess_blob_gas) = self.excess_blob_gas { + U256::from(*excess_blob_gas).encode(out); } } @@ -303,7 +371,10 @@ impl Decodable for Header { nonce: H64::decode(buf)?.to_low_u64_be(), base_fee_per_gas: None, withdrawals_root: None, + blob_gas_used: None, + excess_blob_gas: None, }; + if started_len - buf.len() < rlp_head.payload_length { if buf.first().map(|b| *b == EMPTY_STRING_CODE).unwrap_or_default() { buf.advance(1) @@ -311,9 +382,36 @@ impl Decodable for Header { this.base_fee_per_gas = Some(U256::decode(buf)?.to::()); } } + + // Withdrawals root for post-shanghai headers + if started_len - buf.len() < rlp_head.payload_length { + if buf.first().map(|b| *b == EMPTY_STRING_CODE).unwrap_or_default() { + buf.advance(1) + } else { + this.withdrawals_root = Some(Decodable::decode(buf)?); + } + } + + // Blob gas used and excess blob gas for post-cancun headers + if started_len - buf.len() < rlp_head.payload_length { + if buf.first().map(|b| *b == EMPTY_LIST_CODE).unwrap_or_default() { + buf.advance(1) + } else { + this.blob_gas_used = Some(U256::decode(buf)?.to::()); + } + } + + // Decode excess blob gas. If new fields are added, the above pattern will need to be + // repeated and placeholders decoded. Otherwise, it's impossible to tell _which_ fields are + // missing. This is mainly relevant for contrived cases where a header is created at + // random, for example: + // * A header is created with a withdrawals root, but no base fee. Shanghai blocks are + // post-London, so this is technically not valid. However, a tool like proptest would + // generate a block like this. if started_len - buf.len() < rlp_head.payload_length { - this.withdrawals_root = Some(Decodable::decode(buf)?); + this.excess_blob_gas = Some(U256::decode(buf)?.to::()); } + let consumed = started_len - buf.len(); if consumed != rlp_head.payload_length { return Err(reth_rlp::DecodeError::ListLengthMismatch { @@ -536,6 +634,8 @@ mod ethers_compat { gas_used: block.gas_used.as_u64(), withdrawals_root: None, logs_bloom: block.logs_bloom.unwrap_or_default().0.into(), + blob_gas_used: None, + excess_blob_gas: None, } } } @@ -605,6 +705,8 @@ mod tests { nonce: 0, base_fee_per_gas: Some(0x036b_u64), withdrawals_root: None, + blob_gas_used: None, + excess_blob_gas: None, }; assert_eq!(header.hash_slow(), expected_hash); } @@ -683,6 +785,120 @@ mod tests { assert_eq!(header.hash_slow(), expected_hash); } + // Test vector from: https://github.com/ethereum/tests/blob/7e9e0940c0fcdbead8af3078ede70f969109bd85/BlockchainTests/ValidBlocks/bcExample/cancunExample.json + #[test] + fn test_decode_block_header_with_blob_fields_ef_tests() { + let data = hex::decode("f90221a03a9b485972e7353edd9152712492f0c58d89ef80623686b6bf947a4a6dce6cb6a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa03c837fc158e3e93eafcaf2e658a02f5d8f99abc9f1c4c66cdea96c0ca26406aea04409cc4b699384ba5f8248d92b784713610c5ff9c1de51e9239da0dac76de9cea046cab26abf1047b5b119ecc2dda1296b071766c8b1307e1381fcecc90d513d86b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008001887fffffffffffffff8302a86582079e42a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b42188000000000000000009a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218302000080").unwrap(); + let expected = Header { + parent_hash: H256::from_str( + "3a9b485972e7353edd9152712492f0c58d89ef80623686b6bf947a4a6dce6cb6", + ) + .unwrap(), + ommers_hash: H256::from_str( + "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + ) + .unwrap(), + beneficiary: Address::from_str("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba").unwrap(), + state_root: H256::from_str( + "3c837fc158e3e93eafcaf2e658a02f5d8f99abc9f1c4c66cdea96c0ca26406ae", + ) + .unwrap(), + transactions_root: H256::from_str( + "4409cc4b699384ba5f8248d92b784713610c5ff9c1de51e9239da0dac76de9ce", + ) + .unwrap(), + receipts_root: H256::from_str( + "46cab26abf1047b5b119ecc2dda1296b071766c8b1307e1381fcecc90d513d86", + ) + .unwrap(), + logs_bloom: Default::default(), + difficulty: U256::from(0), + number: 0x1, + gas_limit: 0x7fffffffffffffff, + gas_used: 0x02a865, + timestamp: 0x079e, + extra_data: Bytes::from(vec![0x42]), + mix_hash: H256::from_str( + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + ) + .unwrap(), + nonce: 0, + base_fee_per_gas: Some(9), + withdrawals_root: Some( + H256::from_str("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") + .unwrap(), + ), + blob_gas_used: Some(0x020000), + excess_blob_gas: Some(0), + }; + + let header = Header::decode(&mut data.as_slice()).unwrap(); + assert_eq!(header, expected); + + let expected_hash = + H256::from_str("0x10aca3ebb4cf6ddd9e945a5db19385f9c105ede7374380c50d56384c3d233785") + .unwrap(); + assert_eq!(header.hash_slow(), expected_hash); + } + + #[test] + fn test_decode_block_header_with_blob_fields() { + // Block from devnet-7 + let data = hex::decode("f90239a013a7ec98912f917b3e804654e37c9866092043c13eb8eab94eb64818e886cff5a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794f97e180c050e5ab072211ad2c213eb5aee4df134a0ec229dbe85b0d3643ad0f471e6ec1a36bbc87deffbbd970762d22a53b35d068aa056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080830305988401c9c380808464c40d5499d883010c01846765746888676f312e32302e35856c696e7578a070ccadc40b16e2094954b1064749cc6fbac783c1712f1b271a8aac3eda2f232588000000000000000007a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421808401600000").unwrap(); + let expected = Header { + parent_hash: H256::from_str( + "13a7ec98912f917b3e804654e37c9866092043c13eb8eab94eb64818e886cff5", + ) + .unwrap(), + ommers_hash: H256::from_str( + "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + ) + .unwrap(), + beneficiary: Address::from_str("f97e180c050e5ab072211ad2c213eb5aee4df134").unwrap(), + state_root: H256::from_str( + "ec229dbe85b0d3643ad0f471e6ec1a36bbc87deffbbd970762d22a53b35d068a", + ) + .unwrap(), + transactions_root: H256::from_str( + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + ) + .unwrap(), + receipts_root: H256::from_str( + "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + ) + .unwrap(), + logs_bloom: Default::default(), + difficulty: U256::from(0), + number: 0x30598, + gas_limit: 0x1c9c380, + gas_used: 0, + timestamp: 0x64c40d54, + extra_data: Bytes::from( + hex::decode("d883010c01846765746888676f312e32302e35856c696e7578").unwrap(), + ), + mix_hash: H256::from_str( + "70ccadc40b16e2094954b1064749cc6fbac783c1712f1b271a8aac3eda2f2325", + ) + .unwrap(), + nonce: 0, + base_fee_per_gas: Some(7), + withdrawals_root: Some( + H256::from_str("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") + .unwrap(), + ), + blob_gas_used: Some(0), + excess_blob_gas: Some(0x1600000), + }; + + let header = Header::decode(&mut data.as_slice()).unwrap(); + assert_eq!(header, expected); + + let expected_hash = + H256::from_str("0x539c9ea0a3ca49808799d3964b8b6607037227de26bc51073c6926963127087b") + .unwrap(); + assert_eq!(header.hash_slow(), expected_hash); + } + #[test] fn sanity_direction() { let reverse = true; diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 64faa1e03b1..36c6ce3e58a 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -23,6 +23,7 @@ pub mod abi; mod account; pub mod basefee; mod bits; +pub mod blobfee; mod block; pub mod bloom; mod chain; @@ -58,6 +59,8 @@ pub use block::{ BlockWithSenders, ForkBlock, SealedBlock, SealedBlockWithSenders, }; pub use bloom::Bloom; +#[cfg(feature = "optimism")] +pub use chain::OP_GOERLI; pub use chain::{ AllGenesisFormats, Chain, ChainInfo, ChainSpec, ChainSpecBuilder, DisplayHardforks, ForkCondition, ForkTimestamps, DEV, GOERLI, MAINNET, SEPOLIA, @@ -78,7 +81,7 @@ pub use net::{ SEPOLIA_BOOTNODES, }; pub use peer::{PeerId, WithPeerId}; -pub use prune::{PruneCheckpoint, PruneMode, PruneModes, PrunePart}; +pub use prune::{PruneCheckpoint, PruneMode, PruneModes, PrunePart, PrunePartError}; pub use receipt::{Receipt, ReceiptWithBloom, ReceiptWithBloomRef}; pub use revm_primitives::JumpMap; pub use serde_helper::JsonU256; @@ -91,6 +94,8 @@ pub use transaction::{ TxEip1559, TxEip2930, TxEip4844, TxLegacy, TxType, EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID, LEGACY_TX_TYPE_ID, }; +#[cfg(feature = "optimism")] +pub use transaction::{TxDeposit, DEPOSIT_TX_TYPE, DEPOSIT_VERSION}; pub use withdrawal::Withdrawal; /// A block hash. diff --git a/crates/primitives/src/proofs.rs b/crates/primitives/src/proofs.rs index 95fd279d12b..40740d119af 100644 --- a/crates/primitives/src/proofs.rs +++ b/crates/primitives/src/proofs.rs @@ -171,6 +171,8 @@ mod tests { success: true, cumulative_gas_used: 102068, logs, + #[cfg(feature = "optimism")] + deposit_nonce: None, }, bloom, }; diff --git a/crates/primitives/src/prune/mod.rs b/crates/primitives/src/prune/mod.rs index 4dfc591bfcc..a3bcb959627 100644 --- a/crates/primitives/src/prune/mod.rs +++ b/crates/primitives/src/prune/mod.rs @@ -5,5 +5,5 @@ mod target; pub use checkpoint::PruneCheckpoint; pub use mode::PruneMode; -pub use part::PrunePart; +pub use part::{PrunePart, PrunePartError}; pub use target::PruneModes; diff --git a/crates/primitives/src/prune/part.rs b/crates/primitives/src/prune/part.rs index caa176b86a2..db49870735a 100644 --- a/crates/primitives/src/prune/part.rs +++ b/crates/primitives/src/prune/part.rs @@ -1,8 +1,10 @@ +use derive_more::Display; use reth_codecs::{main_codec, Compact}; +use thiserror::Error; /// Part of the data that can be pruned. #[main_codec] -#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)] +#[derive(Debug, Display, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] pub enum PrunePart { /// Prune part responsible for the `TxSenders` table. SenderRecovery, @@ -16,6 +18,14 @@ pub enum PrunePart { StorageHistory, } +/// PrunePart error type. +#[derive(Debug, Error)] +pub enum PrunePartError { + /// Invalid configuration of a prune part. + #[error("The configuration provided for {0} is invalid.")] + Configuration(PrunePart), +} + #[cfg(test)] impl Default for PrunePart { fn default() -> Self { diff --git a/crates/primitives/src/prune/target.rs b/crates/primitives/src/prune/target.rs index b314ae9d038..af6781897c2 100644 --- a/crates/primitives/src/prune/target.rs +++ b/crates/primitives/src/prune/target.rs @@ -1,4 +1,7 @@ -use crate::{serde_helper::deserialize_opt_prune_mode_with_min_blocks, BlockNumber, PruneMode}; +use crate::{ + prune::PrunePartError, serde_helper::deserialize_opt_prune_mode_with_min_blocks, BlockNumber, + PruneMode, PrunePart, +}; use paste::paste; use serde::{Deserialize, Serialize}; @@ -19,10 +22,16 @@ pub struct PruneModes { )] pub receipts: Option, /// Account History pruning configuration. - #[serde(skip_serializing_if = "Option::is_none")] + #[serde( + skip_serializing_if = "Option::is_none", + deserialize_with = "deserialize_opt_prune_mode_with_min_blocks::<64, _>" + )] pub account_history: Option, /// Storage History pruning configuration. - #[serde(skip_serializing_if = "Option::is_none")] + #[serde( + skip_serializing_if = "Option::is_none", + deserialize_with = "deserialize_opt_prune_mode_with_min_blocks::<64, _>" + )] pub storage_history: Option, } @@ -51,12 +60,15 @@ macro_rules! impl_prune_parts { $human_part, " pruning needs to be done, inclusive, according to the provided tip." )] - pub fn [](&self, tip: BlockNumber) -> Option<(BlockNumber, PruneMode)> { - self.$part.as_ref().and_then(|mode| { - self.prune_target_block(mode, tip, $min_blocks).map(|block| { - (block, *mode) - }) - }) + pub fn [](&self, tip: BlockNumber) -> Result, PrunePartError> { + match &self.$part { + Some(mode) => + match self.prune_target_block(mode, tip, $min_blocks) { + Some(block) => Ok(Some((block, *mode))), + None => Err(PrunePartError::Configuration(PrunePart::[<$human_part>])) + } + None => Ok(None) + } } } )+ @@ -107,17 +119,17 @@ impl PruneModes { Some(tip.saturating_sub(*distance)) } PruneMode::Before(n) if tip.saturating_sub(*n) >= min_blocks.unwrap_or_default() => { - Some(*n) + Some(n.saturating_sub(1)) } _ => None, } } impl_prune_parts!( - (sender_recovery, "Sender Recovery", None), - (transaction_lookup, "Transaction Lookup", None), + (sender_recovery, "SenderRecovery", None), + (transaction_lookup, "TransactionLookup", None), (receipts, "Receipts", Some(64)), - (account_history, "Account History", None), - (storage_history, "Storage History", None) + (account_history, "AccountHistory", Some(64)), + (storage_history, "StorageHistory", Some(64)) ); } diff --git a/crates/primitives/src/receipt.rs b/crates/primitives/src/receipt.rs index 15a5a2308df..356d13fdc2d 100644 --- a/crates/primitives/src/receipt.rs +++ b/crates/primitives/src/receipt.rs @@ -28,6 +28,9 @@ pub struct Receipt { ) )] pub logs: Vec, + /// Deposit nonce for Optimism deposited transactions + #[cfg(feature = "optimism")] + pub deposit_nonce: Option, } impl Receipt { @@ -103,7 +106,29 @@ impl ReceiptWithBloom { let bloom = Decodable::decode(b)?; let logs = reth_rlp::Decodable::decode(b)?; - let this = Self { receipt: Receipt { tx_type, success, cumulative_gas_used, logs }, bloom }; + let receipt = match tx_type { + #[cfg(feature = "optimism")] + TxType::DEPOSIT => { + let deposit_nonce = reth_rlp::Decodable::decode(b)?; + Receipt { + tx_type, + success, + cumulative_gas_used, + logs, + deposit_nonce: Some(deposit_nonce), + } + } + _ => Receipt { + tx_type, + success, + cumulative_gas_used, + logs, + #[cfg(feature = "optimism")] + deposit_nonce: None, + }, + }; + + let this = Self { receipt, bloom }; let consumed = started_len - b.len(); if consumed != rlp_head.payload_length { return Err(reth_rlp::DecodeError::ListLengthMismatch { @@ -141,12 +166,22 @@ impl Decodable for ReceiptWithBloom { let receipt_type = *buf.first().ok_or(reth_rlp::DecodeError::Custom( "typed receipt cannot be decoded from an empty slice", ))?; + + #[cfg(feature = "optimism")] + if receipt_type == 0x7E { + buf.advance(1); + Self::decode_receipt(buf, TxType::DEPOSIT) + } + if receipt_type == 0x01 { buf.advance(1); Self::decode_receipt(buf, TxType::EIP2930) } else if receipt_type == 0x02 { buf.advance(1); Self::decode_receipt(buf, TxType::EIP1559) + } else if receipt_type == 0x03 { + buf.advance(1); + Self::decode_receipt(buf, TxType::EIP4844) } else { Err(reth_rlp::DecodeError::Custom("invalid receipt type")) } @@ -251,6 +286,9 @@ impl<'a> ReceiptWithBloomEncoder<'a> { TxType::EIP1559 => { out.put_u8(0x02); } + TxType::EIP4844 => { + out.put_u8(0x03); + } _ => unreachable!("legacy handled; qed."), } out.put_slice(payload.as_ref()); @@ -270,7 +308,7 @@ impl<'a> Encodable for ReceiptWithBloomEncoder<'a> { fn length(&self) -> usize { let mut payload_len = self.receipt_length(); // account for eip-2718 type prefix and set the list - if matches!(self.receipt.tx_type, TxType::EIP1559 | TxType::EIP2930) { + if matches!(self.receipt.tx_type, TxType::EIP1559 | TxType::EIP2930 | TxType::EIP4844) { payload_len += 1; // we include a string header for typed receipts, so include the length here payload_len += length_of_length(payload_len); @@ -313,6 +351,8 @@ mod tests { data: Bytes::from_str("0100ff").unwrap().0.into(), }], success: false, + #[cfg(feature = "optimism")] + deposit_nonce: None, }, bloom: [0; 256].into(), }; @@ -349,6 +389,8 @@ mod tests { data: Bytes::from_str("0100ff").unwrap().0.into(), }], success: false, + #[cfg(feature = "optimism")] + deposit_nonce: None, }, bloom: [0; 256].into(), }; @@ -383,6 +425,8 @@ mod tests { data: crate::Bytes::from(vec![1; 0xffffff]), }, ], + #[cfg(feature = "optimism")] + deposit_nonce: None, }; let mut data = vec![]; diff --git a/crates/primitives/src/transaction/eip1559.rs b/crates/primitives/src/transaction/eip1559.rs new file mode 100644 index 00000000000..3108c6e51e6 --- /dev/null +++ b/crates/primitives/src/transaction/eip1559.rs @@ -0,0 +1,123 @@ +use super::access_list::AccessList; +use crate::{Bytes, ChainId, TransactionKind}; +use reth_codecs::{main_codec, Compact}; +use std::mem; + +/// A transaction with a priority fee ([EIP-1559](https://eips.ethereum.org/EIPS/eip-1559)). +#[main_codec] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] +pub struct TxEip1559 { + /// Added as EIP-pub 155: Simple replay attack protection + pub chain_id: u64, + /// A scalar value equal to the number of transactions sent by the sender; formally Tn. + pub nonce: u64, + /// A scalar value equal to the maximum + /// amount of gas that should be used in executing + /// this transaction. This is paid up-front, before any + /// computation is done and may not be increased + /// later; formally Tg. + pub gas_limit: u64, + /// A scalar value equal to the maximum + /// amount of gas that should be used in executing + /// this transaction. This is paid up-front, before any + /// computation is done and may not be increased + /// later; formally Tg. + /// + /// As ethereum circulation is around 120mil eth as of 2022 that is around + /// 120000000000000000000000000 wei we are safe to use u128 as its max number is: + /// 340282366920938463463374607431768211455 + /// + /// This is also known as `GasFeeCap` + pub max_fee_per_gas: u128, + /// Max Priority fee that transaction is paying + /// + /// As ethereum circulation is around 120mil eth as of 2022 that is around + /// 120000000000000000000000000 wei we are safe to use u128 as its max number is: + /// 340282366920938463463374607431768211455 + /// + /// This is also known as `GasTipCap` + pub max_priority_fee_per_gas: u128, + /// The 160-bit address of the message call’s recipient or, for a contract creation + /// transaction, ∅, used here to denote the only member of B0 ; formally Tt. + pub to: TransactionKind, + /// A scalar value equal to the number of Wei to + /// be transferred to the message call’s recipient or, + /// in the case of contract creation, as an endowment + /// to the newly created account; formally Tv. + /// + /// As ethereum circulation is around 120mil eth as of 2022 that is around + /// 120000000000000000000000000 wei we are safe to use u128 as its max number is: + /// 340282366920938463463374607431768211455 + pub value: u128, + /// The accessList specifies a list of addresses and storage keys; + /// these addresses and storage keys are added into the `accessed_addresses` + /// and `accessed_storage_keys` global sets (introduced in EIP-2929). + /// A gas cost is charged, though at a discount relative to the cost of + /// accessing outside the list. + pub access_list: AccessList, + /// Input has two uses depending if transaction is Create or Call (if `to` field is None or + /// Some). pub init: An unlimited size byte array specifying the + /// EVM-code for the account initialisation procedure CREATE, + /// data: An unlimited size byte array specifying the + /// input data of the message call, formally Td. + pub input: Bytes, +} + +impl TxEip1559 { + /// Calculates a heuristic for the in-memory size of the [TxEip1559] transaction. + #[inline] + pub fn size(&self) -> usize { + mem::size_of::() + // chain_id + mem::size_of::() + // nonce + mem::size_of::() + // gas_limit + mem::size_of::() + // max_fee_per_gas + mem::size_of::() + // max_priority_fee_per_gas + self.to.size() + // to + mem::size_of::() + // value + self.access_list.size() + // access_list + self.input.len() // input + } +} + +#[cfg(test)] +mod tests { + use super::TxEip1559; + use crate::{ + transaction::{signature::Signature, TransactionKind}, + AccessList, Address, Transaction, TransactionSigned, H256, U256, + }; + use std::str::FromStr; + + #[test] + fn recover_signer_eip1559() { + use crate::hex_literal::hex; + + let signer: Address = hex!("dd6b8b3dc6b7ad97db52f08a275ff4483e024cea").into(); + let hash: H256 = + hex!("0ec0b6a2df4d87424e5f6ad2a654e27aaeb7dac20ae9e8385cc09087ad532ee0").into(); + + let tx = Transaction::Eip1559( TxEip1559 { + chain_id: 1, + nonce: 0x42, + gas_limit: 44386, + to: TransactionKind::Call( hex!("6069a6c32cf691f5982febae4faf8a6f3ab2f0f6").into()), + value: 0, + input: hex!("a22cb4650000000000000000000000005eee75727d804a2b13038928d36f8b188945a57a0000000000000000000000000000000000000000000000000000000000000000").into(), + max_fee_per_gas: 0x4a817c800, + max_priority_fee_per_gas: 0x3b9aca00, + access_list: AccessList::default(), + }); + + let sig = Signature { + r: U256::from_str("0x840cfc572845f5786e702984c2a582528cad4b49b2a10b9db1be7fca90058565") + .unwrap(), + s: U256::from_str("0x25e7109ceb98168d95b09b18bbf6b685130e0562f233877d492b94eee0c5b6d1") + .unwrap(), + odd_y_parity: false, + }; + + let signed_tx = TransactionSigned::from_transaction_and_signature(tx, sig); + assert_eq!(signed_tx.hash(), hash, "Expected same hash"); + assert_eq!(signed_tx.recover_signer(), Some(signer), "Recovering signer should pass."); + } +} diff --git a/crates/primitives/src/transaction/eip2930.rs b/crates/primitives/src/transaction/eip2930.rs new file mode 100644 index 00000000000..ecd6ecb7ec2 --- /dev/null +++ b/crates/primitives/src/transaction/eip2930.rs @@ -0,0 +1,127 @@ +use super::access_list::AccessList; +use crate::{Bytes, ChainId, TransactionKind}; +use reth_codecs::{main_codec, Compact}; +use std::mem; + +/// Transaction with an [`AccessList`] ([EIP-2930](https://eips.ethereum.org/EIPS/eip-2930)). +#[main_codec] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] +pub struct TxEip2930 { + /// Added as EIP-pub 155: Simple replay attack protection + pub chain_id: ChainId, + /// A scalar value equal to the number of transactions sent by the sender; formally Tn. + pub nonce: u64, + /// A scalar value equal to the number of + /// Wei to be paid per unit of gas for all computation + /// costs incurred as a result of the execution of this transaction; formally Tp. + /// + /// As ethereum circulation is around 120mil eth as of 2022 that is around + /// 120000000000000000000000000 wei we are safe to use u128 as its max number is: + /// 340282366920938463463374607431768211455 + pub gas_price: u128, + /// A scalar value equal to the maximum + /// amount of gas that should be used in executing + /// this transaction. This is paid up-front, before any + /// computation is done and may not be increased + /// later; formally Tg. + pub gas_limit: u64, + /// The 160-bit address of the message call’s recipient or, for a contract creation + /// transaction, ∅, used here to denote the only member of B0 ; formally Tt. + pub to: TransactionKind, + /// A scalar value equal to the number of Wei to + /// be transferred to the message call’s recipient or, + /// in the case of contract creation, as an endowment + /// to the newly created account; formally Tv. + /// + /// As ethereum circulation is around 120mil eth as of 2022 that is around + /// 120000000000000000000000000 wei we are safe to use u128 as its max number is: + /// 340282366920938463463374607431768211455 + pub value: u128, + /// The accessList specifies a list of addresses and storage keys; + /// these addresses and storage keys are added into the `accessed_addresses` + /// and `accessed_storage_keys` global sets (introduced in EIP-2929). + /// A gas cost is charged, though at a discount relative to the cost of + /// accessing outside the list. + pub access_list: AccessList, + /// Input has two uses depending if transaction is Create or Call (if `to` field is None or + /// Some). pub init: An unlimited size byte array specifying the + /// EVM-code for the account initialisation procedure CREATE, + /// data: An unlimited size byte array specifying the + /// input data of the message call, formally Td. + pub input: Bytes, +} + +impl TxEip2930 { + /// Calculates a heuristic for the in-memory size of the [TxEip2930] transaction. + #[inline] + pub fn size(&self) -> usize { + mem::size_of::() + // chain_id + mem::size_of::() + // nonce + mem::size_of::() + // gas_price + mem::size_of::() + // gas_limit + self.to.size() + // to + mem::size_of::() + // value + self.access_list.size() + // access_list + self.input.len() // input + } +} + +#[cfg(test)] +mod tests { + use super::TxEip2930; + use crate::{ + transaction::{signature::Signature, TransactionKind}, + Address, Bytes, Transaction, TransactionSigned, U256, + }; + use bytes::BytesMut; + use reth_rlp::{Decodable, Encodable}; + + #[test] + fn test_decode_create() { + // tests that a contract creation tx encodes and decodes properly + let request = Transaction::Eip2930(TxEip2930 { + chain_id: 1u64, + nonce: 0, + gas_price: 1, + gas_limit: 2, + to: TransactionKind::Create, + value: 3, + input: Bytes::from(vec![1, 2]), + access_list: Default::default(), + }); + let signature = Signature { odd_y_parity: true, r: U256::default(), s: U256::default() }; + let tx = TransactionSigned::from_transaction_and_signature(request, signature); + + let mut encoded = BytesMut::new(); + tx.encode(&mut encoded); + assert_eq!(encoded.len(), tx.length()); + + let decoded = TransactionSigned::decode(&mut &*encoded).unwrap(); + assert_eq!(decoded, tx); + } + + #[test] + fn test_decode_call() { + let request = Transaction::Eip2930(TxEip2930 { + chain_id: 1u64, + nonce: 0, + gas_price: 1, + gas_limit: 2, + to: TransactionKind::Call(Address::default()), + value: 3, + input: Bytes::from(vec![1, 2]), + access_list: Default::default(), + }); + + let signature = Signature { odd_y_parity: true, r: U256::default(), s: U256::default() }; + + let tx = TransactionSigned::from_transaction_and_signature(request, signature); + + let mut encoded = BytesMut::new(); + tx.encode(&mut encoded); + assert_eq!(encoded.len(), tx.length()); + + let decoded = TransactionSigned::decode(&mut &*encoded).unwrap(); + assert_eq!(decoded, tx); + } +} diff --git a/crates/primitives/src/transaction/eip4844.rs b/crates/primitives/src/transaction/eip4844.rs new file mode 100644 index 00000000000..34b43e19bb6 --- /dev/null +++ b/crates/primitives/src/transaction/eip4844.rs @@ -0,0 +1,118 @@ +use super::access_list::AccessList; +use crate::{constants::eip4844::DATA_GAS_PER_BLOB, Bytes, ChainId, TransactionKind, H256}; +use reth_codecs::{main_codec, Compact}; +use std::mem; + +/// [EIP-4844 Blob Transaction](https://eips.ethereum.org/EIPS/eip-4844#blob-transaction) +/// +/// A transaction with blob hashes and max blob fee +#[main_codec] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] +pub struct TxEip4844 { + /// Added as EIP-pub 155: Simple replay attack protection + pub chain_id: u64, + /// A scalar value equal to the number of transactions sent by the sender; formally Tn. + pub nonce: u64, + /// A scalar value equal to the maximum + /// amount of gas that should be used in executing + /// this transaction. This is paid up-front, before any + /// computation is done and may not be increased + /// later; formally Tg. + pub gas_limit: u64, + /// A scalar value equal to the maximum + /// amount of gas that should be used in executing + /// this transaction. This is paid up-front, before any + /// computation is done and may not be increased + /// later; formally Tg. + /// + /// As ethereum circulation is around 120mil eth as of 2022 that is around + /// 120000000000000000000000000 wei we are safe to use u128 as its max number is: + /// 340282366920938463463374607431768211455 + /// + /// This is also known as `GasFeeCap` + pub max_fee_per_gas: u128, + /// Max Priority fee that transaction is paying + /// + /// As ethereum circulation is around 120mil eth as of 2022 that is around + /// 120000000000000000000000000 wei we are safe to use u128 as its max number is: + /// 340282366920938463463374607431768211455 + /// + /// This is also known as `GasTipCap` + pub max_priority_fee_per_gas: u128, + /// The 160-bit address of the message call’s recipient or, for a contract creation + /// transaction, ∅, used here to denote the only member of B0 ; formally Tt. + pub to: TransactionKind, + /// A scalar value equal to the number of Wei to + /// be transferred to the message call’s recipient or, + /// in the case of contract creation, as an endowment + /// to the newly created account; formally Tv. + /// + /// As ethereum circulation is around 120mil eth as of 2022 that is around + /// 120000000000000000000000000 wei we are safe to use u128 as its max number is: + /// 340282366920938463463374607431768211455 + pub value: u128, + /// The accessList specifies a list of addresses and storage keys; + /// these addresses and storage keys are added into the `accessed_addresses` + /// and `accessed_storage_keys` global sets (introduced in EIP-2929). + /// A gas cost is charged, though at a discount relative to the cost of + /// accessing outside the list. + pub access_list: AccessList, + + /// It contains a vector of fixed size hash(32 bytes) + pub blob_versioned_hashes: Vec, + + /// Max fee per data gas + /// + /// aka BlobFeeCap + pub max_fee_per_blob_gas: u128, + + /// Input has two uses depending if transaction is Create or Call (if `to` field is None or + /// Some). pub init: An unlimited size byte array specifying the + /// EVM-code for the account initialisation procedure CREATE, + /// data: An unlimited size byte array specifying the + /// input data of the message call, formally Td. + pub input: Bytes, +} + +impl TxEip4844 { + /// Returns the effective gas price for the given `base_fee`. + pub fn effective_gas_price(&self, base_fee: Option) -> u128 { + match base_fee { + None => self.max_fee_per_gas, + Some(base_fee) => { + // if the tip is greater than the max priority fee per gas, set it to the max + // priority fee per gas + base fee + let tip = self.max_fee_per_gas.saturating_sub(base_fee as u128); + if tip > self.max_priority_fee_per_gas { + self.max_priority_fee_per_gas + base_fee as u128 + } else { + // otherwise return the max fee per gas + self.max_fee_per_gas + } + } + } + } + + /// Returns the total gas for all blobs in this transaction. + #[inline] + pub fn blob_gas(&self) -> u64 { + // SAFETY: we don't expect u64::MAX / DATA_GAS_PER_BLOB hashes in a single transaction + self.blob_versioned_hashes.len() as u64 * DATA_GAS_PER_BLOB + } + + /// Calculates a heuristic for the in-memory size of the [TxEip4844] transaction. + #[inline] + pub fn size(&self) -> usize { + mem::size_of::() + // chain_id + mem::size_of::() + // nonce + mem::size_of::() + // gas_limit + mem::size_of::() + // max_fee_per_gas + mem::size_of::() + // max_priority_fee_per_gas + self.to.size() + // to + mem::size_of::() + // value + self.access_list.size() + // access_list + self.input.len() + // input + self.blob_versioned_hashes.capacity() * mem::size_of::() + // blob hashes size + mem::size_of::() // max_fee_per_data_gas + } +} diff --git a/crates/primitives/src/transaction/legacy.rs b/crates/primitives/src/transaction/legacy.rs new file mode 100644 index 00000000000..cd324732bd5 --- /dev/null +++ b/crates/primitives/src/transaction/legacy.rs @@ -0,0 +1,101 @@ +use crate::{Bytes, ChainId, TransactionKind}; +use reth_codecs::{main_codec, Compact}; +use std::mem; + +/// Legacy transaction. +#[main_codec] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] +pub struct TxLegacy { + /// Added as EIP-155: Simple replay attack protection + pub chain_id: Option, + /// A scalar value equal to the number of transactions sent by the sender; formally Tn. + pub nonce: u64, + /// A scalar value equal to the number of + /// Wei to be paid per unit of gas for all computation + /// costs incurred as a result of the execution of this transaction; formally Tp. + /// + /// As ethereum circulation is around 120mil eth as of 2022 that is around + /// 120000000000000000000000000 wei we are safe to use u128 as its max number is: + /// 340282366920938463463374607431768211455 + pub gas_price: u128, + /// A scalar value equal to the maximum + /// amount of gas that should be used in executing + /// this transaction. This is paid up-front, before any + /// computation is done and may not be increased + /// later; formally Tg. + pub gas_limit: u64, + /// The 160-bit address of the message call’s recipient or, for a contract creation + /// transaction, ∅, used here to denote the only member of B0 ; formally Tt. + pub to: TransactionKind, + /// A scalar value equal to the number of Wei to + /// be transferred to the message call’s recipient or, + /// in the case of contract creation, as an endowment + /// to the newly created account; formally Tv. + /// + /// As ethereum circulation is around 120mil eth as of 2022 that is around + /// 120000000000000000000000000 wei we are safe to use u128 as its max number is: + /// 340282366920938463463374607431768211455 + pub value: u128, + /// Input has two uses depending if transaction is Create or Call (if `to` field is None or + /// Some). pub init: An unlimited size byte array specifying the + /// EVM-code for the account initialisation procedure CREATE, + /// data: An unlimited size byte array specifying the + /// input data of the message call, formally Td. + pub input: Bytes, +} + +impl TxLegacy { + /// Calculates a heuristic for the in-memory size of the [TxLegacy] transaction. + #[inline] + pub fn size(&self) -> usize { + mem::size_of::>() + // chain_id + mem::size_of::() + // nonce + mem::size_of::() + // gas_price + mem::size_of::() + // gas_limit + self.to.size() + // to + mem::size_of::() + // value + self.input.len() // input + } +} + +#[cfg(test)] +mod tests { + use super::TxLegacy; + use crate::{ + transaction::{signature::Signature, TransactionKind}, + Address, Transaction, TransactionSigned, H256, U256, + }; + + #[test] + fn recover_signer_legacy() { + use crate::hex_literal::hex; + + let signer: Address = hex!("398137383b3d25c92898c656696e41950e47316b").into(); + let hash: H256 = + hex!("bb3a336e3f823ec18197f1e13ee875700f08f03e2cab75f0d0b118dabb44cba0").into(); + + let tx = Transaction::Legacy(TxLegacy { + chain_id: Some(1), + nonce: 0x18, + gas_price: 0xfa56ea00, + gas_limit: 119902, + to: TransactionKind::Call( hex!("06012c8cf97bead5deae237070f9587f8e7a266d").into()), + value: 0x1c6bf526340000u64.into(), + input: hex!("f7d8c88300000000000000000000000000000000000000000000000000000000000cee6100000000000000000000000000000000000000000000000000000000000ac3e1").into(), + }); + + let sig = Signature { + r: U256::from_be_bytes(hex!( + "2a378831cf81d99a3f06a18ae1b6ca366817ab4d88a70053c41d7a8f0368e031" + )), + s: U256::from_be_bytes(hex!( + "450d831a05b6e418724436c05c155e0a1b7b921015d0fbc2f667aed709ac4fb5" + )), + odd_y_parity: false, + }; + + let signed_tx = TransactionSigned::from_transaction_and_signature(tx, sig); + assert_eq!(signed_tx.hash(), hash, "Expected same hash"); + assert_eq!(signed_tx.recover_signer(), Some(signer), "Recovering signer should pass."); + } +} diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index 5a22b60f9bc..9c7e5ae529f 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -1,301 +1,43 @@ -use std::mem; - use crate::{ compression::{TRANSACTION_COMPRESSOR, TRANSACTION_DECOMPRESSOR}, - keccak256, Address, Bytes, ChainId, TxHash, H256, + keccak256, Address, Bytes, TxHash, H256, }; pub use access_list::{AccessList, AccessListItem, AccessListWithGasUsed}; use bytes::{Buf, BytesMut}; use derive_more::{AsRef, Deref}; pub use error::InvalidTransactionError; pub use meta::TransactionMeta; -use reth_codecs::{add_arbitrary_tests, derive_arbitrary, main_codec, Compact}; +use reth_codecs::{add_arbitrary_tests, derive_arbitrary, Compact}; use reth_rlp::{ length_of_length, Decodable, DecodeError, Encodable, Header, EMPTY_LIST_CODE, EMPTY_STRING_CODE, }; use serde::{Deserialize, Serialize}; pub use signature::Signature; +use std::mem; pub use tx_type::{TxType, EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID, LEGACY_TX_TYPE_ID}; +#[cfg(feature = "optimism")] +pub use optimism::{TxDeposit, DEPOSIT_TX_TYPE, DEPOSIT_VERSION}; + +pub use eip1559::TxEip1559; +pub use eip2930::TxEip2930; +pub use eip4844::TxEip4844; +pub use legacy::TxLegacy; + +#[cfg(feature = "optimism")] +mod optimism; + mod access_list; +mod eip1559; +mod eip2930; +mod eip4844; mod error; +mod legacy; mod meta; mod signature; mod tx_type; pub(crate) mod util; -/// Legacy transaction. -#[main_codec] -#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] -pub struct TxLegacy { - /// Added as EIP-155: Simple replay attack protection - pub chain_id: Option, - /// A scalar value equal to the number of transactions sent by the sender; formally Tn. - pub nonce: u64, - /// A scalar value equal to the number of - /// Wei to be paid per unit of gas for all computation - /// costs incurred as a result of the execution of this transaction; formally Tp. - /// - /// As ethereum circulation is around 120mil eth as of 2022 that is around - /// 120000000000000000000000000 wei we are safe to use u128 as its max number is: - /// 340282366920938463463374607431768211455 - pub gas_price: u128, - /// A scalar value equal to the maximum - /// amount of gas that should be used in executing - /// this transaction. This is paid up-front, before any - /// computation is done and may not be increased - /// later; formally Tg. - pub gas_limit: u64, - /// The 160-bit address of the message call’s recipient or, for a contract creation - /// transaction, ∅, used here to denote the only member of B0 ; formally Tt. - pub to: TransactionKind, - /// A scalar value equal to the number of Wei to - /// be transferred to the message call’s recipient or, - /// in the case of contract creation, as an endowment - /// to the newly created account; formally Tv. - /// - /// As ethereum circulation is around 120mil eth as of 2022 that is around - /// 120000000000000000000000000 wei we are safe to use u128 as its max number is: - /// 340282366920938463463374607431768211455 - pub value: u128, - /// Input has two uses depending if transaction is Create or Call (if `to` field is None or - /// Some). pub init: An unlimited size byte array specifying the - /// EVM-code for the account initialisation procedure CREATE, - /// data: An unlimited size byte array specifying the - /// input data of the message call, formally Td. - pub input: Bytes, -} - -impl TxLegacy { - /// Calculates a heuristic for the in-memory size of the [TxLegacy] transaction. - #[inline] - fn size(&self) -> usize { - mem::size_of::>() + // chain_id - mem::size_of::() + // nonce - mem::size_of::() + // gas_price - mem::size_of::() + // gas_limit - self.to.size() + // to - mem::size_of::() + // value - self.input.len() // input - } -} - -/// Transaction with an [`AccessList`] ([EIP-2930](https://eips.ethereum.org/EIPS/eip-2930)). -#[main_codec] -#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] -pub struct TxEip2930 { - /// Added as EIP-pub 155: Simple replay attack protection - pub chain_id: ChainId, - /// A scalar value equal to the number of transactions sent by the sender; formally Tn. - pub nonce: u64, - /// A scalar value equal to the number of - /// Wei to be paid per unit of gas for all computation - /// costs incurred as a result of the execution of this transaction; formally Tp. - /// - /// As ethereum circulation is around 120mil eth as of 2022 that is around - /// 120000000000000000000000000 wei we are safe to use u128 as its max number is: - /// 340282366920938463463374607431768211455 - pub gas_price: u128, - /// A scalar value equal to the maximum - /// amount of gas that should be used in executing - /// this transaction. This is paid up-front, before any - /// computation is done and may not be increased - /// later; formally Tg. - pub gas_limit: u64, - /// The 160-bit address of the message call’s recipient or, for a contract creation - /// transaction, ∅, used here to denote the only member of B0 ; formally Tt. - pub to: TransactionKind, - /// A scalar value equal to the number of Wei to - /// be transferred to the message call’s recipient or, - /// in the case of contract creation, as an endowment - /// to the newly created account; formally Tv. - /// - /// As ethereum circulation is around 120mil eth as of 2022 that is around - /// 120000000000000000000000000 wei we are safe to use u128 as its max number is: - /// 340282366920938463463374607431768211455 - pub value: u128, - /// The accessList specifies a list of addresses and storage keys; - /// these addresses and storage keys are added into the `accessed_addresses` - /// and `accessed_storage_keys` global sets (introduced in EIP-2929). - /// A gas cost is charged, though at a discount relative to the cost of - /// accessing outside the list. - pub access_list: AccessList, - /// Input has two uses depending if transaction is Create or Call (if `to` field is None or - /// Some). pub init: An unlimited size byte array specifying the - /// EVM-code for the account initialisation procedure CREATE, - /// data: An unlimited size byte array specifying the - /// input data of the message call, formally Td. - pub input: Bytes, -} - -impl TxEip2930 { - /// Calculates a heuristic for the in-memory size of the [TxEip2930] transaction. - #[inline] - pub fn size(&self) -> usize { - mem::size_of::() + // chain_id - mem::size_of::() + // nonce - mem::size_of::() + // gas_price - mem::size_of::() + // gas_limit - self.to.size() + // to - mem::size_of::() + // value - self.access_list.size() + // access_list - self.input.len() // input - } -} - -/// A transaction with a priority fee ([EIP-1559](https://eips.ethereum.org/EIPS/eip-1559)). -#[main_codec] -#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] -pub struct TxEip1559 { - /// Added as EIP-pub 155: Simple replay attack protection - pub chain_id: u64, - /// A scalar value equal to the number of transactions sent by the sender; formally Tn. - pub nonce: u64, - /// A scalar value equal to the maximum - /// amount of gas that should be used in executing - /// this transaction. This is paid up-front, before any - /// computation is done and may not be increased - /// later; formally Tg. - pub gas_limit: u64, - /// A scalar value equal to the maximum - /// amount of gas that should be used in executing - /// this transaction. This is paid up-front, before any - /// computation is done and may not be increased - /// later; formally Tg. - /// - /// As ethereum circulation is around 120mil eth as of 2022 that is around - /// 120000000000000000000000000 wei we are safe to use u128 as its max number is: - /// 340282366920938463463374607431768211455 - pub max_fee_per_gas: u128, - /// Max Priority fee that transaction is paying - /// - /// As ethereum circulation is around 120mil eth as of 2022 that is around - /// 120000000000000000000000000 wei we are safe to use u128 as its max number is: - /// 340282366920938463463374607431768211455 - pub max_priority_fee_per_gas: u128, - /// The 160-bit address of the message call’s recipient or, for a contract creation - /// transaction, ∅, used here to denote the only member of B0 ; formally Tt. - pub to: TransactionKind, - /// A scalar value equal to the number of Wei to - /// be transferred to the message call’s recipient or, - /// in the case of contract creation, as an endowment - /// to the newly created account; formally Tv. - /// - /// As ethereum circulation is around 120mil eth as of 2022 that is around - /// 120000000000000000000000000 wei we are safe to use u128 as its max number is: - /// 340282366920938463463374607431768211455 - pub value: u128, - /// The accessList specifies a list of addresses and storage keys; - /// these addresses and storage keys are added into the `accessed_addresses` - /// and `accessed_storage_keys` global sets (introduced in EIP-2929). - /// A gas cost is charged, though at a discount relative to the cost of - /// accessing outside the list. - pub access_list: AccessList, - /// Input has two uses depending if transaction is Create or Call (if `to` field is None or - /// Some). pub init: An unlimited size byte array specifying the - /// EVM-code for the account initialisation procedure CREATE, - /// data: An unlimited size byte array specifying the - /// input data of the message call, formally Td. - pub input: Bytes, -} - -impl TxEip1559 { - /// Calculates a heuristic for the in-memory size of the [TxEip1559] transaction. - #[inline] - pub fn size(&self) -> usize { - mem::size_of::() + // chain_id - mem::size_of::() + // nonce - mem::size_of::() + // gas_limit - mem::size_of::() + // max_fee_per_gas - mem::size_of::() + // max_priority_fee_per_gas - self.to.size() + // to - mem::size_of::() + // value - self.access_list.size() + // access_list - self.input.len() // input - } -} - -/// A transaction with blob hashes and max blob fee -#[main_codec] -#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] -pub struct TxEip4844 { - /// Added as EIP-pub 155: Simple replay attack protection - pub chain_id: u64, - /// A scalar value equal to the number of transactions sent by the sender; formally Tn. - pub nonce: u64, - /// A scalar value equal to the maximum - /// amount of gas that should be used in executing - /// this transaction. This is paid up-front, before any - /// computation is done and may not be increased - /// later; formally Tg. - pub gas_limit: u64, - /// A scalar value equal to the maximum - /// amount of gas that should be used in executing - /// this transaction. This is paid up-front, before any - /// computation is done and may not be increased - /// later; formally Tg. - /// - /// As ethereum circulation is around 120mil eth as of 2022 that is around - /// 120000000000000000000000000 wei we are safe to use u128 as its max number is: - /// 340282366920938463463374607431768211455 - pub max_fee_per_gas: u128, - /// Max Priority fee that transaction is paying - /// - /// As ethereum circulation is around 120mil eth as of 2022 that is around - /// 120000000000000000000000000 wei we are safe to use u128 as its max number is: - /// 340282366920938463463374607431768211455 - pub max_priority_fee_per_gas: u128, - /// The 160-bit address of the message call’s recipient or, for a contract creation - /// transaction, ∅, used here to denote the only member of B0 ; formally Tt. - pub to: TransactionKind, - /// A scalar value equal to the number of Wei to - /// be transferred to the message call’s recipient or, - /// in the case of contract creation, as an endowment - /// to the newly created account; formally Tv. - /// - /// As ethereum circulation is around 120mil eth as of 2022 that is around - /// 120000000000000000000000000 wei we are safe to use u128 as its max number is: - /// 340282366920938463463374607431768211455 - pub value: u128, - /// The accessList specifies a list of addresses and storage keys; - /// these addresses and storage keys are added into the `accessed_addresses` - /// and `accessed_storage_keys` global sets (introduced in EIP-2929). - /// A gas cost is charged, though at a discount relative to the cost of - /// accessing outside the list. - pub access_list: AccessList, - - /// It contains a vector of fixed size hash(32 bytes) - pub blob_hashes: Vec, - - /// Max fee per data gas - pub max_fee_per_blob: u128, - - /// Input has two uses depending if transaction is Create or Call (if `to` field is None or - /// Some). pub init: An unlimited size byte array specifying the - /// EVM-code for the account initialisation procedure CREATE, - /// data: An unlimited size byte array specifying the - /// input data of the message call, formally Td. - pub input: Bytes, -} - -impl TxEip4844 { - /// Calculates a heuristic for the in-memory size of the [TxEip4844] transaction. - #[inline] - pub fn size(&self) -> usize { - mem::size_of::() + // chain_id - mem::size_of::() + // nonce - mem::size_of::() + // gas_limit - mem::size_of::() + // max_fee_per_gas - mem::size_of::() + // max_priority_fee_per_gas - self.to.size() + // to - mem::size_of::() + // value - self.access_list.size() + // access_list - self.input.len() + // input - self.blob_hashes.capacity() * mem::size_of::() + // blob hashes size - mem::size_of::() // blob fee cap - } -} - /// A raw transaction. /// /// Transaction types were introduced in [EIP-2718](https://eips.ethereum.org/EIPS/eip-2718). @@ -329,6 +71,9 @@ pub enum Transaction { /// transaction, incentivizing miners to include transactions with higher priority fees per /// gas. Eip1559(TxEip1559), + /// Optimism deposit transaction. + #[cfg(feature = "optimism")] + Deposit(TxDeposit), } impl Transaction { @@ -356,6 +101,23 @@ impl Transaction { self.encode_fields(out); signature.encode_with_eip155_chain_id(out, *chain_id); } + #[cfg(feature = "optimism")] + Transaction::Deposit(_) => { + let payload_length = self.fields_len() + signature.payload_len(); + if with_header { + Header { + list: false, + payload_length: 1 + 1 + length_of_length(payload_length) + payload_length, + } + .encode(out); + } + out.put_u8(self.tx_type() as u8); + out.put_u8(DEPOSIT_VERSION); + let header = Header { list: true, payload_length }; + header.encode(out); + self.encode_fields(out); + signature.encode(out); + } _ => { let payload_length = self.fields_len() + signature.payload_len(); if with_header { @@ -380,6 +142,8 @@ impl Transaction { Transaction::Legacy(tx) => tx.nonce = nonce, Transaction::Eip2930(tx) => tx.nonce = nonce, Transaction::Eip1559(tx) => tx.nonce = nonce, + #[cfg(feature = "optimism")] + Transaction::Deposit(_tx) => { /* noop */ } } } @@ -389,6 +153,8 @@ impl Transaction { Transaction::Legacy(tx) => tx.value = value, Transaction::Eip2930(tx) => tx.value = value, Transaction::Eip1559(tx) => tx.value = value, + #[cfg(feature = "optimism")] + Transaction::Deposit(tx) => tx.value = value, } } @@ -398,6 +164,8 @@ impl Transaction { Transaction::Legacy(tx) => tx.input = input, Transaction::Eip2930(tx) => tx.input = input, Transaction::Eip1559(tx) => tx.input = input, + #[cfg(feature = "optimism")] + Transaction::Deposit(tx) => tx.input = input, } } @@ -408,32 +176,42 @@ impl Transaction { Transaction::Legacy(tx) => tx.size(), Transaction::Eip2930(tx) => tx.size(), Transaction::Eip1559(tx) => tx.size(), + #[cfg(feature = "optimism")] + Transaction::Deposit(tx) => tx.size(), } } } impl Compact for Transaction { + // Serializes the TxType to the buffer if necessary, returning 2 bits of the type as an + // identifier instead of the length. fn to_compact(self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { + let identifier = self.tx_type().to_compact(buf); match self { Transaction::Legacy(tx) => { tx.to_compact(buf); - 0 } Transaction::Eip2930(tx) => { tx.to_compact(buf); - 1 } Transaction::Eip1559(tx) => { tx.to_compact(buf); - 2 + } + #[cfg(feature = "optimism")] + Transaction::Deposit(tx) => { + tx.to_compact(buf); } } + identifier } - fn from_compact(buf: &[u8], identifier: usize) -> (Self, &[u8]) { + // For backwards compatibility purposes, only 2 bits of the type are encoded in the identifier + // parameter. In the case of a 3, the full transaction type is read from the buffer as a + // single byte. + fn from_compact(mut buf: &[u8], identifier: usize) -> (Self, &[u8]) { match identifier { 0 => { let (tx, buf) = TxLegacy::from_compact(buf, buf.len()); @@ -447,6 +225,17 @@ impl Compact for Transaction { let (tx, buf) = TxEip1559::from_compact(buf, buf.len()); (Transaction::Eip1559(tx), buf) } + 3 => { + let identifier = buf.get_u8() as usize; + match identifier { + #[cfg(feature = "optimism")] + 126 => { + let (tx, buf) = TxDeposit::from_compact(buf, buf.len()); + (Transaction::Deposit(tx), buf) + } + _ => unreachable!("Junk data in database: unknown Transaction variant"), + } + } _ => unreachable!("Junk data in database: unknown Transaction variant"), } } @@ -469,6 +258,8 @@ impl Transaction { Transaction::Legacy(TxLegacy { chain_id, .. }) => *chain_id, Transaction::Eip2930(TxEip2930 { chain_id, .. }) => Some(*chain_id), Transaction::Eip1559(TxEip1559 { chain_id, .. }) => Some(*chain_id), + #[cfg(feature = "optimism")] + Transaction::Deposit(_) => None, } } @@ -478,6 +269,8 @@ impl Transaction { Transaction::Legacy(TxLegacy { chain_id: ref mut c, .. }) => *c = Some(chain_id), Transaction::Eip2930(TxEip2930 { chain_id: ref mut c, .. }) => *c = chain_id, Transaction::Eip1559(TxEip1559 { chain_id: ref mut c, .. }) => *c = chain_id, + #[cfg(feature = "optimism")] + Transaction::Deposit(_) => (), } } @@ -488,6 +281,8 @@ impl Transaction { Transaction::Legacy(TxLegacy { to, .. }) | Transaction::Eip2930(TxEip2930 { to, .. }) | Transaction::Eip1559(TxEip1559 { to, .. }) => to, + #[cfg(feature = "optimism")] + Transaction::Deposit(TxDeposit { to, .. }) => to, } } @@ -502,6 +297,8 @@ impl Transaction { Transaction::Legacy { .. } => TxType::Legacy, Transaction::Eip2930 { .. } => TxType::EIP2930, Transaction::Eip1559 { .. } => TxType::EIP1559, + #[cfg(feature = "optimism")] + Transaction::Deposit(_) => TxType::DEPOSIT, } } @@ -511,6 +308,8 @@ impl Transaction { Transaction::Legacy(TxLegacy { value, .. }) => value, Transaction::Eip2930(TxEip2930 { value, .. }) => value, Transaction::Eip1559(TxEip1559 { value, .. }) => value, + #[cfg(feature = "optimism")] + Transaction::Deposit(TxDeposit { value, .. }) => value, } } @@ -520,6 +319,9 @@ impl Transaction { Transaction::Legacy(TxLegacy { nonce, .. }) => *nonce, Transaction::Eip2930(TxEip2930 { nonce, .. }) => *nonce, Transaction::Eip1559(TxEip1559 { nonce, .. }) => *nonce, + // Deposit transactions don't have a nonce, so they default to zero. + #[cfg(feature = "optimism")] + Transaction::Deposit(_) => 0, } } @@ -529,6 +331,8 @@ impl Transaction { Transaction::Legacy(TxLegacy { gas_limit, .. }) | Transaction::Eip2930(TxEip2930 { gas_limit, .. }) | Transaction::Eip1559(TxEip1559 { gas_limit, .. }) => *gas_limit, + #[cfg(feature = "optimism")] + Transaction::Deposit(TxDeposit { gas_limit, .. }) => *gas_limit, } } @@ -540,6 +344,10 @@ impl Transaction { Transaction::Legacy(TxLegacy { gas_price, .. }) | Transaction::Eip2930(TxEip2930 { gas_price, .. }) => *gas_price, Transaction::Eip1559(TxEip1559 { 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")] + Transaction::Deposit(_) => 0, } } @@ -554,6 +362,8 @@ impl Transaction { Transaction::Eip1559(TxEip1559 { max_priority_fee_per_gas, .. }) => { Some(*max_priority_fee_per_gas) } + #[cfg(feature = "optimism")] + Transaction::Deposit(_) => None, } } @@ -571,6 +381,8 @@ impl Transaction { Transaction::Eip1559(TxEip1559 { max_priority_fee_per_gas, .. }) => { *max_priority_fee_per_gas } + #[cfg(feature = "optimism")] + Transaction::Deposit(_) => 0, } } @@ -582,6 +394,8 @@ impl Transaction { Transaction::Legacy(tx) => tx.gas_price, Transaction::Eip2930(tx) => tx.gas_price, Transaction::Eip1559(dynamic_tx) => dynamic_tx.effective_gas_price(base_fee), + #[cfg(feature = "optimism")] + Transaction::Deposit(_) => 0, } } @@ -637,9 +451,47 @@ impl Transaction { Transaction::Legacy(TxLegacy { input, .. }) => input, Transaction::Eip2930(TxEip2930 { input, .. }) => input, Transaction::Eip1559(TxEip1559 { input, .. }) => input, + #[cfg(feature = "optimism")] + Transaction::Deposit(TxDeposit { input, .. }) => input, + } + } + + /// Returns the source hash of the transaction, which uniquely identifies its source. + /// If the transaction is not a deposit transaction, this will always return `H256::zero()`. + #[cfg(feature = "optimism")] + pub fn source_hash(&self) -> H256 { + match self { + Transaction::Deposit(TxDeposit { source_hash, .. }) => *source_hash, + _ => H256::zero(), + } + } + + /// Returns the amount of ETH locked up on L1 that will be minted on L2. If the transaction + /// is not a deposit transaction, this will always return `None`. + #[cfg(feature = "optimism")] + pub fn mint(&self) -> Option { + match self { + Transaction::Deposit(TxDeposit { mint, .. }) => *mint, + _ => None, } } + /// Returns whether or not the transaction is a system transaction. If the transaction + /// is not a deposit transaction, this will always return `false`. + #[cfg(feature = "optimism")] + pub fn is_system_transaction(&self) -> bool { + match self { + Transaction::Deposit(TxDeposit { is_system_transaction, .. }) => *is_system_transaction, + _ => false, + } + } + + /// Returns whether or not the transaction is an Optimism Deposited transaction. + #[cfg(feature = "optimism")] + pub fn is_deposit(&self) -> bool { + matches!(self, Transaction::Deposit(_)) + } + /// Encodes EIP-155 arguments into the desired buffer. Only encodes values for legacy /// transactions. pub(crate) fn encode_eip155_fields(&self, out: &mut dyn bytes::BufMut) { @@ -733,6 +585,8 @@ impl Transaction { len += access_list.length(); len } + #[cfg(feature = "optimism")] + Transaction::Deposit(deposit) => deposit.fields_len(), } } @@ -795,6 +649,8 @@ impl Transaction { input.0.encode(out); access_list.encode(out); } + #[cfg(feature = "optimism")] + Transaction::Deposit(deposit) => deposit.encode_fields(out), } } } @@ -816,6 +672,13 @@ impl Encodable for Transaction { self.encode_fields(out); self.encode_eip155_fields(out); } + #[cfg(feature = "optimism")] + Transaction::Deposit(_) => { + out.put_u8(self.tx_type() as u8); + out.put_u8(DEPOSIT_VERSION); + Header { list: true, payload_length: self.fields_len() }.encode(out); + self.encode_fields(out); + } _ => { out.put_u8(self.tx_type() as u8); Header { list: true, payload_length: self.fields_len() }.encode(out); @@ -831,6 +694,12 @@ impl Encodable for Transaction { // 'header length' + 'payload length' length_of_length(payload_length) + payload_length } + #[cfg(feature = "optimism")] + Transaction::Deposit { .. } => { + let payload_length = self.fields_len(); + // 'tx type byte length' + 'version byte' + 'header length' + 'payload length' + 1 + 1 + length_of_length(payload_length) + payload_length + } _ => { let payload_length = self.fields_len(); // 'transaction type byte length' + 'header length' + 'payload length' @@ -848,7 +717,7 @@ impl TxEip1559 { Some(base_fee) => { // if the tip is greater than the max priority fee per gas, set it to the max // priority fee per gas + base fee - let tip = self.max_fee_per_gas - base_fee as u128; + let tip = self.max_fee_per_gas.saturating_sub(base_fee as u128); if tip > self.max_priority_fee_per_gas { self.max_priority_fee_per_gas + base_fee as u128 } else { @@ -1109,6 +978,10 @@ impl TransactionSigned { /// /// Returns `None` if the transaction's signature is invalid, see also [Self::recover_signer]. pub fn recover_signer(&self) -> Option
{ + #[cfg(feature = "optimism")] + if let Transaction::Deposit(TxDeposit { from, .. }) = self.transaction { + return Some(from) + } let signature_hash = self.signature_hash(); self.signature.recover_signer(signature_hash) } @@ -1173,6 +1046,13 @@ impl TransactionSigned { // 'header length' + 'payload length' length_of_length(payload_length) + payload_length } + #[cfg(feature = "optimism")] + Transaction::Deposit(_) => { + let payload_length = self.transaction.fields_len() + self.signature.payload_len(); + // 'tx type byte length' + 'version byte' + 'header length' + 'payload length' + let len = 1 + 1 + length_of_length(payload_length) + payload_length; + length_of_length(len) + len + } _ => { let payload_length = self.transaction.fields_len() + self.signature.payload_len(); // 'transaction type byte length' + 'header length' + 'payload length' @@ -1244,6 +1124,18 @@ impl TransactionSigned { let tx_type = *data.first().ok_or(DecodeError::InputTooShort)?; data.advance(1); + + // If the transaction is a deposit, we need to first ensure that the version + // byte is correct. + #[cfg(feature = "optimism")] + if tx_type == DEPOSIT_TX_TYPE { + let version = *data.first().ok_or(DecodeError::InputTooShort)?; + if version != DEPOSIT_VERSION { + return Err(DecodeError::Custom("Deposit version mismatch")) + } + data.advance(1); + } + // decode the list header for the rest of the transaction let header = Header::decode(data)?; if !header.list { @@ -1252,6 +1144,10 @@ impl TransactionSigned { // length of tx encoding = tx type byte (size = 1) + length of header + payload length let tx_length = 1 + header.length() + header.payload_length; + // If the transaction is a deposit, we need to add one to the length to account for the + // version byte. + #[cfg(feature = "optimism")] + let tx_length = if tx_type == DEPOSIT_TX_TYPE { tx_length + 1 } else { tx_length }; // decode common fields let transaction = match tx_type { @@ -1276,6 +1172,22 @@ impl TransactionSigned { input: Bytes(Decodable::decode(data)?), access_list: Decodable::decode(data)?, }), + #[cfg(feature = "optimism")] + DEPOSIT_TX_TYPE => Transaction::Deposit(TxDeposit { + source_hash: Decodable::decode(data)?, + from: Decodable::decode(data)?, + to: Decodable::decode(data)?, + mint: if *data.first().ok_or(DecodeError::InputTooShort)? == EMPTY_STRING_CODE { + data.advance(1); + None + } else { + Some(Decodable::decode(data)?) + }, + value: Decodable::decode(data)?, + input: Decodable::decode(data)?, + gas_limit: Decodable::decode(data)?, + is_system_transaction: Decodable::decode(data)?, + }), _ => return Err(DecodeError::Custom("unsupported typed transaction type")), }; @@ -1484,9 +1396,8 @@ impl IntoRecoveredTransaction for TransactionSignedEcRecovered { #[cfg(test)] mod tests { use crate::{ - transaction::{signature::Signature, TransactionKind, TxEip1559, TxEip2930, TxLegacy}, - AccessList, Address, Bytes, Transaction, TransactionSigned, TransactionSignedEcRecovered, - H256, U256, + transaction::{signature::Signature, TransactionKind, TxEip1559, TxLegacy}, + Address, Bytes, Transaction, TransactionSigned, TransactionSignedEcRecovered, H256, U256, }; use bytes::BytesMut; use ethers_core::utils::hex; @@ -1500,30 +1411,6 @@ mod tests { assert_eq!(DecodeError::InputTooShort, res); } - #[test] - fn test_decode_create() { - // tests that a contract creation tx encodes and decodes properly - let request = Transaction::Eip2930(TxEip2930 { - chain_id: 1u64, - nonce: 0, - gas_price: 1, - gas_limit: 2, - to: TransactionKind::Create, - value: 3, - input: Bytes::from(vec![1, 2]), - access_list: Default::default(), - }); - let signature = Signature { odd_y_parity: true, r: U256::default(), s: U256::default() }; - let tx = TransactionSigned::from_transaction_and_signature(request, signature); - - let mut encoded = BytesMut::new(); - tx.encode(&mut encoded); - assert_eq!(encoded.len(), tx.length()); - - let decoded = TransactionSigned::decode(&mut &*encoded).unwrap(); - assert_eq!(decoded, tx); - } - #[test] fn test_decode_create_goerli() { // test that an example create tx from goerli decodes properly @@ -1540,31 +1427,6 @@ mod tests { assert_eq!(tx_bytes, encoded); } - #[test] - fn test_decode_call() { - let request = Transaction::Eip2930(TxEip2930 { - chain_id: 1u64, - nonce: 0, - gas_price: 1, - gas_limit: 2, - to: TransactionKind::Call(Address::default()), - value: 3, - input: Bytes::from(vec![1, 2]), - access_list: Default::default(), - }); - - let signature = Signature { odd_y_parity: true, r: U256::default(), s: U256::default() }; - - let tx = TransactionSigned::from_transaction_and_signature(request, signature); - - let mut encoded = BytesMut::new(); - tx.encode(&mut encoded); - assert_eq!(encoded.len(), tx.length()); - - let decoded = TransactionSigned::decode(&mut &*encoded).unwrap(); - assert_eq!(decoded, tx); - } - #[test] fn decode_transaction_consumes_buffer() { let bytes = &mut &hex::decode("b87502f872041a8459682f008459682f0d8252089461815774383099e24810ab832a5b2a5425c154d58829a2241af62c000080c001a059e6b67f48fb32e7e570dfb11e042b5ad2e55e3ce3ce9cd989c7e06e07feeafda0016b83f4f980694ed2eee4d10667242b1f40dc406901b34125b008d334d47469").unwrap()[..]; @@ -1726,72 +1588,6 @@ mod tests { assert_eq!(tx.recover_signer(), Some(signer), "Recovering signer should pass."); } - #[test] - fn recover_signer_legacy() { - use crate::hex_literal::hex; - - let signer: Address = hex!("398137383b3d25c92898c656696e41950e47316b").into(); - let hash: H256 = - hex!("bb3a336e3f823ec18197f1e13ee875700f08f03e2cab75f0d0b118dabb44cba0").into(); - - let tx = Transaction::Legacy(TxLegacy { - chain_id: Some(1), - nonce: 0x18, - gas_price: 0xfa56ea00, - gas_limit: 119902, - to: TransactionKind::Call( hex!("06012c8cf97bead5deae237070f9587f8e7a266d").into()), - value: 0x1c6bf526340000u64.into(), - input: hex!("f7d8c88300000000000000000000000000000000000000000000000000000000000cee6100000000000000000000000000000000000000000000000000000000000ac3e1").into(), - }); - - let sig = Signature { - r: U256::from_be_bytes(hex!( - "2a378831cf81d99a3f06a18ae1b6ca366817ab4d88a70053c41d7a8f0368e031" - )), - s: U256::from_be_bytes(hex!( - "450d831a05b6e418724436c05c155e0a1b7b921015d0fbc2f667aed709ac4fb5" - )), - odd_y_parity: false, - }; - - let signed_tx = TransactionSigned::from_transaction_and_signature(tx, sig); - assert_eq!(signed_tx.hash(), hash, "Expected same hash"); - assert_eq!(signed_tx.recover_signer(), Some(signer), "Recovering signer should pass."); - } - - #[test] - fn recover_signer_eip1559() { - use crate::hex_literal::hex; - - let signer: Address = hex!("dd6b8b3dc6b7ad97db52f08a275ff4483e024cea").into(); - let hash: H256 = - hex!("0ec0b6a2df4d87424e5f6ad2a654e27aaeb7dac20ae9e8385cc09087ad532ee0").into(); - - let tx = Transaction::Eip1559( TxEip1559 { - chain_id: 1, - nonce: 0x42, - gas_limit: 44386, - to: TransactionKind::Call( hex!("6069a6c32cf691f5982febae4faf8a6f3ab2f0f6").into()), - value: 0, - input: hex!("a22cb4650000000000000000000000005eee75727d804a2b13038928d36f8b188945a57a0000000000000000000000000000000000000000000000000000000000000000").into(), - max_fee_per_gas: 0x4a817c800, - max_priority_fee_per_gas: 0x3b9aca00, - access_list: AccessList::default(), - }); - - let sig = Signature { - r: U256::from_str("0x840cfc572845f5786e702984c2a582528cad4b49b2a10b9db1be7fca90058565") - .unwrap(), - s: U256::from_str("0x25e7109ceb98168d95b09b18bbf6b685130e0562f233877d492b94eee0c5b6d1") - .unwrap(), - odd_y_parity: false, - }; - - let signed_tx = TransactionSigned::from_transaction_and_signature(tx, sig); - assert_eq!(signed_tx.hash(), hash, "Expected same hash"); - assert_eq!(signed_tx.recover_signer(), Some(signer), "Recovering signer should pass."); - } - #[test] fn test_envelop_encode() { // random tx: diff --git a/crates/primitives/src/transaction/optimism.rs b/crates/primitives/src/transaction/optimism.rs new file mode 100644 index 00000000000..546a6300b6b --- /dev/null +++ b/crates/primitives/src/transaction/optimism.rs @@ -0,0 +1,84 @@ +use crate::{Address, Bytes, TransactionKind, H256}; +use reth_codecs::{main_codec, Compact}; +use reth_rlp::{Encodable, EMPTY_STRING_CODE}; +use std::mem; + +/// EIP-2718 transaction type selector. +pub const DEPOSIT_TX_TYPE: u8 = 126; + +/// A versioned byte sequence to enable the protocol to upgrade the deposit transaction type without +/// changing the transaction type selector. +pub const DEPOSIT_VERSION: u8 = 0; + +/// Deposited transactions, also known as deposits are transactions which are initiated on L1, and +/// executed on L2. This document outlines a new transaction type for deposits. It also describes +/// how deposits are initiated on L1, along with the authorization and validation conditions on L2. +#[main_codec] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] +pub struct TxDeposit { + /// Hash that uniquely identifies the source of the deposit. + pub source_hash: H256, + /// The address of the sender account. + pub from: Address, + /// The address of the recipient account, or the null (zero-length) address if the deposited + /// transaction is a contract creation. + pub to: TransactionKind, + /// The ETH value to mint on L2. + pub mint: Option, + /// The ETH value to send to the recipient account. + pub value: u128, + /// The gas limit for the L2 transaction. + pub gas_limit: u64, + /// Field indicating if this transaction is exempt from the L2 gas limit. + pub is_system_transaction: bool, + /// Input has two uses depending if transaction is Create or Call (if `to` field is None or + /// Some). + pub input: Bytes, +} + +impl TxDeposit { + /// Calculates a heuristic for the in-memory size of the [TxDeposit] transaction. + #[inline] + pub fn size(&self) -> usize { + mem::size_of::() + // source_hash + mem::size_of::
() + // from + self.to.size() + // to + mem::size_of::>() + // mint + mem::size_of::() + // value + mem::size_of::() + // gas_limit + mem::size_of::() + // is_system_transaction + self.input.len() // input + } + + /// Outputs the length of the transaction's fields, without a RLP header or length of the + /// eip155 fields. + pub(crate) fn fields_len(&self) -> usize { + let mut len = 0; + len += self.source_hash.length(); + len += self.from.length(); + len += self.to.length(); + len += self.mint.map_or(1, |mint| mint.length()); + len += self.value.length(); + len += self.input.0.length(); + len += self.gas_limit.length(); + len += self.is_system_transaction.length(); + len + } + + /// Encodes only the transaction's fields into the desired buffer, without a RLP header. + /// + pub(crate) fn encode_fields(&self, out: &mut dyn bytes::BufMut) { + self.source_hash.encode(out); + self.from.encode(out); + self.to.encode(out); + if let Some(mint) = self.mint { + mint.encode(out); + } else { + out.put_u8(EMPTY_STRING_CODE); + } + self.value.encode(out); + self.input.encode(out); + self.gas_limit.encode(out); + self.is_system_transaction.encode(out); + } +} diff --git a/crates/primitives/src/transaction/signature.rs b/crates/primitives/src/transaction/signature.rs index 6977d8c2618..eff0c56b392 100644 --- a/crates/primitives/src/transaction/signature.rs +++ b/crates/primitives/src/transaction/signature.rs @@ -61,6 +61,11 @@ impl Signature { /// Output the `v` of the signature depends on chain_id #[inline] pub fn v(&self, chain_id: Option) -> u64 { + #[cfg(feature = "optimism")] + if self.r == U256::ZERO && self.s == U256::ZERO { + return 0 + } + if let Some(chain_id) = chain_id { // EIP-155: v = {0, 1} + CHAIN_ID * 2 + 35 self.odd_y_parity as u64 + chain_id * 2 + 35 diff --git a/crates/primitives/src/transaction/tx_type.rs b/crates/primitives/src/transaction/tx_type.rs index 428e06f4534..731eee03c6b 100644 --- a/crates/primitives/src/transaction/tx_type.rs +++ b/crates/primitives/src/transaction/tx_type.rs @@ -1,4 +1,5 @@ use crate::U8; +use bytes::Buf; use reth_codecs::{derive_arbitrary, Compact}; use serde::{Deserialize, Serialize}; @@ -12,7 +13,21 @@ pub const EIP2930_TX_TYPE_ID: u8 = 1; /// Identifier for [TxEip1559](crate::TxEip1559) transaction. pub const EIP1559_TX_TYPE_ID: u8 = 2; +/// Identifier for [TxEip4844](crate::TxEip4844) transaction. +#[allow(unused)] +pub(crate) const EIP4844_TX_TYPE_ID: u8 = 3; + +/// Identifier for [TxDeposit](crate::TxDeposit) transaction. +#[cfg(feature = "optimism")] +use crate::DEPOSIT_TX_TYPE; + /// Transaction Type +/// +/// Currently being used as 2-bit type when encoding it to [`Compact`] on +/// [`crate::TransactionSignedNoHash`]. Adding more transaction types will break the codec and +/// database format. +/// +/// Other required changes when adding a new type can be seen on [PR#3953](https://github.com/paradigmxyz/reth/pull/3953/files). #[derive_arbitrary(compact)] #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default, Serialize, Deserialize)] pub enum TxType { @@ -23,6 +38,11 @@ pub enum TxType { EIP2930 = 1_isize, /// Transaction with Priority fee EIP1559 = 2_isize, + /// Shard Blob Transactions - EIP-4844 + EIP4844 = 3_isize, + /// OP Deposit transaction. + #[cfg(feature = "optimism")] + DEPOSIT = DEPOSIT_TX_TYPE as isize, } impl From for u8 { @@ -31,6 +51,9 @@ impl From for u8 { TxType::Legacy => LEGACY_TX_TYPE_ID, TxType::EIP2930 => EIP2930_TX_TYPE_ID, TxType::EIP1559 => EIP1559_TX_TYPE_ID, + TxType::EIP4844 => EIP4844_TX_TYPE_ID, + #[cfg(feature = "optimism")] + TxType::DEPOSIT => DEPOSIT_TX_TYPE, } } } @@ -42,7 +65,10 @@ impl From for U8 { } impl Compact for TxType { - fn to_compact(self, _: &mut B) -> usize + // For backwards compatibility purposes, 2 bits are reserved for the transaction type in the + // `StructFlags`. In the case where the transaction type is at least 3, the full transaction + // type is encoded into the buffer as a single byte and a 3 is encoded into the flags. + fn to_compact(self, buf: &mut B) -> usize where B: bytes::BufMut + AsMut<[u8]>, { @@ -50,15 +76,24 @@ impl Compact for TxType { TxType::Legacy => 0, TxType::EIP2930 => 1, TxType::EIP1559 => 2, + TxType::EIP4844 => 3, + #[cfg(feature = "optimism")] + TxType::DEPOSIT => 126, } } - fn from_compact(buf: &[u8], identifier: usize) -> (Self, &[u8]) { + // For backwards compatibility purposesm only 2 bits of the type are encoded in the identifier + // parameter. In the case of a 3, the full transaction type is read from the buffer as a + // single byte. + fn from_compact(mut buf: &[u8], identifier: usize) -> (Self, &[u8]) { ( match identifier { 0 => TxType::Legacy, 1 => TxType::EIP2930, - _ => TxType::EIP1559, + 2 => TxType::EIP1559, + #[cfg(feature = "optimism")] + 126 => TxType::DEPOSIT, + _ => TxType::EIP4844, }, buf, ) diff --git a/crates/prune/Cargo.toml b/crates/prune/Cargo.toml index 13d5ae4f916..99a4901cb13 100644 --- a/crates/prune/Cargo.toml +++ b/crates/prune/Cargo.toml @@ -20,8 +20,8 @@ reth-interfaces.workspace = true # misc tracing.workspace = true thiserror.workspace = true -itertools = "0.10" -rayon = "1.6.0" +itertools.workspace = true +rayon.workspace = true [dev-dependencies] # reth diff --git a/crates/prune/src/error.rs b/crates/prune/src/error.rs index fdc0af4484a..1a31a039422 100644 --- a/crates/prune/src/error.rs +++ b/crates/prune/src/error.rs @@ -4,6 +4,9 @@ use thiserror::Error; #[derive(Error, Debug)] pub enum PrunerError { + #[error(transparent)] + PrunePart(#[from] reth_primitives::PrunePartError), + #[error("Inconsistent data: {0}")] InconsistentData(&'static str), diff --git a/crates/prune/src/pruner.rs b/crates/prune/src/pruner.rs index de020dcd272..3dae8bcb04f 100644 --- a/crates/prune/src/pruner.rs +++ b/crates/prune/src/pruner.rs @@ -72,13 +72,13 @@ impl Pruner { let provider = self.provider_factory.provider_rw()?; if let Some((to_block, prune_mode)) = - self.modes.prune_target_block_receipts(tip_block_number) + self.modes.prune_target_block_receipts(tip_block_number)? { self.prune_receipts(&provider, to_block, prune_mode)?; } if let Some((to_block, prune_mode)) = - self.modes.prune_target_block_transaction_lookup(tip_block_number) + self.modes.prune_target_block_transaction_lookup(tip_block_number)? { self.prune_transaction_lookup(&provider, to_block, prune_mode)?; } @@ -160,14 +160,18 @@ impl Pruner { return Ok(()) } }; + let total = range.clone().count(); + let mut processed = 0; provider.prune_table_in_batches::( range, self.batch_sizes.receipts, - |receipts| { + |entries| { + processed += entries; trace!( target: "pruner", - %receipts, + %entries, + progress = format!("{:.1}%", 100.0 * processed as f64 / total as f64), "Pruned receipts" ); }, @@ -201,6 +205,8 @@ impl Pruner { } }; let last_tx_num = *range.end(); + let total = range.clone().count(); + let mut processed = 0; for i in range.step_by(self.batch_sizes.transaction_lookup) { // The `min` ensures that the transaction range doesn't exceed the last transaction @@ -223,19 +229,16 @@ impl Pruner { } // Pre-sort hashes to prune them in order - hashes.sort(); - - provider.prune_table_in_batches::( - hashes, - self.batch_sizes.transaction_lookup, - |entries| { - trace!( - target: "pruner", - %entries, - "Pruned transaction lookup" - ); - }, - )?; + hashes.sort_unstable(); + + let entries = provider.prune_table::(hashes)?; + processed += entries; + trace!( + target: "pruner", + %entries, + progress = format!("{:.1}%", 100.0 * processed as f64 / total as f64), + "Pruned transaction lookup" + ); } provider.save_prune_checkpoint( diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index 037a717d41a..baad89d513d 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -18,11 +18,15 @@ reth-revm-inspectors = { path = "./revm-inspectors" } reth-consensus-common = { path = "../consensus/common" } # revm -revm.workspace = true +revm = { workspace = true } # common -tracing.workspace = true +tracing = { workspace = true } [dev-dependencies] -reth-rlp.workspace = true +reth-rlp = { workspace = true } once_cell = "1.17.0" + +[features] +optimism = ["reth-primitives/optimism", "reth-revm-primitives/optimism"] + diff --git a/crates/revm/revm-inspectors/src/tracing/builder/geth.rs b/crates/revm/revm-inspectors/src/tracing/builder/geth.rs index a3eaa3233fd..2f02b32cfa6 100644 --- a/crates/revm/revm-inspectors/src/tracing/builder/geth.rs +++ b/crates/revm/revm-inspectors/src/tracing/builder/geth.rs @@ -4,8 +4,12 @@ use crate::tracing::{ types::{CallTraceNode, CallTraceStepStackItem}, TracingInspectorConfig, }; -use reth_primitives::{Address, Bytes, H256}; -use reth_rpc_types::trace::geth::*; +use reth_primitives::{Address, Bytes, H256, U256}; +use reth_rpc_types::trace::geth::{ + AccountState, CallConfig, CallFrame, DefaultFrame, DiffMode, GethDefaultTracingOptions, + PreStateConfig, PreStateFrame, PreStateMode, StructLog, +}; +use revm::{db::DatabaseRef, primitives::ResultAndState}; use std::collections::{BTreeMap, HashMap, VecDeque}; /// A type for creating geth style traces @@ -147,4 +151,75 @@ impl GethTraceBuilder { } } } + + /// Returns the accounts necessary for transaction execution. + /// + /// The prestate mode returns the accounts necessary to execute a given transaction. + /// diff_mode returns the differences between the transaction's pre and post-state. + /// + /// * `state` - The state post-transaction execution. + /// * `diff_mode` - if prestate is in diff or prestate mode. + /// * `db` - The database to fetch state pre-transaction execution. + pub fn geth_prestate_traces( + &self, + ResultAndState { state, .. }: &ResultAndState, + prestate_config: PreStateConfig, + db: DB, + ) -> Result + where + DB: DatabaseRef, + { + let account_diffs: Vec<_> = + state.into_iter().map(|(addr, acc)| (*addr, &acc.info)).collect(); + + if prestate_config.is_diff_mode() { + let mut prestate = PreStateMode::default(); + for (addr, _) in account_diffs { + let db_acc = db.basic(addr)?.unwrap_or_default(); + prestate.0.insert( + addr, + AccountState { + balance: Some(db_acc.balance), + nonce: Some(U256::from(db_acc.nonce)), + code: db_acc.code.as_ref().map(|code| Bytes::from(code.original_bytes())), + storage: None, + }, + ); + } + self.update_storage_from_trace(&mut prestate.0, false); + Ok(PreStateFrame::Default(prestate)) + } else { + let mut state_diff = DiffMode::default(); + for (addr, changed_acc) in account_diffs { + let db_acc = db.basic(addr)?.unwrap_or_default(); + let pre_state = AccountState { + balance: Some(db_acc.balance), + nonce: Some(U256::from(db_acc.nonce)), + code: db_acc.code.as_ref().map(|code| Bytes::from(code.original_bytes())), + storage: None, + }; + let post_state = AccountState { + balance: Some(changed_acc.balance), + nonce: Some(U256::from(changed_acc.nonce)), + code: changed_acc.code.as_ref().map(|code| Bytes::from(code.original_bytes())), + storage: None, + }; + state_diff.pre.insert(addr, pre_state); + state_diff.post.insert(addr, post_state); + } + self.update_storage_from_trace(&mut state_diff.pre, false); + self.update_storage_from_trace(&mut state_diff.post, true); + Ok(PreStateFrame::Diff(state_diff)) + } + } + + fn update_storage_from_trace( + &self, + account_states: &mut BTreeMap, + post_value: bool, + ) { + for node in self.nodes.iter() { + node.geth_update_account_storage(account_states, post_value); + } + } } diff --git a/crates/revm/revm-inspectors/src/tracing/builder/parity.rs b/crates/revm/revm-inspectors/src/tracing/builder/parity.rs index cce86c43445..7c3720621bc 100644 --- a/crates/revm/revm-inspectors/src/tracing/builder/parity.rs +++ b/crates/revm/revm-inspectors/src/tracing/builder/parity.rs @@ -330,7 +330,7 @@ impl ParityTraceBuilder { let maybe_execution = Some(VmExecutedOperation { used: step.gas_cost, - push: step.new_stack.map(|new_stack| new_stack.into()), + push: step.new_stack.into_iter().map(|new_stack| new_stack.into()).collect(), mem: maybe_memory, store: maybe_storage, }); diff --git a/crates/revm/revm-inspectors/src/tracing/mod.rs b/crates/revm/revm-inspectors/src/tracing/mod.rs index fe6c5b8609d..de8a3b3b24a 100644 --- a/crates/revm/revm-inspectors/src/tracing/mod.rs +++ b/crates/revm/revm-inspectors/src/tracing/mod.rs @@ -395,7 +395,6 @@ where if self.config.record_steps { self.gas_inspector.step_end(interp, data, is_static, eval); self.fill_step_on_step_end(interp, data, eval); - return eval } InstructionResult::Continue } diff --git a/crates/revm/revm-inspectors/src/tracing/types.rs b/crates/revm/revm-inspectors/src/tracing/types.rs index 22bebdbb247..83c720dd281 100644 --- a/crates/revm/revm-inspectors/src/tracing/types.rs +++ b/crates/revm/revm-inspectors/src/tracing/types.rs @@ -3,7 +3,7 @@ use crate::tracing::{config::TraceStyle, utils::convert_memory}; use reth_primitives::{abi::decode_revert_reason, bytes::Bytes, Address, H256, U256}; use reth_rpc_types::trace::{ - geth::{CallFrame, CallLogFrame, GethDefaultTracingOptions, StructLog}, + geth::{AccountState, CallFrame, CallLogFrame, GethDefaultTracingOptions, StructLog}, parity::{ Action, ActionType, CallAction, CallOutput, CallType, ChangedType, CreateAction, CreateOutput, Delta, SelfdestructAction, StateDiff, TraceOutput, TransactionTrace, @@ -13,7 +13,7 @@ use revm::interpreter::{ opcode, CallContext, CallScheme, CreateScheme, InstructionResult, Memory, OpCode, Stack, }; use serde::{Deserialize, Serialize}; -use std::collections::{btree_map::Entry, VecDeque}; +use std::collections::{btree_map::Entry, BTreeMap, VecDeque}; /// A unified representation of a call #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] @@ -267,9 +267,12 @@ impl CallTraceNode { opcode::CALL | opcode::STATICCALL | opcode::CALLCODE => { - let call_id = self.children[child_id]; - item.call_child_id = Some(call_id); - child_id += 1; + // The opcode of this step is a call but it's possible that this step resulted + // in a revert or out of gas error in which case there's no actual child call executed and recorded: + if let Some(call_id) = self.children.get(child_id).copied() { + item.call_child_id = Some(call_id); + child_id += 1; + } } _ => {} } @@ -443,6 +446,34 @@ impl CallTraceNode { call_frame } + + /// Adds storage in-place to account state for all accounts that were touched in the trace + /// [CallTrace] execution. + /// + /// * `account_states` - the account map updated in place. + /// * `post_value` - if true, it adds storage values after trace transaction execution, if + /// false, returns the storage values before trace execution. + pub(crate) fn geth_update_account_storage( + &self, + account_states: &mut BTreeMap, + post_value: bool, + ) { + let addr = self.trace.address; + let acc_state = account_states.entry(addr).or_insert_with(AccountState::default); + for change in self.trace.steps.iter().filter_map(|s| s.storage_change) { + let StorageChange { key, value, had_value } = change; + let storage_map = acc_state.storage.get_or_insert_with(BTreeMap::new); + let value_to_insert = if post_value { + H256::from(value) + } else { + match had_value { + Some(had_value) => H256::from(had_value), + None => continue, + } + }; + storage_map.insert(key.into(), value_to_insert); + } + } } pub(crate) struct CallTraceStepStackItem<'a> { @@ -504,7 +535,9 @@ pub(crate) struct CallTraceStep { pub(crate) gas_cost: u64, /// Change of the contract state after step execution (effect of the SLOAD/SSTORE instructions) pub(crate) storage_change: Option, - /// Final status of the call + /// Final status of the step + /// + /// This is set after the step was executed. pub(crate) status: InstructionResult, } diff --git a/crates/revm/revm-primitives/Cargo.toml b/crates/revm/revm-primitives/Cargo.toml index a17ca3cfb42..8af184b821c 100644 --- a/crates/revm/revm-primitives/Cargo.toml +++ b/crates/revm/revm-primitives/Cargo.toml @@ -13,3 +13,6 @@ description = "core reth specific revm utilities" reth-primitives.workspace = true revm.workspace = true + +[features] +optimism = ["reth-primitives/optimism"] diff --git a/crates/revm/revm-primitives/src/env.rs b/crates/revm/revm-primitives/src/env.rs index 4a3ee323928..b1d15f2529e 100644 --- a/crates/revm/revm-primitives/src/env.rs +++ b/crates/revm/revm-primitives/src/env.rs @@ -5,6 +5,9 @@ use reth_primitives::{ }; use revm::primitives::{AnalysisKind, BlockEnv, CfgEnv, SpecId, TransactTo, TxEnv}; +#[cfg(feature = "optimism")] +use reth_primitives::TxDeposit; + /// Convenience function to call both [fill_cfg_env] and [fill_block_env] pub fn fill_cfg_and_block_env( cfg: &mut CfgEnv, @@ -212,5 +215,19 @@ where }) .collect(); } + #[cfg(feature = "optimism")] + Transaction::Deposit(TxDeposit { to, value, gas_limit, input, .. }) => { + tx_env.gas_limit = *gas_limit; + tx_env.gas_price = U256::ZERO; + tx_env.gas_priority_fee = None; + match to { + TransactionKind::Call(to) => tx_env.transact_to = TransactTo::Call(*to), + TransactionKind::Create => tx_env.transact_to = TransactTo::create(), + } + tx_env.value = U256::from(*value); + tx_env.data = input.0.clone(); + tx_env.chain_id = None; + tx_env.nonce = None; + } } } diff --git a/crates/revm/src/executor.rs b/crates/revm/src/executor.rs index 5ce714521fb..d76eb644295 100644 --- a/crates/revm/src/executor.rs +++ b/crates/revm/src/executor.rs @@ -26,6 +26,9 @@ use std::{ sync::Arc, }; +#[cfg(feature = "optimism")] +use crate::optimism; + /// Main block executor pub struct Executor where @@ -227,6 +230,9 @@ where self.init_env(&block.header, total_difficulty); + #[cfg(feature = "optimism")] + let mut l1_block_info = optimism::L1BlockInfo::try_from(block)?; + let mut cumulative_gas_used = 0; let mut post_state = PostState::with_tx_capacity(block.number, block.body.len()); for (transaction, sender) in block.body.iter().zip(senders) { @@ -240,33 +246,170 @@ where } .into()) } - // Execute transaction. - let ResultAndState { result, state } = self.transact(transaction, sender)?; - // commit changes - self.commit_changes( - block.number, - state, - self.chain_spec.fork(Hardfork::SpuriousDragon).active_at_block(block.number), - &mut post_state, - ); + #[cfg(feature = "optimism")] + { + let db = self.db(); + let l1_cost = l1_block_info.calculate_tx_l1_cost(transaction); + + let sender_account = + db.load_account(sender).map_err(|_| BlockExecutionError::ProviderError)?; + let old_sender_info = to_reth_acc(&sender_account.info); + + if let Some(m) = transaction.mint() { + // Add balance to the caler account equal to the minted amount. + // Note: This is unconditional, and will not be reverted if the tx fails + // (unless the block can't be built at all due to gas limit constraints) + sender_account.info.balance += U256::from(m); + } - // append gas used - cumulative_gas_used += result.gas_used(); + // Check if the sender balance can cover the L1 cost. + // Deposits pay for their gas directly on L1 so they are exempt from the L2 tx fee. + if !transaction.is_deposit() { + if sender_account.info.balance.cmp(&l1_cost) == std::cmp::Ordering::Less { + return Err(BlockExecutionError::InsufficientFundsForL1Cost { + have: sender_account.info.balance.to::(), + want: l1_cost.to::(), + }) + } - // Push transaction changeset and calculate header bloom filter for receipt. - post_state.add_receipt( - block.number, - Receipt { - tx_type: transaction.tx_type(), - // Success flag was added in `EIP-658: Embedding transaction status code in - // receipts`. - success: result.is_success(), - cumulative_gas_used, - // convert to reth log - logs: result.into_logs().into_iter().map(into_reth_log).collect(), - }, - ); + // Safely take l1_cost from sender (the rest will be deducted by the + // internal EVM execution and included in result.gas_used()) + // TODO: need to handle calls with `disable_balance_check` flag set? + sender_account.info.balance -= l1_cost; + } + + let new_sender_info = to_reth_acc(&sender_account.info); + post_state.change_account(block.number, sender, old_sender_info, new_sender_info); + + // Execute transaction. + let ResultAndState { result, state } = self.transact(transaction, sender)?; + + if transaction.is_deposit() && !result.is_success() { + // If the Deposited transaction failed, the deposit must still be included. + // In this case, we need to increment the sender nonce and disregard the + // state changes. The transaction is also recorded as using all gas. + let db = self.db(); + let sender_account = + db.load_account(sender).map_err(|_| BlockExecutionError::ProviderError)?; + let old_sender_info = to_reth_acc(&sender_account.info); + sender_account.info.nonce += 1; + let new_sender_info = to_reth_acc(&sender_account.info); + + post_state.change_account( + block.number, + sender, + old_sender_info, + new_sender_info, + ); + if !transaction.is_system_transaction() { + cumulative_gas_used += transaction.gas_limit(); + } + + post_state.add_receipt( + block.number, + Receipt { + tx_type: transaction.tx_type(), + success: false, + cumulative_gas_used, + logs: vec![], + deposit_nonce: Some(transaction.nonce()), + }, + ); + continue + } + + // commit changes + self.commit_changes( + block.number, + state, + self.chain_spec.fork(Hardfork::SpuriousDragon).active_at_block(block.number), + &mut post_state, + ); + + if !transaction.is_system_transaction() { + // After Regolith, deposits are reported as using the actual gas used instead of + // all the gas. System transactions are not reported as using any gas. + cumulative_gas_used += result.gas_used() + } + + // Route the l1 cost and base fee to the appropriate optimism vaults + self.increment_account_balance( + block.number, + optimism::l1_cost_recipient(), + l1_cost, + &mut post_state, + )?; + self.increment_account_balance( + block.number, + optimism::base_fee_recipient(), + U256::from( + block + .base_fee_per_gas + .unwrap_or_default() + .saturating_mul(result.gas_used()), + ), + &mut post_state, + )?; + + // cast revm logs to reth logs + let logs = result.logs().into_iter().map(into_reth_log).collect(); + + // Push transaction changeset and calculate header bloom filter for receipt. + post_state.add_receipt( + block.number, + Receipt { + tx_type: transaction.tx_type(), + // Success flag was added in `EIP-658: Embedding transaction status code in + // receipts`. + success: result.is_success(), + cumulative_gas_used, + logs, + deposit_nonce: Some(transaction.nonce()), + }, + ); + } + + #[cfg(not(feature = "optimism"))] + { + // The sum of the transaction’s gas limit, Tg, and the gas utilised in this block + // prior, must be no greater than the block’s gasLimit. + let block_available_gas = block.header.gas_limit - cumulative_gas_used; + if transaction.gas_limit() > block_available_gas { + return Err(BlockValidationError::TransactionGasLimitMoreThanAvailableBlockGas { + transaction_gas_limit: transaction.gas_limit(), + block_available_gas, + } + .into()) + } + // Execute transaction. + let ResultAndState { result, state } = self.transact(transaction, sender)?; + + // commit changes + self.commit_changes( + block.number, + state, + self.chain_spec.fork(Hardfork::SpuriousDragon).active_at_block(block.number), + &mut post_state, + ); + + // append gas used + cumulative_gas_used += result.gas_used(); + + // Push transaction changeset and calculate header bloom filter for receipt. + post_state.add_receipt( + block.number, + Receipt { + tx_type: transaction.tx_type(), + // Success flag was added in `EIP-658: Embedding transaction status code in + // receipts`. + success: result.is_success(), + cumulative_gas_used, + // convert to reth log + logs: result.into_logs().into_iter().map(into_reth_log).collect(), + }, + ); + } } Ok((post_state, cumulative_gas_used)) diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs index 6a99b020381..612a0f6482c 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -33,3 +33,7 @@ pub use revm; /// Etereum DAO hardfork state change data. pub mod eth_dao_fork; + +/// Optimism-specific utilities for the executor +#[cfg(feature = "optimism")] +pub mod optimism; diff --git a/crates/revm/src/optimism.rs b/crates/revm/src/optimism.rs new file mode 100644 index 00000000000..deeb9dcc684 --- /dev/null +++ b/crates/revm/src/optimism.rs @@ -0,0 +1,113 @@ +use std::str::FromStr; + +use reth_interfaces::executor; +use reth_primitives::{Address, Block, TransactionKind, TransactionSigned, U256}; + +const L1_FEE_RECIPIENT: &str = "0x420000000000000000000000000000000000001A"; +const BASE_FEE_RECIPIENT: &str = "0x4200000000000000000000000000000000000019"; +const L1_BLOCK_CONTRACT: &str = "0x4200000000000000000000000000000000000015"; + +const ZERO_BYTE_COST: u64 = 4; +const NON_ZERO_BYTE_COST: u64 = 16; + +/// L1 block info +/// +/// We can extract L1 epoch data from each L2 block, by looking at the `setL1BlockValues` +/// transaction data. This data is then used to calculate the L1 cost of a transaction. +/// +/// Here is the format of the `setL1BlockValues` transaction data: +/// +/// setL1BlockValues(uint64 _number, uint64 _timestamp, uint256 _basefee, bytes32 _hash, +/// uint64 _sequenceNumber, bytes32 _batcherHash, uint256 _l1FeeOverhead, uint256 _l1FeeScalar) +/// +/// For now, we only care about the fields necessary for L1 cost calculation. +pub struct L1BlockInfo { + l1_base_fee: U256, + l1_fee_overhead: U256, + l1_fee_scalar: U256, +} + +impl TryFrom<&Block> for L1BlockInfo { + type Error = executor::BlockExecutionError; + + fn try_from(block: &Block) -> Result { + let l1_block_contract = Address::from_str(L1_BLOCK_CONTRACT).unwrap(); + + let l1_info_tx_data = block + .body + .iter() + .find(|tx| matches!(tx.kind(), TransactionKind::Call(to) if to == &l1_block_contract)) + .ok_or(executor::BlockExecutionError::L1BlockInfoError { + message: "could not find l1 block info tx in the L2 block".to_string(), + }) + .and_then(|tx| { + tx.input().get(4..).ok_or(executor::BlockExecutionError::L1BlockInfoError { + message: "could not get l1 block info tx calldata bytes".to_string(), + }) + })?; + + // The setL1BlockValues tx calldata must be exactly 184 bytes long, considering that + // we already removed the first 4 bytes (the function selector). Detailed breakdown: + // 8 bytes for the block number + // + 8 bytes for the block timestamp + // + 32 bytes for the base fee + // + 32 bytes for the block hash + // + 8 bytes for the block sequence number + // + 32 bytes for the batcher hash + // + 32 bytes for the fee overhead + // + 32 bytes for the fee scalar + if l1_info_tx_data.len() != 184 { + return Err(executor::BlockExecutionError::L1BlockInfoError { + message: "unexpected l1 block info tx calldata length found".to_string(), + }) + } + + let l1_base_fee = U256::try_from_le_slice(&l1_info_tx_data[16..48]).ok_or( + executor::BlockExecutionError::L1BlockInfoError { + message: "could not convert l1 base fee".to_string(), + }, + )?; + let l1_fee_overhead = U256::try_from_le_slice(&l1_info_tx_data[120..152]).ok_or( + executor::BlockExecutionError::L1BlockInfoError { + message: "could not convert l1 fee overhead".to_string(), + }, + )?; + let l1_fee_scalar = U256::try_from_le_slice(&l1_info_tx_data[152..184]).ok_or( + executor::BlockExecutionError::L1BlockInfoError { + message: "could not convert l1 fee scalar".to_string(), + }, + )?; + + Ok(Self { l1_base_fee, l1_fee_overhead, l1_fee_scalar }) + } +} + +impl L1BlockInfo { + /// Calculate the gas cost of a transaction based on L1 block data posted on L2 + pub fn calculate_tx_l1_cost(&mut self, tx: &TransactionSigned) -> U256 { + let rollup_data_gas_cost = U256::from(tx.input().iter().fold(0, |acc, byte| { + acc + if *byte == 0x00 { ZERO_BYTE_COST } else { NON_ZERO_BYTE_COST } + })); + + if tx.is_deposit() || rollup_data_gas_cost == U256::ZERO { + return U256::ZERO + } + + rollup_data_gas_cost + .saturating_add(self.l1_fee_overhead) + .saturating_mul(self.l1_base_fee) + .saturating_mul(self.l1_fee_scalar) + .checked_div(U256::from(1_000_000)) + .unwrap_or_default() + } +} + +/// Get the base fee recipient address +pub fn base_fee_recipient() -> Address { + Address::from_str(BASE_FEE_RECIPIENT).unwrap() +} + +/// Get the L1 cost recipient address +pub fn l1_cost_recipient() -> Address { + Address::from_str(L1_FEE_RECIPIENT).unwrap() +} diff --git a/crates/rpc/ipc/Cargo.toml b/crates/rpc/ipc/Cargo.toml index e78c8f5dee3..0dc0bebee6d 100644 --- a/crates/rpc/ipc/Cargo.toml +++ b/crates/rpc/ipc/Cargo.toml @@ -23,7 +23,7 @@ pin-project.workspace = true tower = "0.4" # misc -jsonrpsee = { version = "0.18", features = ["server", "client"] } +jsonrpsee = { workspace = true, features = ["server", "client"] } serde_json.workspace = true tracing.workspace = true bytes.workspace = true diff --git a/crates/rpc/ipc/src/server/ipc.rs b/crates/rpc/ipc/src/server/ipc.rs index c03f3ca385a..ff04d2c5e71 100644 --- a/crates/rpc/ipc/src/server/ipc.rs +++ b/crates/rpc/ipc/src/server/ipc.rs @@ -6,7 +6,7 @@ use jsonrpsee::{ tracing::{rx_log_from_json, tx_log_from_str}, JsonRawValue, }, - helpers::batch_response_error, + helpers::{batch_response_error, MethodResponseResult}, server::{ logger, logger::{Logger, TransportProtocol}, @@ -219,14 +219,20 @@ pub(crate) async fn execute_call( }; tx_log_from_str(&response.as_response().result, max_log_length); - logger.on_result(name, response.as_response().success, request_start, TransportProtocol::Http); + logger.on_result( + name, + response.as_response().success_or_error, + request_start, + TransportProtocol::Http, + ); response } #[instrument(name = "notification", fields(method = notif.method.as_ref()), skip(notif, max_log_length), level = "TRACE")] fn execute_notification(notif: Notif<'_>, max_log_length: u32) -> MethodResponse { rx_log_from_json(¬if, max_log_length); - let response = MethodResponse { result: String::new(), success: true }; + let response = + MethodResponse { result: String::new(), success_or_error: MethodResponseResult::Success }; tx_log_from_str(&response.result, max_log_length); response } diff --git a/crates/rpc/rpc-api/Cargo.toml b/crates/rpc/rpc-api/Cargo.toml index 7b220ba1123..53a7676b112 100644 --- a/crates/rpc/rpc-api/Cargo.toml +++ b/crates/rpc/rpc-api/Cargo.toml @@ -16,7 +16,7 @@ reth-primitives.workspace = true reth-rpc-types.workspace = true # misc -jsonrpsee = { version = "0.18", features = ["server", "macros"] } +jsonrpsee = { workspace = true, features = ["server", "macros"] } serde_json.workspace = true [features] diff --git a/crates/rpc/rpc-api/src/engine.rs b/crates/rpc/rpc-api/src/engine.rs index 756cb5473c7..9905957e91d 100644 --- a/crates/rpc/rpc-api/src/engine.rs +++ b/crates/rpc/rpc-api/src/engine.rs @@ -21,6 +21,17 @@ pub trait EngineApi { #[method(name = "newPayloadV2")] async fn new_payload_v2(&self, payload: ExecutionPayload) -> RpcResult; + /// Post Cancun payload handler + /// + /// See also + #[method(name = "newPayloadV3")] + async fn new_payload_v3( + &self, + payload: ExecutionPayload, + versioned_hashes: Vec, + parent_beacon_block_root: H256, + ) -> RpcResult; + /// See also /// /// Caution: This should not accept the `withdrawals` field @@ -39,6 +50,16 @@ pub trait EngineApi { payload_attributes: Option, ) -> RpcResult; + /// Same as `forkchoiceUpdatedV2` but supports additional [PayloadAttributes] field. + /// + /// See also + #[method(name = "forkchoiceUpdatedV3")] + async fn fork_choice_updated_v3( + &self, + fork_choice_state: ForkchoiceState, + payload_attributes: Option, + ) -> RpcResult; + /// See also /// /// Returns the most recent version of the payload that is available in the corresponding @@ -59,6 +80,16 @@ pub trait EngineApi { #[method(name = "getPayloadV2")] async fn get_payload_v2(&self, payload_id: PayloadId) -> RpcResult; + /// Post Cancun payload handler which also returns a blobs bundle. + /// + /// See also + /// + /// Returns the most recent version of the payload that is available in the corresponding + /// 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; + /// See also #[method(name = "getPayloadBodiesByHashV1")] async fn get_payload_bodies_by_hash_v1( @@ -86,6 +117,12 @@ pub trait EngineApi { ) -> RpcResult; /// See also + /// + /// Note: This method will be deprecated after the cancun hardfork: + /// + /// > Consensus and execution layer clients MAY remove support of this method after Cancun. If + /// > no longer supported, this method MUST be removed from the engine_exchangeCapabilities + /// > request or response list depending on whether it is consensus or execution layer client. #[method(name = "exchangeTransitionConfigurationV1")] async fn exchange_transition_configuration( &self, diff --git a/crates/rpc/rpc-builder/Cargo.toml b/crates/rpc/rpc-builder/Cargo.toml index aeb1d300d23..e7f7cf826d5 100644 --- a/crates/rpc/rpc-builder/Cargo.toml +++ b/crates/rpc/rpc-builder/Cargo.toml @@ -24,7 +24,7 @@ reth-transaction-pool.workspace = true reth-metrics = { workspace = true, features = ["common"] } # rpc/net -jsonrpsee = { version = "0.18", features = ["server"] } +jsonrpsee = { workspace = true, features = ["server"] } tower-http = { version = "0.4", features = ["full"] } tower = { version = "0.4", features = ["full"] } hyper = "0.14" @@ -34,9 +34,6 @@ strum = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] } thiserror.workspace = true tracing.workspace = true -rayon.workspace = true -pin-project.workspace = true -tokio = { workspace = true, features = ["sync"] } [dev-dependencies] reth-tracing = { path = "../../tracing" } diff --git a/crates/rpc/rpc-builder/src/auth.rs b/crates/rpc/rpc-builder/src/auth.rs index 758713bbdeb..95e64b8fbfe 100644 --- a/crates/rpc/rpc-builder/src/auth.rs +++ b/crates/rpc/rpc-builder/src/auth.rs @@ -17,7 +17,7 @@ use reth_provider::{ use reth_rpc::{ eth::{cache::EthStateCache, gas_oracle::GasPriceOracle}, AuthLayer, Claims, EngineEthApi, EthApi, EthFilter, EthSubscriptionIdProvider, - JwtAuthValidator, JwtSecret, + JwtAuthValidator, JwtSecret, TracingCallPool, }; use reth_rpc_api::{servers::*, EngineApiServer}; use reth_tasks::TaskSpawner; @@ -64,6 +64,7 @@ where gas_oracle, EthConfig::default().rpc_gas_cap, Box::new(executor.clone()), + TracingCallPool::build().expect("failed to build tracing pool"), ); let eth_filter = EthFilter::new( provider, @@ -114,7 +115,7 @@ where let local_addr = server.local_addr()?; - let handle = server.start(module)?; + let handle = server.start(module); Ok(AuthServerHandle { handle, local_addr, secret }) } @@ -153,7 +154,7 @@ impl AuthServerConfig { let local_addr = server.local_addr()?; - let handle = server.start(module.inner)?; + let handle = server.start(module.inner); Ok(AuthServerHandle { handle, local_addr, secret }) } } diff --git a/crates/rpc/rpc-builder/src/eth.rs b/crates/rpc/rpc-builder/src/eth.rs index 34ec4989c62..b372d3be77e 100644 --- a/crates/rpc/rpc-builder/src/eth.rs +++ b/crates/rpc/rpc-builder/src/eth.rs @@ -4,7 +4,7 @@ use reth_rpc::{ gas_oracle::GasPriceOracleConfig, RPC_DEFAULT_GAS_CAP, }, - EthApi, EthFilter, EthPubSub, + EthApi, EthFilter, EthPubSub, TracingCallPool, }; use serde::{Deserialize, Serialize}; @@ -25,6 +25,8 @@ pub struct EthHandlers { pub filter: EthFilter, /// Handler for subscriptions only available for transports that support it (ws, ipc) pub pubsub: EthPubSub, + /// The configured tracing call pool + pub tracing_call_pool: TracingCallPool, } /// Additional config values for the eth namespace diff --git a/crates/rpc/rpc-builder/src/lib.rs b/crates/rpc/rpc-builder/src/lib.rs index 4e86181b940..c66636babb0 100644 --- a/crates/rpc/rpc-builder/src/lib.rs +++ b/crates/rpc/rpc-builder/src/lib.rs @@ -89,7 +89,7 @@ //! let builder = RpcModuleBuilder::new(provider, pool, network, TokioTaskExecutor::default(), events); //! //! // configure the server modules -//! let (modules, auth_module) = builder.build_with_auth_server(transports, engine_api); +//! let (modules, auth_module, _registry) = builder.build_with_auth_server(transports, engine_api); //! //! // start the servers //! let auth_config = AuthServerConfig::builder(JwtSecret::random()).build(); @@ -122,7 +122,8 @@ use reth_rpc::{ gas_oracle::GasPriceOracle, }, AdminApi, DebugApi, EngineEthApi, EthApi, EthFilter, EthPubSub, EthSubscriptionIdProvider, - NetApi, OtterscanApi, RPCApi, RethApi, TraceApi, TracingCallGuard, TxPoolApi, Web3Api, + NetApi, OtterscanApi, RPCApi, RethApi, TraceApi, TracingCallGuard, TracingCallPool, TxPoolApi, + Web3Api, }; use reth_rpc_api::{servers::*, EngineApiServer}; use reth_tasks::{TaskSpawner, TokioTaskExecutor}; @@ -154,9 +155,6 @@ mod eth; /// Common RPC constants. pub mod constants; -/// Additional support for tracing related rpc calls -pub mod tracing_pool; - // Rpc server metrics mod metrics; @@ -345,7 +343,11 @@ where self, module_config: TransportRpcModuleConfig, engine: EngineApi, - ) -> (TransportRpcModules<()>, AuthRpcModule) + ) -> ( + TransportRpcModules<()>, + AuthRpcModule, + RethModuleRegistry, + ) where EngineApi: EngineApiServer, { @@ -371,7 +373,7 @@ where let auth_module = registry.create_auth_module(engine); - (modules, auth_module) + (modules, auth_module, registry) } /// Configures all [RpcModule]s specific to the given [TransportRpcModuleConfig] which can be @@ -816,15 +818,9 @@ where let eth = self.eth_handlers(); self.modules.insert( RethRpcModule::Trace, - TraceApi::new( - self.provider.clone(), - eth.api.clone(), - eth.cache, - Box::new(self.executor.clone()), - self.tracing_call_guard.clone(), - ) - .into_rpc() - .into(), + TraceApi::new(self.provider.clone(), eth.api.clone(), self.tracing_call_guard.clone()) + .into_rpc() + .into(), ); self } @@ -895,8 +891,13 @@ where &mut self, namespaces: impl Iterator, ) -> Vec { - let EthHandlers { api: eth_api, cache: eth_cache, filter: eth_filter, pubsub: eth_pubsub } = - self.with_eth(|eth| eth.clone()); + let EthHandlers { + api: eth_api, + filter: eth_filter, + pubsub: eth_pubsub, + cache: _, + tracing_call_pool: _, + } = self.with_eth(|eth| eth.clone()); // Create a copy, so we can list out all the methods for rpc_ api let namespaces: Vec<_> = namespaces.collect(); @@ -933,8 +934,6 @@ where RethRpcModule::Trace => TraceApi::new( self.provider.clone(), eth_api.clone(), - eth_cache.clone(), - Box::new(self.executor.clone()), self.tracing_call_guard.clone(), ) .into_rpc() @@ -997,6 +996,7 @@ where ); let executor = Box::new(self.executor.clone()); + let tracing_call_pool = TracingCallPool::build().expect("failed to build tracing pool"); let api = EthApi::with_spawner( self.provider.clone(), self.pool.clone(), @@ -1005,6 +1005,7 @@ where gas_oracle, self.config.eth.rpc_gas_cap, executor.clone(), + tracing_call_pool.clone(), ); let filter = EthFilter::new( self.provider.clone(), @@ -1022,19 +1023,19 @@ where executor, ); - let eth = EthHandlers { api, cache, filter, pubsub }; + let eth = EthHandlers { api, cache, filter, pubsub, tracing_call_pool }; self.eth = Some(eth); } f(self.eth.as_ref().expect("exists; qed")) } /// Returns the configured [EthHandlers] or creates it if it does not exist yet - fn eth_handlers(&mut self) -> EthHandlers { + pub fn eth_handlers(&mut self) -> EthHandlers { self.with_eth(|handlers| handlers.clone()) } /// Returns the configured [EthApi] or creates it if it does not exist yet - fn eth_api(&mut self) -> EthApi { + pub fn eth_api(&mut self) -> EthApi { self.with_eth(|handlers| handlers.api.clone()) } } @@ -1459,6 +1460,65 @@ impl TransportRpcModules<()> { &self.config } + /// Merge the given Methods in the configured http methods. + /// + /// Fails if any of the methods in other is present already. + /// + /// Returns Ok(false) if no http transport is configured. + pub fn merge_http( + &mut self, + other: impl Into, + ) -> Result { + if let Some(ref mut http) = self.http { + return http.merge(other.into()).map(|_| true) + } + Ok(false) + } + + /// Merge the given Methods in the configured ws methods. + /// + /// Fails if any of the methods in other is present already. + /// + /// Returns Ok(false) if no http transport is configured. + pub fn merge_ws( + &mut self, + other: impl Into, + ) -> Result { + if let Some(ref mut ws) = self.ws { + return ws.merge(other.into()).map(|_| true) + } + Ok(false) + } + + /// Merge the given Methods in the configured ipc methods. + /// + /// Fails if any of the methods in other is present already. + /// + /// Returns Ok(false) if no ipc transport is configured. + pub fn merge_ipc( + &mut self, + other: impl Into, + ) -> Result { + if let Some(ref mut http) = self.http { + return http.merge(other.into()).map(|_| true) + } + Ok(false) + } + + /// Merge the given Methods in all configured methods. + /// + /// Fails if any of the methods in other is present already. + pub fn merge_configured( + &mut self, + other: impl Into, + ) -> Result<(), jsonrpsee::core::error::Error> { + let other = other.into(); + self.merge_http(other.clone())?; + self.merge_ws(other.clone())?; + self.merge_ipc(other.clone())?; + Ok(()) + } + /// Convenience function for starting a server pub async fn start_server(self, builder: RpcServerConfig) -> Result { builder.start(self).await @@ -1503,7 +1563,7 @@ impl WsHttpServers { config.ensure_ws_http_identical()?; if let Some(module) = http_module.or(ws_module) { - let handle = both.start(module).await?; + let handle = both.start(module).await; http_handle = Some(handle.clone()); ws_handle = Some(handle); } @@ -1512,12 +1572,12 @@ impl WsHttpServers { if let Some((server, module)) = http.and_then(|server| http_module.map(|module| (server, module))) { - http_handle = Some(server.start(module).await?); + http_handle = Some(server.start(module).await); } if let Some((server, module)) = ws.and_then(|server| ws_module.map(|module| (server, module))) { - ws_handle = Some(server.start(module).await?); + ws_handle = Some(server.start(module).await); } } } @@ -1544,10 +1604,10 @@ enum WsHttpServerKind { impl WsHttpServerKind { /// Starts the server and returns the handle - async fn start(self, module: RpcModule<()>) -> Result { + async fn start(self, module: RpcModule<()>) -> ServerHandle { match self { - WsHttpServerKind::Plain(server) => Ok(server.start(module)?), - WsHttpServerKind::WithCors(server) => Ok(server.start(module)?), + WsHttpServerKind::Plain(server) => server.start(module), + WsHttpServerKind::WithCors(server) => server.start(module), } } diff --git a/crates/rpc/rpc-builder/src/metrics.rs b/crates/rpc/rpc-builder/src/metrics.rs index e40e2c23258..ee3ed62328a 100644 --- a/crates/rpc/rpc-builder/src/metrics.rs +++ b/crates/rpc/rpc-builder/src/metrics.rs @@ -1,4 +1,7 @@ -use jsonrpsee::server::logger::{HttpRequest, Logger, MethodKind, Params, TransportProtocol}; +use jsonrpsee::{ + helpers::MethodResponseResult, + server::logger::{HttpRequest, Logger, MethodKind, Params, TransportProtocol}, +}; use reth_metrics::{ metrics::{self, Counter, Histogram}, Metrics, @@ -58,13 +61,13 @@ impl Logger for RpcServerMetrics { fn on_result( &self, _method_name: &str, - success: bool, + success: MethodResponseResult, started_at: Self::Instant, _transport: TransportProtocol, ) { // capture call duration self.call_latency.record(started_at.elapsed().as_millis() as f64); - if !success { + if success.is_error() { self.failed_calls.increment(1); } else { self.successful_calls.increment(1); diff --git a/crates/rpc/rpc-engine-api/Cargo.toml b/crates/rpc/rpc-engine-api/Cargo.toml index fc09c60010a..6e64471c26b 100644 --- a/crates/rpc/rpc-engine-api/Cargo.toml +++ b/crates/rpc/rpc-engine-api/Cargo.toml @@ -25,12 +25,16 @@ tokio = { workspace = true, features = ["sync"] } # misc async-trait.workspace = true thiserror.workspace = true -jsonrpsee-types = "0.18" -jsonrpsee-core = "0.18" +jsonrpsee-types.workspace = true +jsonrpsee-core.workspace = true tracing.workspace = true [dev-dependencies] +reth-rlp.workspace = true reth-interfaces = { workspace = true, features = ["test-utils"] } reth-provider = { workspace = true, features = ["test-utils"] } reth-payload-builder = { workspace = true, features = ["test-utils"] } assert_matches = "1.5.0" + +[features] +optimism = ["reth-primitives/optimism", "reth-rpc-types/optimism"] diff --git a/crates/rpc/rpc-engine-api/src/engine_api.rs b/crates/rpc/rpc-engine-api/src/engine_api.rs index 2a0581595f4..56ddc71cdf9 100644 --- a/crates/rpc/rpc-engine-api/src/engine_api.rs +++ b/crates/rpc/rpc-engine-api/src/engine_api.rs @@ -4,7 +4,7 @@ use jsonrpsee_core::RpcResult; use reth_beacon_consensus::BeaconConsensusEngineHandle; use reth_interfaces::consensus::ForkchoiceState; use reth_payload_builder::PayloadStore; -use reth_primitives::{BlockHash, BlockHashOrNumber, BlockNumber, ChainSpec, Hardfork, U64}; +use reth_primitives::{BlockHash, BlockHashOrNumber, BlockNumber, ChainSpec, Hardfork, H256, U64}; use reth_provider::{BlockReader, EvmEnvProvider, HeaderProvider, StateProviderFactory}; use reth_rpc_api::EngineApiServer; use reth_rpc_types::engine::{ @@ -130,6 +130,26 @@ where Ok(self.inner.beacon_consensus.fork_choice_updated(state, payload_attrs).await?) } + /// Sends a message to the beacon consensus engine to update the fork choice _with_ withdrawals, + /// but only _after_ cancun. + /// + /// See also + pub async fn fork_choice_updated_v3( + &self, + state: ForkchoiceState, + payload_attrs: Option, + ) -> EngineApiResult { + if let Some(ref attrs) = payload_attrs { + self.validate_withdrawals_presence( + EngineApiMessageVersion::V3, + attrs.timestamp.as_u64(), + attrs.withdrawals.is_some(), + )?; + } + + Ok(self.inner.beacon_consensus.fork_choice_updated(state, payload_attrs).await?) + } + /// Returns the most recent version of the payload that is available in the corresponding /// payload build process at the time of receiving this call. /// @@ -321,7 +341,7 @@ where return Err(EngineApiError::NoWithdrawalsPostShanghai) } } - EngineApiMessageVersion::V2 => { + EngineApiMessageVersion::V2 | EngineApiMessageVersion::V3 => { if is_shanghai && !has_withdrawals { return Err(EngineApiError::NoWithdrawalsPostShanghai) } @@ -355,6 +375,15 @@ where Ok(EngineApi::new_payload_v2(self, payload).await?) } + async fn new_payload_v3( + &self, + _payload: ExecutionPayload, + _versioned_hashes: Vec, + _parent_beacon_block_root: H256, + ) -> RpcResult { + Err(jsonrpsee_types::error::ErrorCode::MethodNotFound.into()) + } + /// Handler for `engine_forkchoiceUpdatedV1` /// See also /// @@ -379,6 +408,17 @@ where Ok(EngineApi::fork_choice_updated_v2(self, fork_choice_state, payload_attributes).await?) } + /// Handler for `engine_forkchoiceUpdatedV2` + /// + /// See also + async fn fork_choice_updated_v3( + &self, + _fork_choice_state: ForkchoiceState, + _payload_attributes: Option, + ) -> RpcResult { + Err(jsonrpsee_types::error::ErrorCode::MethodNotFound.into()) + } + /// Handler for `engine_getPayloadV1` /// /// Returns the most recent version of the payload that is available in the corresponding @@ -409,6 +449,10 @@ where Ok(EngineApi::get_payload_v2(self, payload_id).await?) } + async fn get_payload_v3(&self, _payload_id: PayloadId) -> RpcResult { + Err(jsonrpsee_types::error::ErrorCode::MethodNotFound.into()) + } + /// Handler for `engine_getPayloadBodiesByHashV1` /// See also async fn get_payload_bodies_by_hash_v1( diff --git a/crates/rpc/rpc-engine-api/src/error.rs b/crates/rpc/rpc-engine-api/src/error.rs index 26c29916c30..b6dfca1b68c 100644 --- a/crates/rpc/rpc-engine-api/src/error.rs +++ b/crates/rpc/rpc-engine-api/src/error.rs @@ -75,6 +75,11 @@ pub enum EngineApiError { /// Fetching the payload failed #[error(transparent)] GetPayloadError(#[from] PayloadBuilderError), + /// If the optimism feature flag is enabled, the payload attributes must have a present + /// gas limit for the forkchoice updated method. + #[cfg(feature = "optimism")] + #[error("Missing gas limit in payload attributes")] + MissingGasLimitInPayloadAttributes, } impl From for jsonrpsee_types::error::ErrorObject<'static> { @@ -102,6 +107,9 @@ impl From for jsonrpsee_types::error::ErrorObject<'static> { EngineApiError::TerminalBlockHash { .. } | EngineApiError::Internal(_) | EngineApiError::GetPayloadError(_) => INTERNAL_ERROR_CODE, + // Optimism errors + #[cfg(feature = "optimism")] + EngineApiError::MissingGasLimitInPayloadAttributes => INVALID_PARAMS_CODE, }; jsonrpsee_types::error::ErrorObject::owned(code, error.to_string(), None::<()>) } diff --git a/crates/rpc/rpc-engine-api/src/lib.rs b/crates/rpc/rpc-engine-api/src/lib.rs index fa440801b11..ba36670c5aa 100644 --- a/crates/rpc/rpc-engine-api/src/lib.rs +++ b/crates/rpc/rpc-engine-api/src/lib.rs @@ -29,3 +29,10 @@ pub use message::EngineApiMessageVersion; // re-export server trait for convenience pub use reth_rpc_api::EngineApiServer; + +#[cfg(test)] +#[allow(unused_imports)] +mod tests { + // silence unused import warning + use reth_rlp as _; +} diff --git a/crates/rpc/rpc-engine-api/src/message.rs b/crates/rpc/rpc-engine-api/src/message.rs index 9d000ed7440..c0d6b85d511 100644 --- a/crates/rpc/rpc-engine-api/src/message.rs +++ b/crates/rpc/rpc-engine-api/src/message.rs @@ -4,5 +4,11 @@ pub enum EngineApiMessageVersion { /// Version 1 V1, /// Version 2 + /// + /// Added for shanghai hardfork. V2, + /// Version 3 + /// + /// Added for cancun hardfork. + V3, } diff --git a/crates/rpc/rpc-engine-api/tests/it/main.rs b/crates/rpc/rpc-engine-api/tests/it/main.rs new file mode 100644 index 00000000000..cf02e709823 --- /dev/null +++ b/crates/rpc/rpc-engine-api/tests/it/main.rs @@ -0,0 +1,3 @@ +mod payload; + +fn main() {} diff --git a/crates/rpc/rpc-engine-api/tests/it/payload.rs b/crates/rpc/rpc-engine-api/tests/it/payload.rs new file mode 100644 index 00000000000..bc9f1f4249a --- /dev/null +++ b/crates/rpc/rpc-engine-api/tests/it/payload.rs @@ -0,0 +1,127 @@ +//! Some payload tests + +use assert_matches::assert_matches; +use reth_interfaces::test_utils::generators::{ + self, random_block, random_block_range, random_header, +}; +use reth_primitives::{ + bytes::{Bytes, BytesMut}, + proofs::{self}, + Block, SealedBlock, TransactionSigned, H256, U256, +}; +use reth_rlp::{Decodable, DecodeError}; +use reth_rpc_types::engine::{ExecutionPayload, ExecutionPayloadBodyV1, PayloadError}; + +fn transform_block Block>(src: SealedBlock, f: F) -> ExecutionPayload { + let unsealed = src.unseal(); + let mut transformed: Block = f(unsealed); + // Recalculate roots + transformed.header.transactions_root = proofs::calculate_transaction_root(&transformed.body); + transformed.header.ommers_hash = proofs::calculate_ommers_root(&transformed.ommers); + SealedBlock { + header: transformed.header.seal_slow(), + body: transformed.body, + ommers: transformed.ommers, + withdrawals: transformed.withdrawals, + } + .into() +} + +#[test] +fn payload_body_roundtrip() { + let mut rng = generators::rng(); + for block in random_block_range(&mut rng, 0..=99, H256::default(), 0..2) { + let unsealed = block.clone().unseal(); + let payload_body: ExecutionPayloadBodyV1 = unsealed.into(); + + assert_eq!( + Ok(block.body), + payload_body + .transactions + .iter() + .map(|x| TransactionSigned::decode(&mut &x[..])) + .collect::, _>>(), + ); + + assert_eq!(block.withdrawals, payload_body.withdrawals); + } +} + +#[test] +fn payload_validation() { + let mut rng = generators::rng(); + let block = random_block(&mut rng, 100, Some(H256::random()), Some(3), Some(0)); + + // Valid extra data + let block_with_valid_extra_data = transform_block(block.clone(), |mut b| { + b.header.extra_data = BytesMut::zeroed(32).freeze().into(); + b + }); + assert_matches!(TryInto::::try_into(block_with_valid_extra_data), Ok(_)); + + // Invalid extra data + let block_with_invalid_extra_data: Bytes = BytesMut::zeroed(33).freeze(); + let invalid_extra_data_block = transform_block(block.clone(), |mut b| { + b.header.extra_data = block_with_invalid_extra_data.clone().into(); + b + }); + assert_matches!( + TryInto::::try_into(invalid_extra_data_block), + Err(PayloadError::ExtraData(data)) if data == block_with_invalid_extra_data + ); + + // Zero base fee + let block_with_zero_base_fee = transform_block(block.clone(), |mut b| { + b.header.base_fee_per_gas = Some(0); + b + }); + assert_matches!( + TryInto::::try_into(block_with_zero_base_fee), + Err(PayloadError::BaseFee(val)) if val == U256::ZERO + ); + + // Invalid encoded transactions + let mut payload_with_invalid_txs: ExecutionPayload = block.clone().into(); + payload_with_invalid_txs.transactions.iter_mut().for_each(|tx| { + *tx = Bytes::new().into(); + }); + assert_matches!( + TryInto::::try_into(payload_with_invalid_txs), + Err(PayloadError::Decode(DecodeError::InputTooShort)) + ); + + // Non empty ommers + let block_with_ommers = transform_block(block.clone(), |mut b| { + b.ommers.push(random_header(&mut rng, 100, None).unseal()); + b + }); + assert_matches!( + TryInto::::try_into(block_with_ommers.clone()), + Err(PayloadError::BlockHash { consensus, .. }) + if consensus == block_with_ommers.block_hash + ); + + // None zero difficulty + let block_with_difficulty = transform_block(block.clone(), |mut b| { + b.header.difficulty = U256::from(1); + b + }); + assert_matches!( + TryInto::::try_into(block_with_difficulty.clone()), + Err(PayloadError::BlockHash { consensus, .. }) if consensus == block_with_difficulty.block_hash + ); + + // None zero nonce + let block_with_nonce = transform_block(block.clone(), |mut b| { + b.header.nonce = 1; + b + }); + assert_matches!( + TryInto::::try_into(block_with_nonce.clone()), + Err(PayloadError::BlockHash { consensus, .. }) if consensus == block_with_nonce.block_hash + ); + + // Valid block + let valid_block = block; + assert_matches!(TryInto::::try_into(valid_block), Ok(_)); +} diff --git a/crates/rpc/rpc-testing-util/Cargo.toml b/crates/rpc/rpc-testing-util/Cargo.toml index 93415adb681..5d0d1a2b2cf 100644 --- a/crates/rpc/rpc-testing-util/Cargo.toml +++ b/crates/rpc/rpc-testing-util/Cargo.toml @@ -21,7 +21,7 @@ async-trait.workspace = true futures.workspace = true # misc -jsonrpsee = { version = "0.18", features = ["client", "async-client"] } +jsonrpsee = { workspace = true, features = ["client", "async-client"] } serde_json.workspace = true [dev-dependencies] diff --git a/crates/rpc/rpc-testing-util/src/debug.rs b/crates/rpc/rpc-testing-util/src/debug.rs index 2aad67e1754..aedb83ac16f 100644 --- a/crates/rpc/rpc-testing-util/src/debug.rs +++ b/crates/rpc/rpc-testing-util/src/debug.rs @@ -40,11 +40,20 @@ impl DebugApiExt for T { /// A helper type that can be used to build a javascript tracer. #[derive(Debug, Clone, Default)] pub struct JsTracerBuilder { + /// `setup_body` is invoked once at the beginning, during the construction of a given + /// transaction. setup_body: Option, + /// `fault_body` is invoked when an error happens during the execution of an opcode which + /// wasn't reported in step. fault_body: Option, + /// `result_body` returns a JSON-serializable value to the RPC caller. result_body: Option, + /// `enter_body` is invoked on stepping in of an internal call. enter_body: Option, + /// `step_body` is called for each step of the EVM, or when an error occurs, as the specified + /// transaction is traced. step_body: Option, + /// `exit_body` is invoked on stepping out of an internal call. exit_body: Option, } diff --git a/crates/rpc/rpc-types/Cargo.toml b/crates/rpc/rpc-types/Cargo.toml index 8ca59c36ca8..d168a0f70c7 100644 --- a/crates/rpc/rpc-types/Cargo.toml +++ b/crates/rpc/rpc-types/Cargo.toml @@ -19,9 +19,13 @@ reth-rlp.workspace = true thiserror.workspace = true # misc +itertools.workspace = true serde = { workspace = true, features = ["derive"] } serde_json.workspace = true -jsonrpsee-types = { version = "0.18" } +jsonrpsee-types = { workspace = true, optional = true } + +[features] +default = ["jsonrpsee-types"] [dev-dependencies] # reth @@ -29,5 +33,7 @@ reth-interfaces = { workspace = true, features = ["test-utils"] } # misc rand.workspace = true -assert_matches = "1.5" similar-asserts = "1.4" + +[features] +optimism = ["reth-primitives/optimism"] diff --git a/crates/rpc/rpc-types/src/eth/block.rs b/crates/rpc/rpc-types/src/eth/block.rs index ff47937af4e..5b07b300878 100644 --- a/crates/rpc/rpc-types/src/eth/block.rs +++ b/crates/rpc/rpc-types/src/eth/block.rs @@ -267,6 +267,9 @@ impl Header { base_fee_per_gas, extra_data, withdrawals_root, + // TODO: add header fields to the rpc header + blob_gas_used: _, + excess_blob_gas: _, }, hash, } = primitive_header; @@ -376,7 +379,6 @@ pub struct BlockOverrides { #[cfg(test)] mod tests { use super::*; - use jsonrpsee_types::SubscriptionResponse; #[test] fn test_full_conversion() { @@ -388,7 +390,9 @@ mod tests { } #[test] - fn serde_header() { + #[cfg(feature = "jsonrpsee-types")] + fn serde_json_header() { + use jsonrpsee_types::SubscriptionResponse; let resp = r#"{"jsonrpc":"2.0","method":"eth_subscribe","params":{"subscription":"0x7eef37ff35d471f8825b1c8f67a5d3c0","result":{"hash":"0x7a7ada12e140961a32395059597764416499f4178daf1917193fad7bd2cc6386","parentHash":"0xdedbd831f496e705e7f2ec3c8dcb79051040a360bf1455dbd7eb8ea6ad03b751","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","number":"0x8","gasUsed":"0x0","gasLimit":"0x1c9c380","extraData":"0x","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","timestamp":"0x642aa48f","difficulty":"0x0","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000"}}}"#; let _header: SubscriptionResponse<'_, Header> = serde_json::from_str(resp).unwrap(); diff --git a/crates/rpc/rpc-types/src/eth/engine/error.rs b/crates/rpc/rpc-types/src/eth/engine/error.rs deleted file mode 100644 index 36feba4dfd8..00000000000 --- a/crates/rpc/rpc-types/src/eth/engine/error.rs +++ /dev/null @@ -1,58 +0,0 @@ -//! Commonly used errors for the `engine_` namespace. - -/// List of Engine API errors used in RPC, see -/// -/// Note: These are all errors that can be returned by the `engine_` namespace in the error case. -/// -/// TODO: get rid of this -#[derive(Debug, Copy, PartialEq, Eq, Clone, thiserror::Error)] -pub enum EngineRpcError { - /// Invalid JSON was received by the server. - #[error("Invalid JSON was received by the server")] - ParseError, - /// The JSON sent is not a valid Request object. - #[error("The JSON sent is not a valid Request object")] - InvalidRequest, - /// The method does not exist / is not available. - #[error("The method does not exist / is not available")] - MethodNotFound, - /// Invalid method parameter(s). - #[error("Invalid method parameter(s)")] - InvalidParams, - /// Internal JSON-RPC error. - #[error("Internal JSON-RPC error")] - InternalError, - /// Generic client error while processing request. - #[error("Server error")] - ServerError, - /// Payload does not exist / is not available. - #[error("Unknown payload")] - UnknownPayload, - /// Forkchoice state is invalid / inconsistent. - #[error("Invalid forkchoice state")] - InvalidForkchoiceState, - /// Payload attributes are invalid / inconsistent. - #[error("Invalid payload attributes")] - InvalidPayloadAttributes, - /// Number of requested entities is too large. - #[error("Too large request")] - TooLargeRequest, -} - -impl EngineRpcError { - /// Returns the error code as `i32` - pub const fn code(&self) -> i32 { - match *self { - EngineRpcError::ParseError => -32700, - EngineRpcError::InvalidRequest => -32600, - EngineRpcError::MethodNotFound => -32601, - EngineRpcError::InvalidParams => -32602, - EngineRpcError::InternalError => -32603, - EngineRpcError::ServerError => -32000, - EngineRpcError::UnknownPayload => -38001, - EngineRpcError::InvalidForkchoiceState => -38002, - EngineRpcError::InvalidPayloadAttributes => -38003, - EngineRpcError::TooLargeRequest => -38004, - } - } -} diff --git a/crates/rpc/rpc-types/src/eth/engine/forkchoice.rs b/crates/rpc/rpc-types/src/eth/engine/forkchoice.rs index ea215df5c16..f1c2ca4a79c 100644 --- a/crates/rpc/rpc-types/src/eth/engine/forkchoice.rs +++ b/crates/rpc/rpc-types/src/eth/engine/forkchoice.rs @@ -50,6 +50,7 @@ pub enum ForkchoiceUpdateError { UnknownFinalBlock, } +#[cfg(feature = "jsonrpsee-types")] impl From for jsonrpsee_types::error::ErrorObject<'static> { fn from(value: ForkchoiceUpdateError) -> Self { match value { diff --git a/crates/rpc/rpc-types/src/eth/engine/mod.rs b/crates/rpc/rpc-types/src/eth/engine/mod.rs index 5212588decc..2a814374fb2 100644 --- a/crates/rpc/rpc-types/src/eth/engine/mod.rs +++ b/crates/rpc/rpc-types/src/eth/engine/mod.rs @@ -2,12 +2,11 @@ #![allow(missing_docs)] -mod error; mod forkchoice; mod payload; mod transition; -pub use self::{error::*, forkchoice::*, payload::*, transition::*}; +pub use self::{forkchoice::*, payload::*, transition::*}; /// The list of supported Engine capabilities pub const CAPABILITIES: [&str; 9] = [ diff --git a/crates/rpc/rpc-types/src/eth/engine/payload.rs b/crates/rpc/rpc-types/src/eth/engine/payload.rs index 5b63e1b8cc5..72eba8bab8e 100644 --- a/crates/rpc/rpc-types/src/eth/engine/payload.rs +++ b/crates/rpc/rpc-types/src/eth/engine/payload.rs @@ -46,6 +46,10 @@ pub struct ExecutionPayloadEnvelope { /// The expected value to be received by the feeRecipient in wei #[serde(rename = "blockValue")] pub block_value: U256, + // + // // TODO(mattsse): for V3 + // #[serde(rename = "blobsBundle", skip_serializing_if = "Option::is_none")] + // pub blobs_bundle: Option, } impl ExecutionPayloadEnvelope { @@ -168,6 +172,9 @@ impl TryFrom for SealedBlock { ommers_hash: EMPTY_LIST_HASH, difficulty: Default::default(), nonce: Default::default(), + // TODO: add conversion once ExecutionPayload has 4844 fields + blob_gas_used: None, + excess_blob_gas: None, } .seal_slow(); @@ -187,6 +194,14 @@ impl TryFrom for SealedBlock { } } +/// This includes all bundled blob related data of an executed payload. +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct BlobsBundleV1 { + pub commitments: Vec, + pub proofs: Vec, + pub blobs: Vec, +} + /// Error that can occur when handling payloads. #[derive(thiserror::Error, Debug)] pub enum PayloadError { @@ -255,6 +270,28 @@ pub struct PayloadAttributes { /// See #[serde(default, skip_serializing_if = "Option::is_none")] pub withdrawals: Option>, + + /// Root of the parent beacon block enabled with V3. + /// + /// See also + #[serde(default, skip_serializing_if = "Option::is_none")] + pub parent_beacon_block_root: Option, + + /// Transactions is a field for rollups: the transactions list is forced into the block + #[cfg(feature = "optimism")] + #[serde(default, skip_serializing_if = "Option::is_none")] + pub transactions: Option>, + + /// If true, the no transactions are taken out of the tx-pool, only transactions from the above + /// Transactions list will be included. + #[cfg(feature = "optimism")] + #[serde(default, skip_serializing_if = "Option::is_none")] + pub no_tx_pool: Option, + + /// If set, this sets the exact gas limit the block produced with. + #[cfg(feature = "optimism")] + #[serde(default, skip_serializing_if = "Option::is_none")] + pub gas_limit: Option, } /// This structure contains the result of processing a payload or fork choice update. @@ -335,25 +372,25 @@ impl From for PayloadStatusEnum { #[serde(tag = "status", rename_all = "SCREAMING_SNAKE_CASE")] pub enum PayloadStatusEnum { /// VALID is returned by the engine API in the following calls: - /// - newPayloadV1: if the payload was already known or was just validated and executed - /// - forkchoiceUpdateV1: if the chain accepted the reorg (might ignore if it's stale) + /// - newPayload: if the payload was already known or was just validated and executed + /// - forkchoiceUpdate: if the chain accepted the reorg (might ignore if it's stale) Valid, /// INVALID is returned by the engine API in the following calls: - /// - newPayloadV1: if the payload failed to execute on top of the local chain - /// - forkchoiceUpdateV1: if the new head is unknown, pre-merge, or reorg to it fails + /// - newPayload: if the payload failed to execute on top of the local chain + /// - forkchoiceUpdate: if the new head is unknown, pre-merge, or reorg to it fails Invalid { #[serde(rename = "validationError")] validation_error: String, }, /// SYNCING is returned by the engine API in the following calls: - /// - newPayloadV1: if the payload was accepted on top of an active sync - /// - forkchoiceUpdateV1: if the new head was seen before, but not part of the chain + /// - newPayload: if the payload was accepted on top of an active sync + /// - forkchoiceUpdate: if the new head was seen before, but not part of the chain Syncing, /// ACCEPTED is returned by the engine API in the following calls: - /// - newPayloadV1: if the payload was accepted, but not processed (side chain) + /// - newPayload: if the payload was accepted, but not processed (side chain) Accepted, } @@ -429,130 +466,6 @@ pub enum PayloadValidationError { #[cfg(test)] mod tests { use super::*; - use assert_matches::assert_matches; - use reth_interfaces::test_utils::generators::{ - self, random_block, random_block_range, random_header, - }; - use reth_primitives::{ - bytes::{Bytes, BytesMut}, - TransactionSigned, H256, - }; - use reth_rlp::{Decodable, DecodeError}; - - fn transform_block Block>(src: SealedBlock, f: F) -> ExecutionPayload { - let unsealed = src.unseal(); - let mut transformed: Block = f(unsealed); - // Recalculate roots - transformed.header.transactions_root = - proofs::calculate_transaction_root(&transformed.body); - transformed.header.ommers_hash = proofs::calculate_ommers_root(&transformed.ommers); - SealedBlock { - header: transformed.header.seal_slow(), - body: transformed.body, - ommers: transformed.ommers, - withdrawals: transformed.withdrawals, - } - .into() - } - - #[test] - fn payload_body_roundtrip() { - let mut rng = generators::rng(); - for block in random_block_range(&mut rng, 0..=99, H256::default(), 0..2) { - let unsealed = block.clone().unseal(); - let payload_body: ExecutionPayloadBodyV1 = unsealed.into(); - - assert_eq!( - Ok(block.body), - payload_body - .transactions - .iter() - .map(|x| TransactionSigned::decode(&mut &x[..])) - .collect::, _>>(), - ); - - assert_eq!(block.withdrawals, payload_body.withdrawals); - } - } - - #[test] - fn payload_validation() { - let mut rng = generators::rng(); - let block = random_block(&mut rng, 100, Some(H256::random()), Some(3), Some(0)); - - // Valid extra data - let block_with_valid_extra_data = transform_block(block.clone(), |mut b| { - b.header.extra_data = BytesMut::zeroed(32).freeze().into(); - b - }); - assert_matches!(TryInto::::try_into(block_with_valid_extra_data), Ok(_)); - - // Invalid extra data - let block_with_invalid_extra_data: Bytes = BytesMut::zeroed(33).freeze(); - let invalid_extra_data_block = transform_block(block.clone(), |mut b| { - b.header.extra_data = block_with_invalid_extra_data.clone().into(); - b - }); - assert_matches!( - TryInto::::try_into(invalid_extra_data_block), - Err(PayloadError::ExtraData(data)) if data == block_with_invalid_extra_data - ); - - // Zero base fee - let block_with_zero_base_fee = transform_block(block.clone(), |mut b| { - b.header.base_fee_per_gas = Some(0); - b - }); - assert_matches!( - TryInto::::try_into(block_with_zero_base_fee), - Err(PayloadError::BaseFee(val)) if val == U256::ZERO - ); - - // Invalid encoded transactions - let mut payload_with_invalid_txs: ExecutionPayload = block.clone().into(); - payload_with_invalid_txs.transactions.iter_mut().for_each(|tx| { - *tx = Bytes::new().into(); - }); - assert_matches!( - TryInto::::try_into(payload_with_invalid_txs), - Err(PayloadError::Decode(DecodeError::InputTooShort)) - ); - - // Non empty ommers - let block_with_ommers = transform_block(block.clone(), |mut b| { - b.ommers.push(random_header(&mut rng, 100, None).unseal()); - b - }); - assert_matches!( - TryInto::::try_into(block_with_ommers.clone()), - Err(PayloadError::BlockHash { consensus, .. }) - if consensus == block_with_ommers.block_hash - ); - - // None zero difficulty - let block_with_difficulty = transform_block(block.clone(), |mut b| { - b.header.difficulty = U256::from(1); - b - }); - assert_matches!( - TryInto::::try_into(block_with_difficulty.clone()), - Err(PayloadError::BlockHash { consensus, .. }) if consensus == block_with_difficulty.block_hash - ); - - // None zero nonce - let block_with_nonce = transform_block(block.clone(), |mut b| { - b.header.nonce = 1; - b - }); - assert_matches!( - TryInto::::try_into(block_with_nonce.clone()), - Err(PayloadError::BlockHash { consensus, .. }) if consensus == block_with_nonce.block_hash - ); - - // Valid block - let valid_block = block; - assert_matches!(TryInto::::try_into(valid_block), Ok(_)); - } #[test] fn serde_payload_status() { diff --git a/crates/rpc/rpc-types/src/eth/filter.rs b/crates/rpc/rpc-types/src/eth/filter.rs index d4b6365ccc1..bfe92fbe070 100644 --- a/crates/rpc/rpc-types/src/eth/filter.rs +++ b/crates/rpc/rpc-types/src/eth/filter.rs @@ -1,5 +1,5 @@ use crate::Log as RpcLog; -use jsonrpsee_types::SubscriptionId; +use itertools::{EitherOrBoth::*, Itertools}; use reth_primitives::{ bloom::{Bloom, Input}, keccak256, Address, BlockNumberOrTag, Log, H160, H256, U256, U64, @@ -9,13 +9,120 @@ use serde::{ ser::SerializeStruct, Deserialize, Deserializer, Serialize, Serializer, }; -use std::ops::{Range, RangeFrom, RangeTo}; +use std::{ + collections::HashSet, + hash::Hash, + ops::{Range, RangeFrom, RangeTo}, +}; /// Helper type to represent a bloom filter used for matching logs. -pub type BloomFilter = Vec>; +#[derive(Default, Debug)] +pub struct BloomFilter(Vec); + +impl From> for BloomFilter { + fn from(src: Vec) -> Self { + BloomFilter(src) + } +} + +impl BloomFilter { + /// Returns whether the given bloom matches the list of Blooms in the current filter. + /// If the filter is empty (the list is empty), then any bloom matches + /// Otherwise, there must be at least one matche for the BloomFilter to match. + pub fn matches(&self, bloom: Bloom) -> bool { + self.0.is_empty() || self.0.iter().any(|a| bloom.contains_bloom(a)) + } +} + +#[derive(Default, Debug, PartialEq, Eq, Clone, Deserialize)] +/// FilterSet is a set of values that will be used to filter logs +pub struct FilterSet(HashSet); + +impl From for FilterSet { + fn from(src: T) -> Self { + FilterSet(HashSet::from([src])) + } +} + +impl From> for FilterSet { + fn from(src: Vec) -> Self { + FilterSet(HashSet::from_iter(src.into_iter().map(Into::into))) + } +} + +impl From> for FilterSet { + fn from(src: ValueOrArray) -> Self { + match src { + ValueOrArray::Value(val) => val.into(), + ValueOrArray::Array(arr) => arr.into(), + } + } +} + +impl From>> for FilterSet { + fn from(src: ValueOrArray>) -> Self { + match src { + ValueOrArray::Value(None) => FilterSet(HashSet::new()), + ValueOrArray::Value(Some(val)) => val.into(), + ValueOrArray::Array(arr) => { + // If the array contains at least one `null` (ie. None), as it's considered + // a "wildcard" value, the whole filter should be treated as matching everything, + // thus is empty. + if arr.iter().contains(&None) { + FilterSet(HashSet::new()) + } else { + // Otherwise, we flatten the array, knowing there are no `None` values + arr.into_iter().flatten().collect::>().into() + } + } + } + } +} + +impl FilterSet { + /// Returns wheter the filter is empty + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Returns whether the given value matches the filter. It the filter is empty + /// any value matches. Otherwise, the filter must include the value + pub fn matches(&self, value: &T) -> bool { + self.is_empty() || self.0.contains(value) + } +} + +impl + Eq + Hash> FilterSet { + /// Returns a list of Bloom (BloomFilter) corresponding to the filter's values + pub fn to_bloom_filter(&self) -> BloomFilter { + self.0.iter().map(|a| Input::Raw(a.as_ref()).into()).collect::>().into() + } +} + +impl FilterSet { + /// Returns a ValueOrArray inside an Option, so that: + /// - If the filter is empty, it returns None + /// - If the filter has only 1 value, it returns the single value + /// - Otherwise it returns an array of values + /// This should be useful for serialization + pub fn to_value_or_array(&self) -> Option> { + let mut values = self.0.iter().cloned().collect::>(); + match values.len() { + 0 => None, + 1 => Some(ValueOrArray::Value(values.pop().expect("values length is one"))), + _ => Some(ValueOrArray::Array(values)), + } + } +} /// A single topic -pub type Topic = ValueOrArray>; +pub type Topic = FilterSet; + +impl From for Topic { + fn from(src: U256) -> Self { + Into::::into(src).into() + } +} /// Represents the target range of blocks for the filter #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] @@ -141,16 +248,16 @@ impl FilterBlockOption { } /// Filter for -#[derive(Default, Debug, PartialEq, Eq, Clone, Hash)] +#[derive(Default, Debug, PartialEq, Eq, Clone)] pub struct Filter { /// Filter block options, specifying on which blocks the filter should /// match. // https://eips.ethereum.org/EIPS/eip-234 pub block_option: FilterBlockOption, /// Address - pub address: Option>, + pub address: FilterSet
, /// Topics (maxmimum of 4) - pub topics: [Option; 4], + pub topics: [Topic; 4], } impl Filter { @@ -279,7 +386,7 @@ impl Filter { /// ``` #[must_use] pub fn address>>(mut self, address: T) -> Self { - self.address = Some(address.into()); + self.address = address.into().into(); self } @@ -300,28 +407,28 @@ impl Filter { /// Sets topic0 (the event name for non-anonymous events) #[must_use] pub fn topic0>(mut self, topic: T) -> Self { - self.topics[0] = Some(topic.into()); + self.topics[0] = topic.into(); self } /// Sets the 1st indexed topic #[must_use] pub fn topic1>(mut self, topic: T) -> Self { - self.topics[1] = Some(topic.into()); + self.topics[1] = topic.into(); self } /// Sets the 2nd indexed topic #[must_use] pub fn topic2>(mut self, topic: T) -> Self { - self.topics[2] = Some(topic.into()); + self.topics[2] = topic.into(); self } /// Sets the 3rd indexed topic #[must_use] pub fn topic3>(mut self, topic: T) -> Self { - self.topics[3] = Some(topic.into()); + self.topics[3] = topic.into(); self } @@ -348,64 +455,9 @@ impl Filter { } } - /// Flattens the topics using the cartesian product - fn flatten(&self) -> Vec>> { - fn cartesian(lists: &[Vec>]) -> Vec>> { - let mut res = Vec::new(); - let mut list_iter = lists.iter(); - if let Some(first_list) = list_iter.next() { - for &i in first_list { - res.push(vec![i]); - } - } - for l in list_iter { - let mut tmp = Vec::new(); - for r in res { - for &el in l { - let mut tmp_el = r.clone(); - tmp_el.push(el); - tmp.push(tmp_el); - } - } - res = tmp; - } - res - } - let mut out = Vec::new(); - let mut tmp = Vec::new(); - for v in self.topics.iter() { - let v = if let Some(v) = v { - match v { - ValueOrArray::Value(s) => { - vec![*s] - } - ValueOrArray::Array(s) => { - if s.is_empty() { - vec![None] - } else { - s.clone() - } - } - } - } else { - vec![None] - }; - tmp.push(v); - } - for v in cartesian(&tmp) { - out.push(ValueOrArray::Array(v)); - } - out - } - - /// Returns an iterator over all existing topics - pub fn topics(&self) -> impl Iterator + '_ { - self.topics.iter().flatten() - } - /// Returns true if at least one topic is set pub fn has_topics(&self) -> bool { - self.topics.iter().any(|t| t.is_some()) + self.topics.iter().any(|t| !t.is_empty()) } } @@ -429,27 +481,28 @@ impl Serialize for Filter { FilterBlockOption::AtBlockHash(ref h) => s.serialize_field("blockHash", h)?, } - if let Some(ref address) = self.address { - s.serialize_field("address", address)?; + if let Some(address) = self.address.to_value_or_array() { + s.serialize_field("address", &address)?; } let mut filtered_topics = Vec::new(); - for i in 0..4 { - if self.topics[i].is_some() { - filtered_topics.push(&self.topics[i]); - } else { - // TODO: This can be optimized - if self.topics[i + 1..].iter().any(|x| x.is_some()) { - filtered_topics.push(&None); - } + let mut filtered_topics_len = 0; + for (i, topic) in self.topics.iter().enumerate() { + if !topic.is_empty() { + filtered_topics_len = i + 1; } + filtered_topics.push(topic.to_value_or_array()); } + filtered_topics.truncate(filtered_topics_len); s.serialize_field("topics", &filtered_topics)?; s.end() } } +type RawAddressFilter = ValueOrArray>; +type RawTopicsFilter = Vec>>>; + impl<'de> Deserialize<'de> for Filter { fn deserialize(deserializer: D) -> Result where @@ -471,8 +524,8 @@ impl<'de> Deserialize<'de> for Filter { let mut from_block: Option> = None; let mut to_block: Option> = None; let mut block_hash: Option> = None; - let mut address: Option>> = None; - let mut topics: Option>>> = None; + let mut address: Option> = None; + let mut topics: Option> = None; while let Some(key) = map.next_key::()? { match key.as_str() { @@ -534,16 +587,21 @@ impl<'de> Deserialize<'de> for Filter { let from_block = from_block.unwrap_or_default(); let to_block = to_block.unwrap_or_default(); let block_hash = block_hash.unwrap_or_default(); - let address = address.unwrap_or_default(); + let address = address.flatten().map(|a| a.into()).unwrap_or_default(); let topics_vec = topics.flatten().unwrap_or_default(); // maximum allowed filter len if topics_vec.len() > 4 { return Err(serde::de::Error::custom("exceeded maximum topics len")) } - let mut topics: [Option; 4] = [None, None, None, None]; + let mut topics: [Topic; 4] = [ + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ]; for (idx, topic) in topics_vec.into_iter().enumerate() { - topics[idx] = topic; + topics[idx] = topic.map(|t| t.into()).unwrap_or_default(); } let block_option = if let Some(block_hash) = block_hash { @@ -581,47 +639,12 @@ impl From> for ValueOrArray { } } -impl From for Topic { - fn from(src: H256) -> Self { - ValueOrArray::Value(Some(src)) - } -} - impl From> for ValueOrArray { fn from(src: Vec) -> Self { ValueOrArray::Array(src) } } -impl From> for Topic { - fn from(src: ValueOrArray) -> Self { - match src { - ValueOrArray::Value(val) => ValueOrArray::Value(Some(val)), - ValueOrArray::Array(arr) => arr.into(), - } - } -} - -impl> From> for Topic { - fn from(src: Vec) -> Self { - ValueOrArray::Array(src.into_iter().map(Into::into).map(Some).collect()) - } -} - -impl From
for Topic { - fn from(src: Address) -> Self { - let mut bytes = [0; 32]; - bytes[12..32].copy_from_slice(src.as_bytes()); - ValueOrArray::Value(Some(H256::from(bytes))) - } -} - -impl From for Topic { - fn from(src: U256) -> Self { - ValueOrArray::Value(Some(src.into())) - } -} - impl Serialize for ValueOrArray where T: Serialize, @@ -672,8 +695,6 @@ where pub struct FilteredParams { /// The original filter, if any pub filter: Option, - /// Flattened topics of the `filter` used to determine if the the filter matches a log. - pub flat_topics: Vec>>, } impl FilteredParams { @@ -681,86 +702,43 @@ impl FilteredParams { /// for matching pub fn new(filter: Option) -> Self { if let Some(filter) = filter { - let flat_topics = filter.flatten(); - FilteredParams { filter: Some(filter), flat_topics } + FilteredParams { filter: Some(filter) } } else { Default::default() } } /// Returns the [BloomFilter] for the given address - pub fn address_filter(address: &Option>) -> BloomFilter { - address.as_ref().map(address_to_bloom_filter).unwrap_or_default() + pub fn address_filter(address: &FilterSet
) -> BloomFilter { + address.to_bloom_filter() } /// Returns the [BloomFilter] for the given topics - pub fn topics_filter(topics: &Option>>>) -> Vec { - let mut output = Vec::new(); - if let Some(topics) = topics { - output.extend(topics.iter().map(topics_to_bloom_filter)); - } - output + pub fn topics_filter(topics: &[FilterSet]) -> Vec { + topics.iter().map(|t| t.to_bloom_filter()).collect() } /// Returns `true` if the bloom matches the topics - pub fn matches_topics(bloom: Bloom, topic_filters: &[BloomFilter]) -> bool { + pub fn matches_topics(bloom: Bloom, topic_filters: &Vec) -> bool { if topic_filters.is_empty() { return true } - // returns true if a filter matches + // for each filter, iterate through the list of filter blooms. for each set of filter + // (each BloomFilter), the given `bloom` must match at least one of them, unless the list is + // empty (no filters). for filter in topic_filters.iter() { - let mut is_match = false; - for maybe_bloom in filter { - is_match = maybe_bloom.as_ref().map(|b| bloom.contains_bloom(b)).unwrap_or(true); - if !is_match { - break - } - } - if is_match { - return true + if !filter.matches(bloom) { + return false } } - false + true } - /// Returns `true` if the bloom contains the address + /// Returns `true` if the bloom contains one of the address blooms, or the address blooms + /// list is empty (thus, no filters) pub fn matches_address(bloom: Bloom, address_filter: &BloomFilter) -> bool { - if address_filter.is_empty() { - return true - } else { - for maybe_bloom in address_filter { - if maybe_bloom.as_ref().map(|b| bloom.contains_bloom(b)).unwrap_or(true) { - return true - } - } - } - false - } - - /// Replace None values - aka wildcards - for the log input value in that position. - pub fn replace(&self, log: &Log, topic: Topic) -> Option> { - let mut out: Vec = Vec::new(); - match topic { - ValueOrArray::Value(value) => { - if let Some(value) = value { - out.push(value); - } - } - ValueOrArray::Array(value) => { - for (k, v) in value.into_iter().enumerate() { - if let Some(v) = v { - out.push(v); - } else { - out.push(log.topics[k]); - } - } - } - }; - if out.is_empty() { - return None - } - Some(out) + address_filter.matches(bloom) } /// Returns true if the filter matches the given block number @@ -805,117 +783,37 @@ impl FilteredParams { /// Returns `true` if the filter matches the given log. pub fn filter_address(&self, log: &Log) -> bool { - if let Some(input_address) = &self.filter.as_ref().and_then(|f| f.address.clone()) { - match input_address { - ValueOrArray::Value(x) => { - if log.address != *x { - return false - } - } - ValueOrArray::Array(x) => { - if x.is_empty() { - return true - } - if !x.contains(&log.address) { - return false - } - } - } - } - true + self.filter.as_ref().map(|f| f.address.matches(&log.address)).unwrap_or(true) } - /// Returns `true` if the log matches any topic + /// Returns `true` if the log matches the filter's topics pub fn filter_topics(&self, log: &Log) -> bool { - let mut out: bool = true; - for topic in self.flat_topics.iter().cloned() { - match topic { - ValueOrArray::Value(single) => { - if let Some(single) = single { - if !log.topics.starts_with(&[single]) { - out = false; - } - } - } - ValueOrArray::Array(multi) => { - if multi.is_empty() { - out = true; - continue - } - // Shrink the topics until the last item is Some. - let mut new_multi = multi; - while new_multi.iter().last().unwrap_or(&Some(H256::default())).is_none() { - new_multi.pop(); - } - // We can discard right away any logs with lesser topics than the filter. - if new_multi.len() > log.topics.len() { - out = false; - break - } - let replaced: Option> = - self.replace(log, ValueOrArray::Array(new_multi)); - if let Some(replaced) = replaced { - out = false; - if log.topics.starts_with(&replaced[..]) { - out = true; - break - } + let topics = match self.filter.as_ref() { + None => return true, + Some(f) => &f.topics, + }; + for topic_tuple in topics.iter().zip_longest(log.topics.iter()) { + match topic_tuple { + // We exhausted the `log.topics`, so if there's a filter set for + // this topic index, there is no match. Otherwise (empty filter), continue. + Left(filter_topic) => { + if !filter_topic.is_empty() { + return false } } - } - } - out - } -} - -fn topics_to_bloom_filter(topics: &ValueOrArray>) -> BloomFilter { - let mut blooms = BloomFilter::new(); - match topics { - ValueOrArray::Value(topic) => { - if let Some(topic) = topic { - let bloom: Bloom = Input::Raw(topic.as_ref()).into(); - blooms.push(Some(bloom)); - } else { - blooms.push(None); - } - } - ValueOrArray::Array(topics) => { - if topics.is_empty() { - blooms.push(None); - } else { - for topic in topics.iter() { - if let Some(topic) = topic { - let bloom: Bloom = Input::Raw(topic.as_ref()).into(); - blooms.push(Some(bloom)); - } else { - blooms.push(None); + // We exhausted the filter topics, therefore any subsequent log topic + // will match. + Right(_) => return true, + // Check that `log_topic` is included in `filter_topic` + Both(filter_topic, log_topic) => { + if !filter_topic.matches(log_topic) { + return false } } } } + true } - blooms -} - -fn address_to_bloom_filter(address: &ValueOrArray
) -> BloomFilter { - let mut blooms = BloomFilter::new(); - match address { - ValueOrArray::Value(address) => { - let bloom: Bloom = Input::Raw(address.as_ref()).into(); - blooms.push(Some(bloom)) - } - ValueOrArray::Array(addresses) => { - if addresses.is_empty() { - blooms.push(None); - } else { - for address in addresses.iter() { - let bloom: Bloom = Input::Raw(address.as_ref()).into(); - blooms.push(Some(bloom)); - } - } - } - } - blooms } /// Response of the `eth_getFilterChanges` RPC. @@ -975,7 +873,7 @@ impl<'de> Deserialize<'de> for FilterChanges { } } -/// Owned equivalent of [SubscriptionId] +/// Owned equivalent of a `SubscriptionId` #[derive(Debug, PartialEq, Clone, Hash, Eq, Deserialize, Serialize)] #[serde(deny_unknown_fields)] #[serde(untagged)] @@ -986,20 +884,22 @@ pub enum FilterId { Str(String), } -impl From for SubscriptionId<'_> { +#[cfg(feature = "jsonrpsee-types")] +impl From for jsonrpsee_types::SubscriptionId<'_> { fn from(value: FilterId) -> Self { match value { - FilterId::Num(n) => SubscriptionId::Num(n), - FilterId::Str(s) => SubscriptionId::Str(s.into()), + FilterId::Num(n) => jsonrpsee_types::SubscriptionId::Num(n), + FilterId::Str(s) => jsonrpsee_types::SubscriptionId::Str(s.into()), } } } -impl From> for FilterId { - fn from(value: SubscriptionId<'_>) -> Self { +#[cfg(feature = "jsonrpsee-types")] +impl From> for FilterId { + fn from(value: jsonrpsee_types::SubscriptionId<'_>) -> Self { match value { - SubscriptionId::Num(n) => FilterId::Num(n), - SubscriptionId::Str(s) => FilterId::Str(s.into_owned()), + jsonrpsee_types::SubscriptionId::Num(n) => FilterId::Num(n), + jsonrpsee_types::SubscriptionId::Str(s) => FilterId::Str(s.into_owned()), } } } @@ -1007,6 +907,7 @@ impl From> for FilterId { #[cfg(test)] mod tests { use super::*; + use reth_primitives::U256; use serde_json::json; fn serialize(t: &T) -> serde_json::Value { @@ -1020,40 +921,36 @@ mod tests { similar_asserts::assert_eq!( filter.topics, [ - Some(ValueOrArray::Array(vec![Some( - "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925" - .parse() - .unwrap() - ),])), - Some(ValueOrArray::Array(vec![])), - Some(ValueOrArray::Array(vec![Some( - "0x0000000000000000000000000c17e776cd218252adfca8d4e761d3fe757e9778" - .parse() - .unwrap() - )])), - None + "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925" + .parse::() + .unwrap() + .into(), + Default::default(), + "0x0000000000000000000000000c17e776cd218252adfca8d4e761d3fe757e9778" + .parse::() + .unwrap() + .into(), + Default::default(), ] ); + } - let filtered_params = FilteredParams::new(Some(filter)); - let topics = filtered_params.flat_topics; - assert_eq!( - topics, - vec![ValueOrArray::Array(vec![ - Some( - "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925" - .parse() - .unwrap() - ), - None, - Some( - "0x0000000000000000000000000c17e776cd218252adfca8d4e761d3fe757e9778" - .parse() - .unwrap() - ), - None - ])] - ) + #[test] + fn test_filter_topics_middle_wildcard() { + let s = r#"{"fromBlock": "0xfc359e", "toBlock": "0xfc359e", "topics": [["0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"], [], [null, "0x0000000000000000000000000c17e776cd218252adfca8d4e761d3fe757e9778"]]}"#; + let filter = serde_json::from_str::(s).unwrap(); + similar_asserts::assert_eq!( + filter.topics, + [ + "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925" + .parse::() + .unwrap() + .into(), + Default::default(), + Default::default(), + Default::default(), + ] + ); } #[test] @@ -1076,11 +973,13 @@ mod tests { #[test] fn filter_serialization_test() { - let t1 = "9729a6fbefefc8f6005933898b13dc45c3a2c8b7".parse::
().unwrap(); + let t1 = "0000000000000000000000009729a6fbefefc8f6005933898b13dc45c3a2c8b7" + .parse::() + .unwrap(); let t2 = H256::from([0; 32]); let t3 = U256::from(123); - let t1_padded = H256::from(t1); + let t1_padded = t1; let t3_padded = H256::from({ let mut x = [0; 32]; x[31] = 123; @@ -1143,24 +1042,17 @@ mod tests { block_bloom } - fn topic_filter( - topic1: H256, - topic2: H256, - topic3: H256, - ) -> (Filter, Option>>>) { - let filter = Filter { + fn topic_filter(topic1: H256, topic2: H256, topic3: H256) -> Filter { + Filter { block_option: Default::default(), - address: None, + address: Default::default(), topics: [ - Some(ValueOrArray::Value(Some(topic1))), - Some(ValueOrArray::Array(vec![Some(topic2), Some(topic3)])), - None, - None, + topic1.into(), + vec![topic2, topic3].into(), + Default::default(), + Default::default(), ], - }; - let filtered_params = FilteredParams::new(Some(filter.clone())); - - (filter, Some(filtered_params.flat_topics)) + } } #[test] @@ -1169,7 +1061,7 @@ mod tests { let topic2 = H256::random(); let topic3 = H256::random(); - let (_, topics) = topic_filter(topic1, topic2, topic3); + let topics = topic_filter(topic1, topic2, topic3).topics; let topics_bloom = FilteredParams::topics_filter(&topics); assert!(!FilteredParams::matches_topics( build_bloom(Address::random(), H256::random(), H256::random()), @@ -1183,7 +1075,7 @@ mod tests { let topic2 = H256::random(); let topic3 = H256::random(); - let (_, topics) = topic_filter(topic1, topic2, topic3); + let topics = topic_filter(topic1, topic2, topic3).topics; let _topics_bloom = FilteredParams::topics_filter(&topics); let topics_bloom = FilteredParams::topics_filter(&topics); @@ -1195,11 +1087,12 @@ mod tests { #[test] fn can_match_empty_topics() { - let filter = - Filter { block_option: Default::default(), address: None, topics: Default::default() }; - - let filtered_params = FilteredParams::new(Some(filter)); - let topics = Some(filtered_params.flat_topics); + let filter = Filter { + block_option: Default::default(), + address: Default::default(), + topics: Default::default(), + }; + let topics = filter.topics; let topics_bloom = FilteredParams::topics_filter(&topics); assert!(FilteredParams::matches_topics( @@ -1217,16 +1110,16 @@ mod tests { let filter = Filter { block_option: Default::default(), - address: Some(ValueOrArray::Value(rng_address)), + address: rng_address.into(), topics: [ - Some(ValueOrArray::Value(Some(topic1))), - Some(ValueOrArray::Array(vec![Some(topic2), Some(topic3)])), - None, - None, + topic1.into(), + vec![topic2, topic3].into(), + Default::default(), + Default::default(), ], }; - let filtered_params = FilteredParams::new(Some(filter.clone())); - let topics = Some(filtered_params.flat_topics); + let topics = filter.topics; + let address_filter = FilteredParams::address_filter(&filter.address); let topics_filter = FilteredParams::topics_filter(&topics); assert!( @@ -1248,11 +1141,16 @@ mod tests { let filter = Filter { block_option: Default::default(), - address: None, - topics: [None, Some(ValueOrArray::Array(vec![Some(topic2), Some(topic3)])), None, None], + address: Default::default(), + topics: [ + Default::default(), + vec![topic2, topic3].into(), + Default::default(), + Default::default(), + ], }; - let filtered_params = FilteredParams::new(Some(filter)); - let topics = Some(filtered_params.flat_topics); + let topics = filter.topics; + let topics_bloom = FilteredParams::topics_filter(&topics); assert!(FilteredParams::matches_topics( build_bloom(Address::random(), topic1, topic2), @@ -1264,16 +1162,16 @@ mod tests { fn can_match_topics_wildcard_mismatch() { let filter = Filter { block_option: Default::default(), - address: None, + address: Default::default(), topics: [ - None, - Some(ValueOrArray::Array(vec![Some(H256::random()), Some(H256::random())])), - None, - None, + Default::default(), + vec![H256::random(), H256::random()].into(), + Default::default(), + Default::default(), ], }; - let filtered_params = FilteredParams::new(Some(filter)); - let topics_input = Some(filtered_params.flat_topics); + let topics_input = filter.topics; + let topics_bloom = FilteredParams::topics_filter(&topics_input); assert!(!FilteredParams::matches_topics( build_bloom(Address::random(), H256::random(), H256::random()), @@ -1286,7 +1184,7 @@ mod tests { let rng_address = Address::random(); let filter = Filter { block_option: Default::default(), - address: Some(ValueOrArray::Value(rng_address)), + address: rng_address.into(), topics: Default::default(), }; let address_bloom = FilteredParams::address_filter(&filter.address); @@ -1302,7 +1200,7 @@ mod tests { let rng_address = Address::random(); let filter = Filter { block_option: Default::default(), - address: Some(ValueOrArray::Value(rng_address)), + address: rng_address.into(), topics: Default::default(), }; let address_bloom = FilteredParams::address_filter(&filter.address); @@ -1335,26 +1233,24 @@ mod tests { from_block: Some(4365627u64.into()), to_block: Some(4365627u64.into()), }, - address: Some(ValueOrArray::Value( - "0xb59f67a8bff5d8cd03f6ac17265c550ed8f33907".parse().unwrap() - )), + address: "0xb59f67a8bff5d8cd03f6ac17265c550ed8f33907" + .parse::
() + .unwrap() + .into(), topics: [ - Some(ValueOrArray::Value(Some( - "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" - .parse() - .unwrap(), - ))), - Some(ValueOrArray::Value(Some( - "0x00000000000000000000000000b46c2526e227482e2ebb8f4c69e4674d262e75" - .parse() - .unwrap(), - ))), - Some(ValueOrArray::Value(Some( - "0x00000000000000000000000054a2d42a40f51259dedd1978f6c118a0f0eff078" - .parse() - .unwrap(), - ))), - None, + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" + .parse::() + .unwrap() + .into(), + "0x00000000000000000000000000b46c2526e227482e2ebb8f4c69e4674d262e75" + .parse::() + .unwrap() + .into(), + "0x00000000000000000000000054a2d42a40f51259dedd1978f6c118a0f0eff078" + .parse::() + .unwrap() + .into(), + Default::default(), ], } ); @@ -1379,8 +1275,8 @@ mod tests { from_block: Some(4365627u64.into()), to_block: Some(4365627u64.into()), }, - address: None, - topics: [None, None, None, None,], + address: Default::default(), + topics: Default::default(), } ); } diff --git a/crates/rpc/rpc-types/src/eth/pubsub.rs b/crates/rpc/rpc-types/src/eth/pubsub.rs index d54f6f849a8..bf2b3b55d44 100644 --- a/crates/rpc/rpc-types/src/eth/pubsub.rs +++ b/crates/rpc/rpc-types/src/eth/pubsub.rs @@ -97,7 +97,7 @@ pub enum SubscriptionKind { } /// Any additional parameters for a subscription. -#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] +#[derive(Debug, Clone, PartialEq, Eq, Default)] pub enum Params { /// No parameters passed. #[default] diff --git a/crates/rpc/rpc-types/src/eth/trace/geth/call.rs b/crates/rpc/rpc-types/src/eth/trace/geth/call.rs index 99085208f14..d131f0f6a32 100644 --- a/crates/rpc/rpc-types/src/eth/trace/geth/call.rs +++ b/crates/rpc/rpc-types/src/eth/trace/geth/call.rs @@ -16,7 +16,7 @@ pub struct CallFrame { pub output: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub error: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(default, rename = "revertReason", skip_serializing_if = "Option::is_none")] pub revert_reason: Option, #[serde(default, skip_serializing_if = "Vec::is_empty")] pub calls: Vec, diff --git a/crates/rpc/rpc-types/src/eth/trace/geth/pre_state.rs b/crates/rpc/rpc-types/src/eth/trace/geth/pre_state.rs index b6301da3f2e..2f8be5ab36b 100644 --- a/crates/rpc/rpc-types/src/eth/trace/geth/pre_state.rs +++ b/crates/rpc/rpc-types/src/eth/trace/geth/pre_state.rs @@ -1,4 +1,4 @@ -use reth_primitives::{serde_helper::num::from_int_or_hex_opt, Address, H256, U256}; +use reth_primitives::{serde_helper::num::from_int_or_hex_opt, Address, Bytes, H256, U256}; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; @@ -29,7 +29,7 @@ pub struct AccountState { )] pub balance: Option, #[serde(default, skip_serializing_if = "Option::is_none")] - pub code: Option, + pub code: Option, #[serde( default, deserialize_with = "from_int_or_hex_opt", @@ -47,6 +47,12 @@ pub struct PreStateConfig { pub diff_mode: Option, } +impl PreStateConfig { + pub fn is_diff_mode(&self) -> bool { + self.diff_mode.unwrap_or_default() + } +} + #[cfg(test)] mod tests { use super::*; @@ -86,4 +92,11 @@ mod tests { _ => unreachable!(), } } + + #[test] + fn test_is_diff_mode() { + assert!(PreStateConfig { diff_mode: Some(true) }.is_diff_mode()); + assert!(!PreStateConfig { diff_mode: Some(false) }.is_diff_mode()); + assert!(!PreStateConfig { diff_mode: None }.is_diff_mode()); + } } diff --git a/crates/rpc/rpc-types/src/eth/trace/parity.rs b/crates/rpc/rpc-types/src/eth/trace/parity.rs index f3fa4eb9b1f..f99f97de674 100644 --- a/crates/rpc/rpc-types/src/eth/trace/parity.rs +++ b/crates/rpc/rpc-types/src/eth/trace/parity.rs @@ -308,16 +308,16 @@ pub struct VmExecutedOperation { /// The total gas used. pub used: u64, /// The stack item placed, if any. - pub push: Option, + pub push: Vec, /// If altered, the memory delta. pub mem: Option, /// The altered storage value, if any. pub store: Option, } +/// A diff of some chunk of memory. #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -/// A diff of some chunk of memory. pub struct MemoryDelta { /// Offset into memory the change begins. pub off: usize, @@ -325,6 +325,7 @@ pub struct MemoryDelta { pub data: Bytes, } +/// A diff of some storage value. #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct StorageDelta { diff --git a/crates/rpc/rpc-types/src/eth/transaction/mod.rs b/crates/rpc/rpc-types/src/eth/transaction/mod.rs index 24005376c82..9482fe64490 100644 --- a/crates/rpc/rpc-types/src/eth/transaction/mod.rs +++ b/crates/rpc/rpc-types/src/eth/transaction/mod.rs @@ -112,7 +112,7 @@ impl Transaction { let (gas_price, max_fee_per_gas) = match signed_tx.tx_type() { TxType::Legacy => (Some(U128::from(signed_tx.max_fee_per_gas())), None), TxType::EIP2930 => (Some(U128::from(signed_tx.max_fee_per_gas())), None), - TxType::EIP1559 => { + TxType::EIP1559 | TxType::EIP4844 => { // the gas price field for EIP1559 is set to `min(tip, gasFeeCap - baseFee) + // baseFee` let gas_price = base_fee @@ -123,10 +123,14 @@ impl Transaction { (Some(U128::from(gas_price)), Some(U128::from(signed_tx.max_fee_per_gas()))) } + #[cfg(feature = "optimism")] + TxType::DEPOSIT => (None, None), }; let chain_id = signed_tx.chain_id().map(U64::from); let access_list = match &signed_tx.transaction { + #[cfg(feature = "optimism")] + PrimitiveTransaction::Deposit(_) => None, PrimitiveTransaction::Legacy(_) => None, PrimitiveTransaction::Eip2930(tx) => Some( tx.access_list diff --git a/crates/rpc/rpc/Cargo.toml b/crates/rpc/rpc/Cargo.toml index bfb9421f92d..662f7579bbf 100644 --- a/crates/rpc/rpc/Cargo.toml +++ b/crates/rpc/rpc/Cargo.toml @@ -35,7 +35,7 @@ ethers-core = { workspace = true, features = ["eip712"] } revm-primitives = { workspace = true, features = ["serde"] } # rpc -jsonrpsee = { version = "0.18" } +jsonrpsee.workspace = true http = "0.2.8" http-body = "0.4.5" hyper = "0.14.24" @@ -48,6 +48,7 @@ tower = "0.4" tokio-stream = { workspace = true, features = ["sync"] } tokio-util = "0.7" pin-project.workspace = true +rayon.workspace = true bytes.workspace = true secp256k1 = { workspace = true, features = ["global-context", "rand-std", "recovery"] } @@ -62,7 +63,10 @@ schnellru = "0.2" futures.workspace = true [dev-dependencies] -jsonrpsee = { version = "0.18", features = ["client"] } +jsonrpsee = { workspace = true, features = ["client"] } assert_matches = "1.5.0" tempfile = "3.5.0" reth-interfaces = { workspace = true, features = ["test-utils"] } + +[features] +optimism = ["reth-primitives/optimism"] diff --git a/crates/rpc/rpc/src/call_guard.rs b/crates/rpc/rpc/src/call_guard.rs deleted file mode 100644 index bec4ed3ca98..00000000000 --- a/crates/rpc/rpc/src/call_guard.rs +++ /dev/null @@ -1,26 +0,0 @@ -use std::sync::Arc; -use tokio::sync::{AcquireError, OwnedSemaphorePermit, Semaphore}; - -/// RPC Tracing call guard semaphore. -/// -/// This is used to restrict the number of concurrent RPC requests to tracing methods like -/// `debug_traceTransaction` because they can consume a lot of memory and CPU. -#[derive(Clone, Debug)] -pub struct TracingCallGuard(Arc); - -impl TracingCallGuard { - /// Create a new `TracingCallGuard` with the given maximum number of tracing calls in parallel. - pub fn new(max_tracing_requests: u32) -> Self { - Self(Arc::new(Semaphore::new(max_tracing_requests as usize))) - } - - /// See also [Semaphore::acquire_owned] - pub async fn acquire_owned(self) -> Result { - self.0.acquire_owned().await - } - - /// See also [Semaphore::acquire_many_owned] - pub async fn acquire_many_owned(self, n: u32) -> Result { - self.0.acquire_many_owned(n).await - } -} diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index d6abad21fe2..3fd5bdef06b 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -2,7 +2,7 @@ use crate::{ eth::{ error::{EthApiError, EthResult}, revm_utils::{ - clone_into_empty_db, inspect, prepare_call_env, replay_transactions_until, + clone_into_empty_db, inspect, inspect_and_return_db, replay_transactions_until, result_output, EvmOverrides, }, EthTransactions, TransactionSource, @@ -40,8 +40,8 @@ use revm_primitives::{ db::{DatabaseCommit, DatabaseRef}, BlockEnv, CfgEnv, }; -use std::{future::Future, sync::Arc}; -use tokio::sync::{mpsc, oneshot, AcquireError, OwnedSemaphorePermit}; +use std::sync::Arc; +use tokio::sync::{mpsc, AcquireError, OwnedSemaphorePermit}; use tokio_stream::{wrappers::ReceiverStream, StreamExt}; /// `debug` API implementation. @@ -74,30 +74,13 @@ where Provider: BlockReaderIdExt + HeaderProvider + 'static, Eth: EthTransactions + 'static, { - /// Executes the future on a new blocking task. - async fn on_blocking_task(&self, c: C) -> EthResult - where - C: FnOnce(Self) -> F, - F: Future> + Send + 'static, - R: Send + 'static, - { - let (tx, rx) = oneshot::channel(); - let this = self.clone(); - let f = c(this); - self.inner.task_spawner.spawn_blocking(Box::pin(async move { - let res = f.await; - let _ = tx.send(res); - })); - rx.await.map_err(|_| EthApiError::InternalTracingError)? - } - /// Acquires a permit to execute a tracing call. async fn acquire_trace_permit(&self) -> Result { self.inner.tracing_call_guard.clone().acquire_owned().await } - /// Trace the entire block - fn trace_block_with_sync( + /// Trace the entire block asynchronously + async fn trace_block_with( &self, at: BlockId, transactions: Vec, @@ -107,43 +90,31 @@ where ) -> EthResult> { // replay all transactions of the block let this = self.clone(); - self.inner.eth_api.with_state_at_block(at, move |state| { - let mut results = Vec::with_capacity(transactions.len()); - let mut db = SubState::new(State::new(state)); - - let mut transactions = transactions.into_iter().peekable(); - while let Some(tx) = transactions.next() { - let tx = tx.into_ecrecovered().ok_or(BlockError::InvalidSignature)?; - let tx = tx_env_with_recovered(&tx); - let env = Env { cfg: cfg.clone(), block: block_env.clone(), tx }; - let (result, state_changes) = - this.trace_transaction(opts.clone(), env, at, &mut db)?; - results.push(TraceResult::Success { result }); - - if transactions.peek().is_some() { - // need to apply the state changes of this transaction before executing the next - // transaction - db.commit(state_changes) - } - } + self.inner + .eth_api + .spawn_with_state_at_block(at, move |state| { + let mut results = Vec::with_capacity(transactions.len()); + let mut db = SubState::new(State::new(state)); - Ok(results) - }) - } + let mut transactions = transactions.into_iter().peekable(); + while let Some(tx) = transactions.next() { + let tx = tx.into_ecrecovered().ok_or(BlockError::InvalidSignature)?; + let tx = tx_env_with_recovered(&tx); + let env = Env { cfg: cfg.clone(), block: block_env.clone(), tx }; + let (result, state_changes) = + this.trace_transaction(opts.clone(), env, at, &mut db)?; + results.push(TraceResult::Success { result }); + + if transactions.peek().is_some() { + // need to apply the state changes of this transaction before executing the + // next transaction + db.commit(state_changes) + } + } - /// Trace the entire block asynchronously - async fn trace_block_with( - &self, - at: BlockId, - transactions: Vec, - cfg: CfgEnv, - block_env: BlockEnv, - opts: GethDebugTracingOptions, - ) -> EthResult> { - self.on_blocking_task(|this| async move { - this.trace_block_with_sync(at, transactions, cfg, block_env, opts) - }) - .await + Ok(results) + }) + .await } /// Replays the given block and returns the trace of each transaction. @@ -171,17 +142,6 @@ where &self, block_id: BlockId, opts: GethDebugTracingOptions, - ) -> EthResult> { - self.on_blocking_task( - |this| async move { this.try_debug_trace_block(block_id, opts).await }, - ) - .await - } - - async fn try_debug_trace_block( - &self, - block_id: BlockId, - opts: GethDebugTracingOptions, ) -> EthResult> { let block_hash = self .inner @@ -199,7 +159,7 @@ where // its parent block's state let state_at = block.parent_hash; - self.trace_block_with_sync(state_at.into(), block.body, cfg, block_env, opts) + self.trace_block_with(state_at.into(), block.body, cfg, block_env, opts).await } /// Trace the transaction according to the provided options. @@ -221,8 +181,10 @@ where let state_at: BlockId = block.parent_hash.into(); let block_txs = block.body; - self.on_blocking_task(|this| async move { - this.inner.eth_api.with_state_at_block(state_at, |state| { + let this = self.clone(); + self.inner + .eth_api + .spawn_with_state_at_block(state_at, move |state| { // configure env for the target transaction let tx = transaction.into_recovered(); @@ -239,8 +201,7 @@ where let env = Env { cfg, block: block_env, tx: tx_env_with_recovered(&tx) }; this.trace_transaction(opts, env, state_at, &mut db).map(|(trace, _)| trace) }) - }) - .await + .await } /// The debug_traceCall method lets you run an `eth_call` within the context of the given block @@ -250,22 +211,6 @@ where call: CallRequest, block_id: Option, opts: GethDebugTracingCallOptions, - ) -> EthResult { - self.on_blocking_task(|this| async move { - this.try_debug_trace_call(call, block_id, opts).await - }) - .await - } - - /// The debug_traceCall method lets you run an `eth_call` within the context of the given block - /// execution using the final state of parent block as the base. - /// - /// Caution: while this is async, this may still be blocking on necessary DB io. - async fn try_debug_trace_call( - &self, - call: CallRequest, - block_id: Option, - opts: GethDebugTracingCallOptions, ) -> EthResult { let at = block_id.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest)); let GethDebugTracingCallOptions { tracing_options, state_overrides, block_overrides } = @@ -278,10 +223,13 @@ where GethDebugTracerType::BuiltInTracer(tracer) => match tracer { GethDebugBuiltInTracerType::FourByteTracer => { let mut inspector = FourByteInspector::default(); - let (_res, _) = self + let inspector = self .inner .eth_api - .inspect_call_at(call, at, overrides, &mut inspector) + .spawn_with_call_at(call, at, overrides, move |db, env| { + inspect(db, env, &mut inspector)?; + Ok(inspector) + }) .await?; return Ok(FourByteFrame::from(inspector).into()) } @@ -295,18 +243,39 @@ where .set_record_logs(call_config.with_log.unwrap_or_default()), ); - let _ = self + let frame = self .inner .eth_api - .inspect_call_at(call, at, overrides, &mut inspector) + .spawn_with_call_at(call, at, overrides, move |db, env| { + inspect(db, env, &mut inspector)?; + let frame = + inspector.into_geth_builder().geth_call_traces(call_config); + Ok(frame.into()) + }) .await?; - - let frame = inspector.into_geth_builder().geth_call_traces(call_config); - - return Ok(frame.into()) + return Ok(frame) } GethDebugBuiltInTracerType::PreStateTracer => { - Err(EthApiError::Unsupported("pre state tracer currently unsupported.")) + let prestate_config = tracer_config + .into_pre_state_config() + .map_err(|_| EthApiError::InvalidTracerConfig)?; + let mut inspector = TracingInspector::new( + TracingInspectorConfig::from_geth_config(&config), + ); + + let frame = + self.inner + .eth_api + .spawn_with_call_at(call, at, overrides, move |db, env| { + let (res, _, db) = + inspect_and_return_db(db, env, &mut inspector)?; + let frame = inspector + .into_geth_builder() + .geth_prestate_traces(&res, prestate_config, &db)?; + Ok(frame) + }) + .await?; + return Ok(frame.into()) } GethDebugBuiltInTracerType::NoopTracer => Ok(NoopFrame::default().into()), }, @@ -315,18 +284,10 @@ where // for JS tracing we need to setup all async work before we can start tracing // because JSTracer and all JS types are not Send - let (cfg, block_env, at) = self.inner.eth_api.evm_env_at(at).await?; + let (_, _, at) = self.inner.eth_api.evm_env_at(at).await?; let state = self.inner.eth_api.state_at(at)?; - let mut db = SubState::new(State::new(state)); + let db = SubState::new(State::new(state)); let has_state_overrides = overrides.has_state(); - let env = prepare_call_env( - cfg, - block_env, - call, - self.inner.eth_api.call_gas_limit(), - &mut db, - overrides, - )?; // If the caller provided state overrides we need to clone the DB so the js // service has access these modifications @@ -337,11 +298,17 @@ where let to_db_service = self.spawn_js_trace_service(at, maybe_override_db)?; - let mut inspector = JsInspector::new(code, config, to_db_service)?; - let (res, env) = inspect(db, env, &mut inspector)?; + let res = self + .inner + .eth_api + .spawn_with_call_at(call, at, overrides, move |db, env| { + let mut inspector = JsInspector::new(code, config, to_db_service)?; + let (res, _) = inspect(db, env.clone(), &mut inspector)?; + Ok(inspector.json_result(res, &env)?) + }) + .await?; - let result = inspector.json_result(res, &env)?; - Ok(GethTrace::JS(result)) + Ok(GethTrace::JS(res)) } } } @@ -351,8 +318,14 @@ where let mut inspector = TracingInspector::new(inspector_config); - let (res, _) = - self.inner.eth_api.inspect_call_at(call, at, overrides, &mut inspector).await?; + let (res, inspector) = self + .inner + .eth_api + .spawn_with_call_at(call, at, overrides, move |db, env| { + let (res, _) = inspect(db, env, &mut inspector)?; + Ok((res, inspector)) + }) + .await?; let gas_used = res.result.gas_used(); let return_value = result_output(&res.result).unwrap_or_default().into(); let frame = inspector.into_geth_builder().geth_traces(gas_used, return_value, config); @@ -365,6 +338,8 @@ where /// Returns the trace frame and the state that got updated after executing the transaction. /// /// Note: this does not apply any state overrides if they're configured in the `opts`. + /// + /// Caution: this is blocking and should be performed on a blocking task. fn trace_transaction( &self, opts: GethDebugTracingOptions, @@ -399,7 +374,22 @@ where return Ok((frame.into(), res.state)) } GethDebugBuiltInTracerType::PreStateTracer => { - Err(EthApiError::Unsupported("prestate tracer is unimplemented yet.")) + let prestate_config = tracer_config + .into_pre_state_config() + .map_err(|_| EthApiError::InvalidTracerConfig)?; + + let mut inspector = TracingInspector::new( + TracingInspectorConfig::from_geth_config(&config), + ); + let (res, _) = inspect(&mut *db, env, &mut inspector)?; + + let frame = inspector.into_geth_builder().geth_prestate_traces( + &res, + prestate_config, + &*db, + )?; + + return Ok((frame.into(), res.state)) } GethDebugBuiltInTracerType::NoopTracer => { Ok((NoopFrame::default().into(), Default::default())) diff --git a/crates/rpc/rpc/src/eth/api/mod.rs b/crates/rpc/rpc/src/eth/api/mod.rs index de5d12f8d10..20ecf114e2a 100644 --- a/crates/rpc/rpc/src/eth/api/mod.rs +++ b/crates/rpc/rpc/src/eth/api/mod.rs @@ -37,6 +37,7 @@ mod sign; mod state; mod transactions; +use crate::TracingCallPool; pub use transactions::{EthTransactions, TransactionSource}; /// `Eth` API trait. @@ -88,6 +89,7 @@ where eth_cache: EthStateCache, gas_oracle: GasPriceOracle, gas_cap: impl Into, + tracing_call_pool: TracingCallPool, ) -> Self { Self::with_spawner( provider, @@ -97,10 +99,12 @@ where gas_oracle, gas_cap.into().into(), Box::::default(), + tracing_call_pool, ) } /// Creates a new, shareable instance. + #[allow(clippy::too_many_arguments)] pub fn with_spawner( provider: Provider, pool: Pool, @@ -109,6 +113,7 @@ where gas_oracle: GasPriceOracle, gas_cap: u64, task_spawner: Box, + tracing_call_pool: TracingCallPool, ) -> Self { // get the block number of the latest block let latest_block = provider @@ -129,6 +134,7 @@ where starting_block: U256::from(latest_block), task_spawner, pending_block: Default::default(), + tracing_call_pool, }; Self { inner: Arc::new(inner) } } @@ -421,4 +427,6 @@ struct EthApiInner { task_spawner: Box, /// Cached pending block if any pending_block: Mutex>, + /// A pool dedicated to tracing calls + tracing_call_pool: TracingCallPool, } diff --git a/crates/rpc/rpc/src/eth/api/pending_block.rs b/crates/rpc/rpc/src/eth/api/pending_block.rs index b5b8bc89c41..7631351b32f 100644 --- a/crates/rpc/rpc/src/eth/api/pending_block.rs +++ b/crates/rpc/rpc/src/eth/api/pending_block.rs @@ -110,6 +110,8 @@ impl PendingBlockEnv { success: result.is_success(), cumulative_gas_used, logs: result.logs().into_iter().map(into_reth_log).collect(), + #[cfg(feature = "optimism")] + deposit_nonce: None, }, ); // append transaction to the list of executed transactions @@ -143,6 +145,8 @@ impl PendingBlockEnv { difficulty: U256::ZERO, gas_used: cumulative_gas_used, extra_data: Default::default(), + blob_gas_used: None, + excess_blob_gas: None, }; // seal the block diff --git a/crates/rpc/rpc/src/eth/api/server.rs b/crates/rpc/rpc/src/eth/api/server.rs index 1cca7addc90..663308cd895 100644 --- a/crates/rpc/rpc/src/eth/api/server.rs +++ b/crates/rpc/rpc/src/eth/api/server.rs @@ -392,7 +392,7 @@ where mod tests { use crate::{ eth::{cache::EthStateCache, gas_oracle::GasPriceOracle}, - EthApi, + EthApi, TracingCallPool, }; use jsonrpsee::types::error::INVALID_PARAMS_CODE; use reth_interfaces::test_utils::{generators, generators::Rng}; @@ -428,6 +428,7 @@ mod tests { cache.clone(), GasPriceOracle::new(provider, Default::default(), cache), ETHEREUM_BLOCK_GAS_LIMIT, + TracingCallPool::build().expect("failed to build tracing pool"), ) } diff --git a/crates/rpc/rpc/src/eth/api/state.rs b/crates/rpc/rpc/src/eth/api/state.rs index 0930bf0b6c5..2887ac58fb8 100644 --- a/crates/rpc/rpc/src/eth/api/state.rs +++ b/crates/rpc/rpc/src/eth/api/state.rs @@ -146,7 +146,10 @@ where #[cfg(test)] mod tests { use super::*; - use crate::eth::{cache::EthStateCache, gas_oracle::GasPriceOracle}; + use crate::{ + eth::{cache::EthStateCache, gas_oracle::GasPriceOracle}, + TracingCallPool, + }; use reth_primitives::{constants::ETHEREUM_BLOCK_GAS_LIMIT, StorageKey, StorageValue}; use reth_provider::test_utils::{ExtendedAccount, MockEthProvider, NoopProvider}; use reth_transaction_pool::test_utils::testing_pool; @@ -165,6 +168,7 @@ mod tests { cache.clone(), GasPriceOracle::new(NoopProvider::default(), Default::default(), cache), ETHEREUM_BLOCK_GAS_LIMIT, + TracingCallPool::build().expect("failed to build tracing pool"), ); let address = Address::random(); let storage = eth_api.storage_at(address, U256::ZERO.into(), None).unwrap(); @@ -186,6 +190,7 @@ mod tests { cache.clone(), GasPriceOracle::new(mock_provider, Default::default(), cache), ETHEREUM_BLOCK_GAS_LIMIT, + TracingCallPool::build().expect("failed to build tracing pool"), ); let storage_key: U256 = storage_key.into(); diff --git a/crates/rpc/rpc/src/eth/api/transactions.rs b/crates/rpc/rpc/src/eth/api/transactions.rs index 514d436b3ab..6d869daa604 100644 --- a/crates/rpc/rpc/src/eth/api/transactions.rs +++ b/crates/rpc/rpc/src/eth/api/transactions.rs @@ -40,7 +40,10 @@ use revm_primitives::{utilities::create_address, Env, ResultAndState, SpecId}; /// Helper alias type for the state's [CacheDB] pub(crate) type StateCacheDB<'r> = CacheDB>>; -/// Commonly used transaction related functions for the [EthApi] type in the `eth_` namespace +/// Commonly used transaction related functions for the [EthApi] type in the `eth_` namespace. +/// +/// Async functions that are spawned onto the +/// [TracingCallPool](crate::tracing_call::TracingCallPool) begin with `spawn_` #[async_trait::async_trait] pub trait EthTransactions: Send + Sync { /// Returns default gas limit to use for `eth_call` and tracing RPC methods. @@ -54,6 +57,12 @@ pub trait EthTransactions: Send + Sync { where F: FnOnce(StateProviderBox<'_>) -> EthResult; + /// Executes the closure with the state that corresponds to the given [BlockId] on a new task + async fn spawn_with_state_at_block(&self, at: BlockId, f: F) -> EthResult + where + F: FnOnce(StateProviderBox<'_>) -> EthResult + Send + 'static, + T: Send + 'static; + /// Returns the revm evm env for the requested [BlockId] /// /// If the [BlockId] this will return the [BlockId::Hash] of the block the env was configured @@ -121,8 +130,8 @@ pub trait EthTransactions: Send + Sync { async fn send_transaction(&self, request: TransactionRequest) -> EthResult; /// Prepares the state and env for the given [CallRequest] at the given [BlockId] and executes - /// the closure. - async fn with_call_at( + /// the closure on a new task returning the result of the closure. + async fn spawn_with_call_at( &self, request: CallRequest, at: BlockId, @@ -130,7 +139,8 @@ pub trait EthTransactions: Send + Sync { f: F, ) -> EthResult where - F: for<'r> FnOnce(StateCacheDB<'r>, Env) -> EthResult + Send; + F: for<'r> FnOnce(StateCacheDB<'r>, Env) -> EthResult + Send + 'static, + R: Send + 'static; /// Executes the call request at the given [BlockId]. async fn transact_call_at( @@ -140,8 +150,9 @@ pub trait EthTransactions: Send + Sync { overrides: EvmOverrides, ) -> EthResult<(ResultAndState, Env)>; - /// Executes the call request at the given [BlockId] - async fn inspect_call_at( + /// Executes the call request at the given [BlockId] on a new task and returns the result of the + /// inspect call. + async fn spawn_inspect_call_at( &self, request: CallRequest, at: BlockId, @@ -149,24 +160,15 @@ pub trait EthTransactions: Send + Sync { inspector: I, ) -> EthResult<(ResultAndState, Env)> where - I: for<'r> Inspector> + Send; - - /// Executes the call request at the given [BlockId] - async fn inspect_call_at_and_return_state<'a, I>( - &'a self, - request: CallRequest, - at: BlockId, - overrides: EvmOverrides, - inspector: I, - ) -> EthResult<(ResultAndState, Env, StateCacheDB<'a>)> - where - I: Inspector> + Send; + I: for<'r> Inspector> + Send + 'static; /// Executes the transaction on top of the given [BlockId] with a tracer configured by the /// config. /// /// The callback is then called with the [TracingInspector] and the [ResultAndState] after the /// configured [Env] was inspected. + /// + /// Caution: this is blocking fn trace_at( &self, env: Env, @@ -184,7 +186,7 @@ pub trait EthTransactions: Send + Sync { /// /// The callback is then called with the [TracingInspector] and the [ResultAndState] after the /// configured [Env] was inspected. - fn trace_at_with_state( + async fn spawn_trace_at_with_state( &self, env: Env, config: TracingInspectorConfig, @@ -192,7 +194,10 @@ pub trait EthTransactions: Send + Sync { f: F, ) -> EthResult where - F: for<'a> FnOnce(TracingInspector, ResultAndState, StateCacheDB<'a>) -> EthResult; + F: for<'a> FnOnce(TracingInspector, ResultAndState, StateCacheDB<'a>) -> EthResult + + Send + + 'static, + R: Send + 'static; /// Fetches the transaction and the transaction's block async fn transaction_and_block( @@ -206,7 +211,10 @@ pub trait EthTransactions: Send + Sync { /// state by executing them first. /// The callback `f` is invoked with the [ResultAndState] after the transaction was executed and /// the database that points to the beginning of the transaction. - async fn trace_transaction_in_block( + /// + /// Note: Implementers should use a threadpool where blocking is allowed, such as + /// [TracingCallPool](crate::tracing_call::TracingCallPool). + async fn spawn_trace_transaction_in_block( &self, hash: H256, config: TracingInspectorConfig, @@ -219,7 +227,9 @@ pub trait EthTransactions: Send + Sync { ResultAndState, StateCacheDB<'a>, ) -> EthResult - + Send; + + Send + + 'static, + R: Send + 'static; } #[async_trait] @@ -245,6 +255,22 @@ where f(state) } + async fn spawn_with_state_at_block(&self, at: BlockId, f: F) -> EthResult + where + F: FnOnce(StateProviderBox<'_>) -> EthResult + Send + 'static, + T: Send + 'static, + { + let this = self.clone(); + self.inner + .tracing_call_pool + .spawn(move || { + let state = this.state_at(at)?; + f(state) + }) + .await + .map_err(|_| EthApiError::InternalTracingError)? + } + async fn evm_env_at(&self, at: BlockId) -> EthResult<(CfgEnv, BlockEnv, BlockId)> { if at.is_pending() { let PendingBlockEnv { cfg, block_env, origin } = self.pending_block_env_and_cfg()?; @@ -473,7 +499,7 @@ where Ok(hash) } - async fn with_call_at( + async fn spawn_with_call_at( &self, request: CallRequest, at: BlockId, @@ -481,15 +507,29 @@ where f: F, ) -> EthResult where - F: for<'r> FnOnce(StateCacheDB<'r>, Env) -> EthResult + Send, + F: for<'r> FnOnce(StateCacheDB<'r>, Env) -> EthResult + Send + 'static, + R: Send + 'static, { let (cfg, block_env, at) = self.evm_env_at(at).await?; - let state = self.state_at(at)?; - let mut db = SubState::new(State::new(state)); - - let env = - prepare_call_env(cfg, block_env, request, self.call_gas_limit(), &mut db, overrides)?; - f(db, env) + let this = self.clone(); + self.inner + .tracing_call_pool + .spawn(move || { + let state = this.state_at(at)?; + let mut db = SubState::new(State::new(state)); + + let env = prepare_call_env( + cfg, + block_env, + request, + this.call_gas_limit(), + &mut db, + overrides, + )?; + f(db, env) + }) + .await + .map_err(|_| EthApiError::InternalTracingError)? } async fn transact_call_at( @@ -498,10 +538,11 @@ where at: BlockId, overrides: EvmOverrides, ) -> EthResult<(ResultAndState, Env)> { - self.with_call_at(request, at, overrides, |mut db, env| transact(&mut db, env)).await + self.spawn_with_call_at(request, at, overrides, move |mut db, env| transact(&mut db, env)) + .await } - async fn inspect_call_at( + async fn spawn_inspect_call_at( &self, request: CallRequest, at: BlockId, @@ -509,28 +550,10 @@ where inspector: I, ) -> EthResult<(ResultAndState, Env)> where - I: for<'r> Inspector> + Send, - { - self.with_call_at(request, at, overrides, |db, env| inspect(db, env, inspector)).await - } - - async fn inspect_call_at_and_return_state<'a, I>( - &'a self, - request: CallRequest, - at: BlockId, - overrides: EvmOverrides, - inspector: I, - ) -> EthResult<(ResultAndState, Env, StateCacheDB<'a>)> - where - I: Inspector> + Send, + I: for<'r> Inspector> + Send + 'static, { - let (cfg, block_env, at) = self.evm_env_at(at).await?; - let state = self.state_at(at)?; - let mut db = SubState::new(State::new(state)); - - let env = - prepare_call_env(cfg, block_env, request, self.call_gas_limit(), &mut db, overrides)?; - inspect_and_return_db(db, env, inspector) + self.spawn_with_call_at(request, at, overrides, move |db, env| inspect(db, env, inspector)) + .await } fn trace_at( @@ -553,7 +576,7 @@ where }) } - fn trace_at_with_state( + async fn spawn_trace_at_with_state( &self, env: Env, config: TracingInspectorConfig, @@ -561,15 +584,19 @@ where f: F, ) -> EthResult where - F: for<'a> FnOnce(TracingInspector, ResultAndState, StateCacheDB<'a>) -> EthResult, + F: for<'a> FnOnce(TracingInspector, ResultAndState, StateCacheDB<'a>) -> EthResult + + Send + + 'static, + R: Send + 'static, { - self.with_state_at_block(at, |state| { + self.spawn_with_state_at_block(at, move |state| { let db = SubState::new(State::new(state)); let mut inspector = TracingInspector::new(config); let (res, _, db) = inspect_and_return_db(db, env, &mut inspector)?; f(inspector, res, db) }) + .await } async fn transaction_and_block( @@ -590,7 +617,7 @@ where Ok(block.map(|block| (transaction, block.seal(block_hash)))) } - async fn trace_transaction_in_block( + async fn spawn_trace_transaction_in_block( &self, hash: H256, config: TracingInspectorConfig, @@ -603,7 +630,9 @@ where ResultAndState, StateCacheDB<'a>, ) -> EthResult - + Send, + + Send + + 'static, + R: Send + 'static, { let (transaction, block) = match self.transaction_and_block(hash).await? { None => return Ok(None), @@ -618,7 +647,7 @@ where let parent_block = block.parent_hash; let block_txs = block.body; - self.with_state_at_block(parent_block.into(), |state| { + self.spawn_with_state_at_block(parent_block.into(), move |state| { let mut db = SubState::new(State::new(state)); // replay all transactions prior to the targeted transaction @@ -630,6 +659,7 @@ where let (res, _, db) = inspect_and_return_db(db, env, &mut inspector)?; f(tx_info, inspector, res, db) }) + .await .map(Some) } } @@ -878,7 +908,7 @@ mod tests { use super::*; use crate::{ eth::{cache::EthStateCache, gas_oracle::GasPriceOracle}, - EthApi, + EthApi, TracingCallPool, }; use reth_network_api::noop::NoopNetwork; use reth_primitives::{constants::ETHEREUM_BLOCK_GAS_LIMIT, hex_literal::hex, Bytes}; @@ -900,6 +930,7 @@ mod tests { cache.clone(), GasPriceOracle::new(noop_provider, Default::default(), cache), ETHEREUM_BLOCK_GAS_LIMIT, + TracingCallPool::build().expect("failed to build tracing pool"), ); // https://etherscan.io/tx/0xa694b71e6c128a2ed8e2e0f6770bddbe52e3bb8f10e8472f9a79ab81497a8b5d diff --git a/crates/rpc/rpc/src/eth/filter.rs b/crates/rpc/rpc/src/eth/filter.rs index e7b39730799..280bc0817f1 100644 --- a/crates/rpc/rpc/src/eth/filter.rs +++ b/crates/rpc/rpc/src/eth/filter.rs @@ -363,11 +363,9 @@ where let mut all_logs = Vec::new(); let filter_params = FilteredParams::new(Some(filter.clone())); - let topics = filter.has_topics().then(|| filter_params.flat_topics.clone()); - // derive bloom filters from filter input let address_filter = FilteredParams::address_filter(&filter.address); - let topics_filter = FilteredParams::topics_filter(&topics); + let topics_filter = FilteredParams::topics_filter(&filter.topics); let is_multi_block_range = from_block != to_block; diff --git a/crates/rpc/rpc/src/layers/auth_layer.rs b/crates/rpc/rpc/src/layers/auth_layer.rs index b3a92de804b..0548320eb11 100644 --- a/crates/rpc/rpc/src/layers/auth_layer.rs +++ b/crates/rpc/rpc/src/layers/auth_layer.rs @@ -286,7 +286,7 @@ mod tests { let mut module = RpcModule::new(()); module.register_method("greet_melkor", |_, _| "You are the dark lord").unwrap(); - server.start(module).unwrap() + server.start(module) } fn to_u64(time: SystemTime) -> u64 { diff --git a/crates/rpc/rpc/src/lib.rs b/crates/rpc/rpc/src/lib.rs index b885608139a..e1818523f29 100644 --- a/crates/rpc/rpc/src/lib.rs +++ b/crates/rpc/rpc/src/lib.rs @@ -31,7 +31,6 @@ //! disk-io, hence these calls are spawned as futures to a blocking task manually. mod admin; -mod call_guard; mod debug; mod engine; pub mod eth; @@ -41,11 +40,11 @@ mod otterscan; mod reth; mod rpc; mod trace; +pub mod tracing_call; mod txpool; mod web3; pub use admin::AdminApi; -pub use call_guard::TracingCallGuard; pub use debug::DebugApi; pub use engine::{EngineApi, EngineEthApi}; pub use eth::{EthApi, EthApiSpec, EthFilter, EthPubSub, EthSubscriptionIdProvider}; @@ -55,6 +54,7 @@ pub use otterscan::OtterscanApi; pub use reth::RethApi; pub use rpc::RPCApi; pub use trace::TraceApi; +pub use tracing_call::{TracingCallGuard, TracingCallPool}; pub use txpool::TxPoolApi; pub use web3::Web3Api; diff --git a/crates/rpc/rpc/src/trace.rs b/crates/rpc/rpc/src/trace.rs index ff2d09e50c7..494741097c7 100644 --- a/crates/rpc/rpc/src/trace.rs +++ b/crates/rpc/rpc/src/trace.rs @@ -1,8 +1,7 @@ use crate::{ eth::{ - cache::EthStateCache, error::{EthApiError, EthResult}, - revm_utils::{inspect, prepare_call_env, EvmOverrides}, + revm_utils::{inspect, inspect_and_return_db, prepare_call_env, EvmOverrides}, utils::recover_raw_transaction, EthTransactions, }, @@ -29,11 +28,10 @@ use reth_rpc_types::{ trace::{filter::TraceFilter, parity::*}, BlockError, BlockOverrides, CallRequest, Index, TransactionInfo, }; -use reth_tasks::TaskSpawner; use revm::{db::CacheDB, primitives::Env}; use revm_primitives::{db::DatabaseCommit, ExecutionResult, ResultAndState}; -use std::{collections::HashSet, future::Future, sync::Arc}; -use tokio::sync::{oneshot, AcquireError, OwnedSemaphorePermit}; +use std::{collections::HashSet, sync::Arc}; +use tokio::sync::{AcquireError, OwnedSemaphorePermit}; /// `trace` API implementation. /// @@ -51,20 +49,8 @@ impl TraceApi { } /// Create a new instance of the [TraceApi] - pub fn new( - provider: Provider, - eth_api: Eth, - eth_cache: EthStateCache, - task_spawner: Box, - tracing_call_guard: TracingCallGuard, - ) -> Self { - let inner = Arc::new(TraceApiInner { - provider, - eth_api, - eth_cache, - task_spawner, - tracing_call_guard, - }); + pub fn new(provider: Provider, eth_api: Eth, tracing_call_guard: TracingCallGuard) -> Self { + let inner = Arc::new(TraceApiInner { provider, eth_api, tracing_call_guard }); Self { inner } } @@ -83,23 +69,6 @@ where Provider: BlockReader + StateProviderFactory + EvmEnvProvider + ChainSpecProvider + 'static, Eth: EthTransactions + 'static, { - /// Executes the future on a new blocking task. - async fn on_blocking_task(&self, c: C) -> EthResult - where - C: FnOnce(Self) -> F, - F: Future> + Send + 'static, - R: Send + 'static, - { - let (tx, rx) = oneshot::channel(); - let this = self.clone(); - let f = c(this); - self.inner.task_spawner.spawn_blocking(Box::pin(async move { - let res = f.await; - let _ = tx.send(res); - })); - rx.await.map_err(|_| EthApiError::InternalTracingError)? - } - /// Executes the given call and returns a number of possible traces for it. pub async fn trace_call( &self, @@ -108,43 +77,23 @@ where block_id: Option, state_overrides: Option, block_overrides: Option>, - ) -> EthResult { - self.on_blocking_task(|this| async move { - this.try_trace_call( - call, - trace_types, - block_id, - EvmOverrides::new(state_overrides, block_overrides), - ) - .await - }) - .await - } - - async fn try_trace_call( - &self, - call: CallRequest, - trace_types: HashSet, - block_id: Option, - overrides: EvmOverrides, ) -> EthResult { let at = block_id.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest)); let config = tracing_config(&trace_types); + let overrides = EvmOverrides::new(state_overrides, block_overrides); let mut inspector = TracingInspector::new(config); - - let (res, _, db) = self - .inner + self.inner .eth_api - .inspect_call_at_and_return_state(call, at, overrides, &mut inspector) - .await?; - - let trace_res = inspector.into_parity_builder().into_trace_results_with_state( - res, - &trace_types, - &db, - )?; - - Ok(trace_res) + .spawn_with_call_at(call, at, overrides, move |db, env| { + let (res, _, db) = inspect_and_return_db(db, env, &mut inspector)?; + let trace_res = inspector.into_parity_builder().into_trace_results_with_state( + res, + &trace_types, + &db, + )?; + Ok(trace_res) + }) + .await } /// Traces a call to `eth_sendRawTransaction` without making the call, returning the traces. @@ -166,16 +115,16 @@ where let config = tracing_config(&trace_types); - self.on_blocking_task(|this| async move { - this.inner.eth_api.trace_at_with_state(env, config, at, |inspector, res, db| { + self.inner + .eth_api + .spawn_trace_at_with_state(env, config, at, move |inspector, res, db| { Ok(inspector.into_parity_builder().into_trace_results_with_state( res, &trace_types, &db, )?) }) - }) - .await + .await } /// Performs multiple call traces on top of the same block. i.e. transaction n will be executed @@ -190,10 +139,11 @@ where let at = block_id.unwrap_or(BlockId::Number(BlockNumberOrTag::Pending)); let (cfg, block_env, at) = self.inner.eth_api.evm_env_at(at).await?; - self.on_blocking_task(|this| async move { - let gas_limit = this.inner.eth_api.call_gas_limit(); - // execute all transactions on top of each other and record the traces - this.inner.eth_api.with_state_at_block(at, move |state| { + let gas_limit = self.inner.eth_api.call_gas_limit(); + // execute all transactions on top of each other and record the traces + self.inner + .eth_api + .spawn_with_state_at_block(at, move |state| { let mut results = Vec::with_capacity(calls.len()); let mut db = SubState::new(State::new(state)); @@ -239,8 +189,7 @@ where Ok(results) }) - }) - .await + .await } /// Replays a transaction, returning the traces. @@ -250,22 +199,19 @@ where trace_types: HashSet, ) -> EthResult { let config = tracing_config(&trace_types); - self.on_blocking_task(|this| async move { - this.inner - .eth_api - .trace_transaction_in_block(hash, config, |_, inspector, res, db| { - let trace_res = inspector.into_parity_builder().into_trace_results_with_state( - res, - &trace_types, - &db, - )?; - Ok(trace_res) - }) - .await - .transpose() - .ok_or_else(|| EthApiError::TransactionNotFound)? - }) - .await + self.inner + .eth_api + .spawn_trace_transaction_in_block(hash, config, move |_, inspector, res, db| { + let trace_res = inspector.into_parity_builder().into_trace_results_with_state( + res, + &trace_types, + &db, + )?; + Ok(trace_res) + }) + .await + .transpose() + .ok_or_else(|| EthApiError::TransactionNotFound)? } /// Returns transaction trace objects at the given index @@ -308,22 +254,18 @@ where &self, hash: H256, ) -> EthResult>> { - self.on_blocking_task(|this| async move { - this.inner - .eth_api - .trace_transaction_in_block( - hash, - TracingInspectorConfig::default_parity(), - |tx_info, inspector, _, _| { - let traces = inspector - .into_parity_builder() - .into_localized_transaction_traces(tx_info); - Ok(traces) - }, - ) - .await - }) - .await + self.inner + .eth_api + .spawn_trace_transaction_in_block( + hash, + TracingInspectorConfig::default_parity(), + move |tx_info, inspector, _, _| { + let traces = + inspector.into_parity_builder().into_localized_transaction_traces(tx_info); + Ok(traces) + }, + ) + .await } /// Executes all transactions of a block and returns a list of callback results. @@ -371,48 +313,46 @@ where let block_hash = block.hash; let transactions = block.body; - self.on_blocking_task(|this| async move { - // replay all transactions of the block - this.inner - .eth_api - .with_state_at_block(state_at.into(), move |state| { - let mut results = Vec::with_capacity(transactions.len()); - let mut db = SubState::new(State::new(state)); - - let mut transactions = transactions.into_iter().enumerate().peekable(); - - while let Some((idx, tx)) = transactions.next() { - let tx = tx.into_ecrecovered().ok_or(BlockError::InvalidSignature)?; - let tx_info = TransactionInfo { - hash: Some(tx.hash()), - index: Some(idx as u64), - block_hash: Some(block_hash), - block_number: Some(block_env.number.try_into().unwrap_or(u64::MAX)), - base_fee: Some(block_env.basefee.try_into().unwrap_or(u64::MAX)), - }; - - let tx = tx_env_with_recovered(&tx); - let env = Env { cfg: cfg.clone(), block: block_env.clone(), tx }; - - let mut inspector = TracingInspector::new(config); - let (res, _) = inspect(&mut db, env, &mut inspector)?; - let ResultAndState { result, state } = res; - results.push(f(tx_info, inspector, result, &state, &db)?); - - // need to apply the state changes of this transaction before executing the - // next transaction - if transactions.peek().is_some() { - // need to apply the state changes of this transaction before executing - // the next transaction - db.commit(state) - } + // replay all transactions of the block + self.inner + .eth_api + .spawn_with_state_at_block(state_at.into(), move |state| { + let mut results = Vec::with_capacity(transactions.len()); + let mut db = SubState::new(State::new(state)); + + let mut transactions = transactions.into_iter().enumerate().peekable(); + + while let Some((idx, tx)) = transactions.next() { + let tx = tx.into_ecrecovered().ok_or(BlockError::InvalidSignature)?; + let tx_info = TransactionInfo { + hash: Some(tx.hash()), + index: Some(idx as u64), + block_hash: Some(block_hash), + block_number: Some(block_env.number.try_into().unwrap_or(u64::MAX)), + base_fee: Some(block_env.basefee.try_into().unwrap_or(u64::MAX)), + }; + + let tx = tx_env_with_recovered(&tx); + let env = Env { cfg: cfg.clone(), block: block_env.clone(), tx }; + + let mut inspector = TracingInspector::new(config); + let (res, _) = inspect(&mut db, env, &mut inspector)?; + let ResultAndState { result, state } = res; + results.push(f(tx_info, inspector, result, &state, &db)?); + + // need to apply the state changes of this transaction before executing the + // next transaction + if transactions.peek().is_some() { + // need to apply the state changes of this transaction before executing + // the next transaction + db.commit(state) } + } - Ok(results) - }) - .map(Some) - }) - .await + Ok(results) + }) + .await + .map(Some) } /// Returns traces created at given block. @@ -626,11 +566,6 @@ struct TraceApiInner { provider: Provider, /// Access to commonly used code of the `eth` namespace eth_api: Eth, - /// The async cache frontend for eth-related data - #[allow(unused)] // we need this for trace_filter eventually - eth_cache: EthStateCache, - /// The type that can spawn tasks which would otherwise be blocking. - task_spawner: Box, // restrict the number of concurrent calls to `trace_*` tracing_call_guard: TracingCallGuard, } diff --git a/crates/rpc/rpc-builder/src/tracing_pool.rs b/crates/rpc/rpc/src/tracing_call.rs similarity index 71% rename from crates/rpc/rpc-builder/src/tracing_pool.rs rename to crates/rpc/rpc/src/tracing_call.rs index dd305611780..26956ae2d50 100644 --- a/crates/rpc/rpc-builder/src/tracing_pool.rs +++ b/crates/rpc/rpc/src/tracing_call.rs @@ -8,9 +8,46 @@ use std::{ task::{ready, Context, Poll}, thread, }; -use tokio::sync::oneshot; +use tokio::sync::{oneshot, AcquireError, OwnedSemaphorePermit, Semaphore}; + +/// RPC Tracing call guard semaphore. +/// +/// This is used to restrict the number of concurrent RPC requests to tracing methods like +/// `debug_traceTransaction` because they can consume a lot of memory and CPU. +/// +/// This types serves as an entry guard for the [TracingCallPool] and is used to rate limit parallel +/// tracing calls on the pool. +#[derive(Clone, Debug)] +pub struct TracingCallGuard(Arc); + +impl TracingCallGuard { + /// Create a new `TracingCallGuard` with the given maximum number of tracing calls in parallel. + pub fn new(max_tracing_requests: u32) -> Self { + Self(Arc::new(Semaphore::new(max_tracing_requests as usize))) + } + + /// See also [Semaphore::acquire_owned] + pub async fn acquire_owned(self) -> Result { + self.0.acquire_owned().await + } + + /// See also [Semaphore::acquire_many_owned] + pub async fn acquire_many_owned(self, n: u32) -> Result { + self.0.acquire_many_owned(n).await + } +} /// Used to execute tracing calls on a rayon threadpool from within a tokio runtime. +/// +/// This is a dedicated threadpool for tracing calls which are CPU bound. +/// RPC calls that perform blocking IO (disk lookups) are not executed on this pool but on the tokio +/// runtime's blocking pool, which performs poorly with CPU bound tasks. Once the tokio blocking +/// pool is saturated it is converted into a queue, tracing calls could then interfere with the +/// queue and block other RPC calls. +/// +/// See also [tokio-docs] for more information. +/// +/// [tokio-docs]: https://docs.rs/tokio/latest/tokio/index.html#cpu-bound-tasks-and-blocking-code #[derive(Clone, Debug)] pub struct TracingCallPool { pool: Arc, diff --git a/crates/stages/Cargo.toml b/crates/stages/Cargo.toml index 9d975e28e8c..960ca15aa57 100644 --- a/crates/stages/Cargo.toml +++ b/crates/stages/Cargo.toml @@ -40,7 +40,7 @@ serde.workspace = true # misc thiserror.workspace = true aquamarine = "0.3.0" -itertools = "0.10.5" +itertools.workspace = true rayon.workspace = true num-traits = "0.2.15" @@ -56,7 +56,7 @@ reth-rlp.workspace = true reth-revm = { path = "../revm" } reth-trie = { path = "../trie", features = ["test-utils"] } -itertools = "0.10.5" +itertools.workspace = true tokio = { workspace = true, features = ["rt", "sync", "macros"] } assert_matches = "1.5.0" rand.workspace = true diff --git a/crates/stages/benches/criterion.rs b/crates/stages/benches/criterion.rs index 8fce2e37035..d9b079d2984 100644 --- a/crates/stages/benches/criterion.rs +++ b/crates/stages/benches/criterion.rs @@ -95,7 +95,7 @@ fn merkle(c: &mut Criterion) { // don't need to run each stage for that many times group.sample_size(10); - let stage = MerkleStage::Both { clean_threshold: u64::MAX }; + let stage = MerkleStage::Both { clean_threshold: u64::MAX, prune_modes: Default::default() }; measure_stage( &mut group, setup::unwind_hashes, @@ -104,7 +104,7 @@ fn merkle(c: &mut Criterion) { "Merkle-incremental".to_string(), ); - let stage = MerkleStage::Both { clean_threshold: 0 }; + let stage = MerkleStage::Both { clean_threshold: 0, prune_modes: Default::default() }; measure_stage( &mut group, setup::unwind_hashes, diff --git a/crates/stages/src/error.rs b/crates/stages/src/error.rs index 20310111ca9..b5158f3e155 100644 --- a/crates/stages/src/error.rs +++ b/crates/stages/src/error.rs @@ -49,6 +49,9 @@ pub enum StageError { #[source] error: executor::BlockExecutionError, }, + /// Invalid pruning configuration + #[error(transparent)] + PruningConfiguration(#[from] reth_primitives::PrunePartError), /// Invalid checkpoint passed to the stage #[error("Invalid stage checkpoint: {0}")] StageCheckpoint(u64), diff --git a/crates/stages/src/pipeline/mod.rs b/crates/stages/src/pipeline/mod.rs index a108695cfad..d15fefacf67 100644 --- a/crates/stages/src/pipeline/mod.rs +++ b/crates/stages/src/pipeline/mod.rs @@ -431,6 +431,19 @@ where "Stage encountered a validation error: {error}" ); + // FIXME: When handling errors, we do not commit the database transaction. + // This leads to the Merkle stage not clearing its + // checkpoint, and restarting from an invalid place. + drop(provider_rw); + provider_rw = factory.provider_rw().map_err(PipelineError::Interface)?; + provider_rw + .save_stage_checkpoint_progress(StageId::MerkleExecute, vec![])?; + provider_rw.save_stage_checkpoint( + StageId::MerkleExecute, + prev_checkpoint.unwrap_or_default(), + )?; + provider_rw.commit()?; + // We unwind because of a validation error. If the unwind itself fails, // we bail entirely, otherwise we restart the execution loop from the // beginning. diff --git a/crates/stages/src/stages/execution.rs b/crates/stages/src/stages/execution.rs index 366e10e7c3c..fc8e789a1f0 100644 --- a/crates/stages/src/stages/execution.rs +++ b/crates/stages/src/stages/execution.rs @@ -60,7 +60,7 @@ pub struct ExecutionStage { /// The commit thresholds of the execution stage. thresholds: ExecutionStageThresholds, /// Pruning configuration. - prune_targets: PruneModes, + prune_modes: PruneModes, } impl ExecutionStage { @@ -68,9 +68,9 @@ impl ExecutionStage { pub fn new( executor_factory: EF, thresholds: ExecutionStageThresholds, - prune_targets: PruneModes, + prune_modes: PruneModes, ) -> Self { - Self { metrics_tx: None, executor_factory, thresholds, prune_targets } + Self { metrics_tx: None, executor_factory, thresholds, prune_modes } } /// Create an execution stage with the provided executor factory. @@ -110,7 +110,7 @@ impl ExecutionStage { // Execute block range let mut state = PostState::default(); - state.add_prune_targets(self.prune_targets); + state.add_prune_modes(self.prune_modes); for block_number in start_block..=max_block { let td = provider @@ -425,8 +425,7 @@ mod tests { use reth_db::{models::AccountBeforeTx, test_utils::create_test_rw_db}; use reth_primitives::{ hex_literal::hex, keccak256, stage::StageUnitCheckpoint, Account, Bytecode, - ChainSpecBuilder, PruneMode, PruneModes, SealedBlock, StorageEntry, H160, H256, MAINNET, - U256, + ChainSpecBuilder, PruneModes, SealedBlock, StorageEntry, H160, H256, MAINNET, U256, }; use reth_provider::{AccountReader, BlockWriter, ProviderFactory, ReceiptProvider}; use reth_revm::Factory; @@ -894,86 +893,4 @@ mod tests { ] ); } - - #[tokio::test] - async fn test_prune() { - let test_tx = TestTransaction::default(); - let factory = Arc::new(ProviderFactory::new(test_tx.tx.as_ref(), MAINNET.clone())); - - let provider = factory.provider_rw().unwrap(); - let input = ExecInput { - target: Some(1), - /// The progress of this stage the last time it was executed. - checkpoint: None, - }; - let mut genesis_rlp = hex!("f901faf901f5a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa045571b40ae66ca7480791bbb2887286e4e4c4b1b298b191c889d6959023a32eda056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000808502540be400808000a00000000000000000000000000000000000000000000000000000000000000000880000000000000000c0c0").as_slice(); - let genesis = SealedBlock::decode(&mut genesis_rlp).unwrap(); - let mut block_rlp = hex!("f90262f901f9a075c371ba45999d87f4542326910a11af515897aebce5265d3f6acd1f1161f82fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa098f2dcd87c8ae4083e7017a05456c14eea4b1db2032126e27b3b1563d57d7cc0a08151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcba03f4e5c2ec5b2170b711d97ee755c160457bb58d8daa338e835ec02ae6860bbabb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018502540be40082a8798203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f863f861800a8405f5e10094100000000000000000000000000000000000000080801ba07e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37a05f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509bc0").as_slice(); - let block = SealedBlock::decode(&mut block_rlp).unwrap(); - provider.insert_block(genesis, None).unwrap(); - provider.insert_block(block.clone(), None).unwrap(); - provider.commit().unwrap(); - - // insert pre state - let provider = factory.provider_rw().unwrap(); - let code = hex!("5a465a905090036002900360015500"); - let code_hash = keccak256(hex!("5a465a905090036002900360015500")); - provider - .tx_ref() - .put::( - H160(hex!("1000000000000000000000000000000000000000")), - Account { nonce: 0, balance: U256::ZERO, bytecode_hash: Some(code_hash) }, - ) - .unwrap(); - provider - .tx_ref() - .put::( - H160(hex!("a94f5374fce5edbc8e2a8697c15331677e6ebf0b")), - Account { - nonce: 0, - balance: U256::from(0x3635c9adc5dea00000u128), - bytecode_hash: None, - }, - ) - .unwrap(); - provider - .tx_ref() - .put::(code_hash, Bytecode::new_raw(code.to_vec().into())) - .unwrap(); - provider.commit().unwrap(); - - let check_pruning = |factory: Arc>, - prune_targets: PruneModes, - expect_num_receipts: usize| async move { - let provider = factory.provider_rw().unwrap(); - - let mut execution_stage = ExecutionStage::new( - Factory::new(Arc::new(ChainSpecBuilder::mainnet().berlin_activated().build())), - ExecutionStageThresholds { max_blocks: Some(100), max_changes: None }, - prune_targets, - ); - - execution_stage.execute(&provider, input).await.unwrap(); - assert_eq!( - provider.receipts_by_block(1.into()).unwrap().unwrap().len(), - expect_num_receipts - ); - }; - - let mut prune = PruneModes::none(); - - check_pruning(factory.clone(), prune, 1).await; - - prune.receipts = Some(PruneMode::Full); - check_pruning(factory.clone(), prune, 0).await; - - prune.receipts = Some(PruneMode::Before(1)); - check_pruning(factory.clone(), prune, 1).await; - - prune.receipts = Some(PruneMode::Before(2)); - check_pruning(factory.clone(), prune, 0).await; - - prune.receipts = Some(PruneMode::Distance(0)); - check_pruning(factory.clone(), prune, 1).await; - } } diff --git a/crates/stages/src/stages/hashing_account.rs b/crates/stages/src/stages/hashing_account.rs index ccb6fb960fd..7eed29bd33d 100644 --- a/crates/stages/src/stages/hashing_account.rs +++ b/crates/stages/src/stages/hashing_account.rs @@ -15,6 +15,7 @@ use reth_primitives::{ AccountHashingCheckpoint, CheckpointBlockRange, EntitiesCheckpoint, StageCheckpoint, StageId, }, + PruneModes, }; use reth_provider::{AccountExtReader, DatabaseProviderRW, HashingWriter}; use std::{ @@ -34,18 +35,25 @@ pub struct AccountHashingStage { pub clean_threshold: u64, /// The maximum number of accounts to process before committing. pub commit_threshold: u64, + /// Prune mode configuration. Required to know if we can actually make an incremental + /// update based on how many changesets exist. + pub prune_modes: PruneModes, } impl AccountHashingStage { /// Create new instance of [AccountHashingStage]. - pub fn new(clean_threshold: u64, commit_threshold: u64) -> Self { - Self { clean_threshold, commit_threshold } + pub fn new(clean_threshold: u64, commit_threshold: u64, prune_modes: PruneModes) -> Self { + Self { clean_threshold, commit_threshold, prune_modes } } } impl Default for AccountHashingStage { fn default() -> Self { - Self { clean_threshold: 500_000, commit_threshold: 100_000 } + Self { + clean_threshold: 500_000, + commit_threshold: 100_000, + prune_modes: PruneModes::default(), + } } } @@ -143,12 +151,19 @@ impl Stage for AccountHashingStage { } let (from_block, to_block) = input.next_block_range().into_inner(); + let has_enough_changesets = self + .prune_modes + .prune_target_block_account_history(to_block)? + .map(|(block_number, _)| block_number) + .unwrap_or_default() < + from_block; // if there are more blocks then threshold it is faster to go over Plain state and hash all // account otherwise take changesets aggregate the sets and apply hashing to // AccountHashing table. Also, if we start from genesis, we need to hash from scratch, as // genesis accounts are not in changeset. - if to_block - from_block > self.clean_threshold || from_block == 1 { + if to_block - from_block > self.clean_threshold || from_block == 1 || !has_enough_changesets + { let tx = provider.tx_ref(); let stage_checkpoint = input .checkpoint @@ -448,6 +463,7 @@ mod tests { pub(crate) tx: TestTransaction, commit_threshold: u64, clean_threshold: u64, + prune_modes: PruneModes, } impl AccountHashingTestRunner { @@ -511,6 +527,7 @@ mod tests { tx: TestTransaction::default(), commit_threshold: 1000, clean_threshold: 1000, + prune_modes: PruneModes::default(), } } } @@ -526,6 +543,7 @@ mod tests { Self::S { commit_threshold: self.commit_threshold, clean_threshold: self.clean_threshold, + prune_modes: self.prune_modes, } } } diff --git a/crates/stages/src/stages/hashing_storage.rs b/crates/stages/src/stages/hashing_storage.rs index 040b6375bd0..c6a65b3d80c 100644 --- a/crates/stages/src/stages/hashing_storage.rs +++ b/crates/stages/src/stages/hashing_storage.rs @@ -14,7 +14,7 @@ use reth_primitives::{ CheckpointBlockRange, EntitiesCheckpoint, StageCheckpoint, StageId, StorageHashingCheckpoint, }, - StorageEntry, + PruneModes, StorageEntry, }; use reth_provider::{DatabaseProviderRW, HashingWriter, StorageReader}; use std::{collections::BTreeMap, fmt::Debug}; @@ -29,18 +29,25 @@ pub struct StorageHashingStage { pub clean_threshold: u64, /// The maximum number of slots to process before committing. pub commit_threshold: u64, + /// Prune mode configuration. Required to know if we can actually make an incremental + /// update based on how many changesets exist. + pub prune_modes: PruneModes, } impl StorageHashingStage { /// Create new instance of [StorageHashingStage]. - pub fn new(clean_threshold: u64, commit_threshold: u64) -> Self { - Self { clean_threshold, commit_threshold } + pub fn new(clean_threshold: u64, commit_threshold: u64, prune_modes: PruneModes) -> Self { + Self { clean_threshold, commit_threshold, prune_modes } } } impl Default for StorageHashingStage { fn default() -> Self { - Self { clean_threshold: 500_000, commit_threshold: 100_000 } + Self { + clean_threshold: 500_000, + commit_threshold: 100_000, + prune_modes: PruneModes::default(), + } } } @@ -63,12 +70,19 @@ impl Stage for StorageHashingStage { } let (from_block, to_block) = input.next_block_range().into_inner(); + let has_enough_changesets = self + .prune_modes + .prune_target_block_storage_history(to_block)? + .map(|(block_number, _)| block_number) + .unwrap_or_default() < + from_block; // if there are more blocks then threshold it is faster to go over Plain state and hash all // account otherwise take changesets aggregate the sets and apply hashing to // AccountHashing table. Also, if we start from genesis, we need to hash from scratch, as // genesis accounts are not in changeset, along with their storages. - if to_block - from_block > self.clean_threshold || from_block == 1 { + if to_block - from_block > self.clean_threshold || from_block == 1 || !has_enough_changesets + { let stage_checkpoint = input .checkpoint .and_then(|checkpoint| checkpoint.storage_hashing_stage_checkpoint()); @@ -457,11 +471,17 @@ mod tests { tx: TestTransaction, commit_threshold: u64, clean_threshold: u64, + prune_modes: PruneModes, } impl Default for StorageHashingTestRunner { fn default() -> Self { - Self { tx: TestTransaction::default(), commit_threshold: 1000, clean_threshold: 1000 } + Self { + tx: TestTransaction::default(), + commit_threshold: 1000, + clean_threshold: 1000, + prune_modes: PruneModes::default(), + } } } @@ -476,6 +496,7 @@ mod tests { Self::S { commit_threshold: self.commit_threshold, clean_threshold: self.clean_threshold, + prune_modes: self.prune_modes, } } } diff --git a/crates/stages/src/stages/index_account_history.rs b/crates/stages/src/stages/index_account_history.rs index fe0b6d3b404..d259ec3f815 100644 --- a/crates/stages/src/stages/index_account_history.rs +++ b/crates/stages/src/stages/index_account_history.rs @@ -1,6 +1,9 @@ use crate::{ExecInput, ExecOutput, Stage, StageError, UnwindInput, UnwindOutput}; use reth_db::database::Database; -use reth_primitives::stage::{StageCheckpoint, StageId}; +use reth_primitives::{ + stage::{StageCheckpoint, StageId}, + PruneModes, +}; use reth_provider::{AccountExtReader, DatabaseProviderRW, HistoryWriter}; use std::fmt::Debug; @@ -12,18 +15,20 @@ pub struct IndexAccountHistoryStage { /// Number of blocks after which the control /// flow will be returned to the pipeline for commit. pub commit_threshold: u64, + /// Pruning configuration. + pub prune_modes: PruneModes, } impl IndexAccountHistoryStage { /// Create new instance of [IndexAccountHistoryStage]. pub fn new(commit_threshold: u64) -> Self { - Self { commit_threshold } + Self { commit_threshold, prune_modes: PruneModes::default() } } } impl Default for IndexAccountHistoryStage { fn default() -> Self { - Self { commit_threshold: 100_000 } + Self { commit_threshold: 100_000, prune_modes: PruneModes::default() } } } @@ -38,8 +43,16 @@ impl Stage for IndexAccountHistoryStage { async fn execute( &mut self, provider: &DatabaseProviderRW<'_, &DB>, - input: ExecInput, + mut input: ExecInput, ) -> Result { + if let Some((target_prunable_block, _)) = + self.prune_modes.prune_target_block_account_history(input.target())? + { + if target_prunable_block > input.checkpoint().block_number { + input.checkpoint = Some(StageCheckpoint::new(target_prunable_block)); + } + } + if input.target_reached() { return Ok(ExecOutput::done(input.checkpoint())) } @@ -372,11 +385,16 @@ mod tests { struct IndexAccountHistoryTestRunner { pub(crate) tx: TestTransaction, commit_threshold: u64, + prune_modes: PruneModes, } impl Default for IndexAccountHistoryTestRunner { fn default() -> Self { - Self { tx: TestTransaction::default(), commit_threshold: 1000 } + Self { + tx: TestTransaction::default(), + commit_threshold: 1000, + prune_modes: PruneModes::default(), + } } } @@ -388,7 +406,7 @@ mod tests { } fn stage(&self) -> Self::S { - Self::S { commit_threshold: self.commit_threshold } + Self::S { commit_threshold: self.commit_threshold, prune_modes: self.prune_modes } } } diff --git a/crates/stages/src/stages/index_storage_history.rs b/crates/stages/src/stages/index_storage_history.rs index 945bafc5f33..4759cd82c59 100644 --- a/crates/stages/src/stages/index_storage_history.rs +++ b/crates/stages/src/stages/index_storage_history.rs @@ -1,6 +1,9 @@ use crate::{ExecInput, ExecOutput, Stage, StageError, UnwindInput, UnwindOutput}; use reth_db::{database::Database, models::BlockNumberAddress}; -use reth_primitives::stage::{StageCheckpoint, StageId}; +use reth_primitives::{ + stage::{StageCheckpoint, StageId}, + PruneModes, +}; use reth_provider::{DatabaseProviderRW, HistoryWriter, StorageReader}; use std::fmt::Debug; @@ -12,18 +15,20 @@ pub struct IndexStorageHistoryStage { /// Number of blocks after which the control /// flow will be returned to the pipeline for commit. pub commit_threshold: u64, + /// Pruning configuration. + pub prune_modes: PruneModes, } impl IndexStorageHistoryStage { /// Create new instance of [IndexStorageHistoryStage]. pub fn new(commit_threshold: u64) -> Self { - Self { commit_threshold } + Self { commit_threshold, prune_modes: PruneModes::default() } } } impl Default for IndexStorageHistoryStage { fn default() -> Self { - Self { commit_threshold: 100_000 } + Self { commit_threshold: 100_000, prune_modes: PruneModes::default() } } } @@ -38,8 +43,16 @@ impl Stage for IndexStorageHistoryStage { async fn execute( &mut self, provider: &DatabaseProviderRW<'_, &DB>, - input: ExecInput, + mut input: ExecInput, ) -> Result { + if let Some((target_prunable_block, _)) = + self.prune_modes.prune_target_block_storage_history(input.target())? + { + if target_prunable_block > input.checkpoint().block_number { + input.checkpoint = Some(StageCheckpoint::new(target_prunable_block)); + } + } + if input.target_reached() { return Ok(ExecOutput::done(input.checkpoint())) } @@ -73,17 +86,29 @@ mod tests { use std::collections::BTreeMap; use super::*; - use crate::test_utils::TestTransaction; + use crate::test_utils::{ + stage_test_suite_ext, ExecuteStageTestRunner, StageTestRunner, TestRunnerError, + TestTransaction, UnwindStageTestRunner, + }; + use itertools::Itertools; use reth_db::{ + cursor::DbCursorRO, models::{ + sharded_key, storage_sharded_key::{StorageShardedKey, NUM_OF_INDICES_IN_SHARD}, BlockNumberAddress, ShardedKey, StoredBlockBodyIndices, }, tables, - transaction::DbTxMut, + transaction::{DbTx, DbTxMut}, BlockNumberList, }; - use reth_primitives::{hex_literal::hex, StorageEntry, H160, H256, MAINNET, U256}; + use reth_interfaces::test_utils::{ + generators, + generators::{random_block_range, random_contract_account_range, random_transition_range}, + }; + use reth_primitives::{ + hex_literal::hex, Address, BlockNumber, StorageEntry, H160, H256, MAINNET, U256, + }; const ADDRESS: H160 = H160(hex!("0000000000000000000000000000000000000001")); const STORAGE_KEY: H256 = @@ -368,4 +393,147 @@ mod tests { ]) ); } + + stage_test_suite_ext!(IndexStorageHistoryTestRunner, index_storage_history); + + struct IndexStorageHistoryTestRunner { + pub(crate) tx: TestTransaction, + commit_threshold: u64, + prune_modes: PruneModes, + } + + impl Default for IndexStorageHistoryTestRunner { + fn default() -> Self { + Self { + tx: TestTransaction::default(), + commit_threshold: 1000, + prune_modes: PruneModes::default(), + } + } + } + + impl StageTestRunner for IndexStorageHistoryTestRunner { + type S = IndexStorageHistoryStage; + + fn tx(&self) -> &TestTransaction { + &self.tx + } + + fn stage(&self) -> Self::S { + Self::S { commit_threshold: self.commit_threshold, prune_modes: self.prune_modes } + } + } + + impl ExecuteStageTestRunner for IndexStorageHistoryTestRunner { + type Seed = (); + + fn seed_execution(&mut self, input: ExecInput) -> Result { + let stage_process = input.checkpoint().block_number; + let start = stage_process + 1; + let end = input.target(); + let mut rng = generators::rng(); + + let num_of_accounts = 31; + let accounts = random_contract_account_range(&mut rng, &mut (0..num_of_accounts)) + .into_iter() + .collect::>(); + + let blocks = random_block_range(&mut rng, start..=end, H256::zero(), 0..3); + + let (transitions, _) = random_transition_range( + &mut rng, + blocks.iter(), + accounts.into_iter().map(|(addr, acc)| (addr, (acc, Vec::new()))), + 0..3, + 0..256, + ); + + // add block changeset from block 1. + self.tx.insert_transitions(transitions, Some(start))?; + + Ok(()) + } + + fn validate_execution( + &self, + input: ExecInput, + output: Option, + ) -> Result<(), TestRunnerError> { + if let Some(output) = output { + let start_block = input.next_block(); + let end_block = output.checkpoint.block_number; + if start_block > end_block { + return Ok(()) + } + + assert_eq!( + output, + ExecOutput { checkpoint: StageCheckpoint::new(input.target()), done: true } + ); + + let provider = self.tx.inner(); + let mut changeset_cursor = + provider.tx_ref().cursor_read::()?; + + let storage_transitions = changeset_cursor + .walk_range(BlockNumberAddress::range(start_block..=end_block))? + .try_fold( + BTreeMap::new(), + |mut storages: BTreeMap<(Address, H256), Vec>, + entry| + -> Result<_, TestRunnerError> { + let (index, storage) = entry?; + storages + .entry((index.address(), storage.key)) + .or_default() + .push(index.block_number()); + Ok(storages) + }, + )?; + + let mut result = BTreeMap::new(); + for (partial_key, indices) in storage_transitions { + // chunk indices and insert them in shards of N size. + let mut chunks = indices + .iter() + .chunks(sharded_key::NUM_OF_INDICES_IN_SHARD) + .into_iter() + .map(|chunks| chunks.map(|i| *i as usize).collect::>()) + .collect::>(); + let last_chunk = chunks.pop(); + + chunks.into_iter().for_each(|list| { + result.insert( + StorageShardedKey::new( + partial_key.0, + partial_key.1, + *list.last().expect("Chuck does not return empty list") + as BlockNumber, + ), + list, + ); + }); + + if let Some(last_list) = last_chunk { + result.insert( + StorageShardedKey::new(partial_key.0, partial_key.1, u64::MAX), + last_list, + ); + }; + } + + let table = cast(self.tx.table::().unwrap()); + assert_eq!(table, result); + } + Ok(()) + } + } + + impl UnwindStageTestRunner for IndexStorageHistoryTestRunner { + fn validate_unwind(&self, _input: UnwindInput) -> Result<(), TestRunnerError> { + let table = self.tx.table::().unwrap(); + assert!(table.is_empty()); + Ok(()) + } + } } diff --git a/crates/stages/src/stages/merkle.rs b/crates/stages/src/stages/merkle.rs index 1a95341d85e..ffcc427b58c 100644 --- a/crates/stages/src/stages/merkle.rs +++ b/crates/stages/src/stages/merkle.rs @@ -10,7 +10,7 @@ use reth_primitives::{ hex, stage::{EntitiesCheckpoint, MerkleCheckpoint, StageCheckpoint, StageId}, trie::StoredSubNode, - BlockNumber, SealedHeader, H256, + BlockNumber, PruneModes, SealedHeader, H256, }; use reth_provider::{ DatabaseProviderRW, HeaderProvider, ProviderError, StageCheckpointReader, StageCheckpointWriter, @@ -47,6 +47,9 @@ pub enum MerkleStage { /// The threshold (in number of blocks) for switching from incremental trie building /// of changes to whole rebuild. clean_threshold: u64, + /// Prune mode configuration. Required to know if we can actually make an incremental root + /// update based on how many changesets exist. + prune_modes: PruneModes, }, /// The unwind portion of the merkle stage. Unwind, @@ -54,13 +57,13 @@ pub enum MerkleStage { /// Able to execute and unwind. Used for tests #[cfg(any(test, feature = "test-utils"))] #[allow(missing_docs)] - Both { clean_threshold: u64 }, + Both { clean_threshold: u64, prune_modes: PruneModes }, } impl MerkleStage { /// Stage default for the [MerkleStage::Execution]. pub fn default_execution() -> Self { - Self::Execution { clean_threshold: 50_000 } + Self::Execution { clean_threshold: 50_000, prune_modes: PruneModes::default() } } /// Stage default for the [MerkleStage::Unwind]. @@ -69,8 +72,8 @@ impl MerkleStage { } /// Create new instance of [MerkleStage::Execution]. - pub fn new_execution(clean_threshold: u64) -> Self { - Self::Execution { clean_threshold } + pub fn new_execution(clean_threshold: u64, prune_modes: PruneModes) -> Self { + Self::Execution { clean_threshold, prune_modes } } /// Check that the computed state root matches the root in the expected header. @@ -128,6 +131,26 @@ impl MerkleStage { } Ok(provider.save_stage_checkpoint_progress(StageId::MerkleExecute, buf)?) } + + /// When pruning is enabled for account and storage history, we might not have all changesets + /// required for an incremental state root update on a pipeline re-run. + pub fn has_enough_changesets( + &self, + prune_modes: PruneModes, + from_block: BlockNumber, + to_block: BlockNumber, + ) -> Result { + Ok(prune_modes + .prune_target_block_account_history(to_block)? + .map(|(block_number, _)| block_number) + .unwrap_or_default() < + from_block && + prune_modes + .prune_target_block_storage_history(to_block)? + .map(|(block_number, _)| block_number) + .unwrap_or_default() < + from_block) + } } #[async_trait::async_trait] @@ -148,14 +171,16 @@ impl Stage for MerkleStage { provider: &DatabaseProviderRW<'_, &DB>, input: ExecInput, ) -> Result { - let threshold = match self { + let (threshold, prune_modes) = match self { MerkleStage::Unwind => { info!(target: "sync::stages::merkle::unwind", "Stage is always skipped"); return Ok(ExecOutput::done(StageCheckpoint::new(input.target()))) } - MerkleStage::Execution { clean_threshold } => *clean_threshold, + MerkleStage::Execution { clean_threshold, prune_modes } => { + (*clean_threshold, *prune_modes) + } #[cfg(any(test, feature = "test-utils"))] - MerkleStage::Both { clean_threshold } => *clean_threshold, + MerkleStage::Both { clean_threshold, prune_modes } => (*clean_threshold, *prune_modes), }; let range = input.next_block_range(); @@ -168,10 +193,12 @@ impl Stage for MerkleStage { let target_block_root = target_block.state_root; let mut checkpoint = self.get_execution_checkpoint(provider)?; - let (trie_root, entities_checkpoint) = if range.is_empty() { (target_block_root, input.checkpoint().entities_stage_checkpoint().unwrap_or_default()) - } else if to_block - from_block > threshold || from_block == 1 { + } else if to_block - from_block > threshold || + from_block == 1 || + !self.has_enough_changesets(prune_modes, from_block, to_block)? + { // if there are more blocks than threshold it is faster to rebuild the trie let mut entities_checkpoint = if let Some(checkpoint) = checkpoint.as_ref().filter(|c| c.target_block == to_block) @@ -445,11 +472,16 @@ mod tests { struct MerkleTestRunner { tx: TestTransaction, clean_threshold: u64, + prune_modes: PruneModes, } impl Default for MerkleTestRunner { fn default() -> Self { - Self { tx: TestTransaction::default(), clean_threshold: 10000 } + Self { + tx: TestTransaction::default(), + clean_threshold: 10000, + prune_modes: PruneModes::default(), + } } } @@ -461,7 +493,7 @@ mod tests { } fn stage(&self) -> Self::S { - Self::S::Both { clean_threshold: self.clean_threshold } + Self::S::Both { clean_threshold: self.clean_threshold, prune_modes: self.prune_modes } } } diff --git a/crates/stages/src/stages/mod.rs b/crates/stages/src/stages/mod.rs index 96f1ec5fecb..3cf295abeb4 100644 --- a/crates/stages/src/stages/mod.rs +++ b/crates/stages/src/stages/mod.rs @@ -35,3 +35,188 @@ pub use merkle::*; pub use sender_recovery::*; pub use total_difficulty::*; pub use tx_lookup::*; + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + stage::Stage, + stages::{ExecutionStage, IndexAccountHistoryStage, IndexStorageHistoryStage}, + test_utils::TestTransaction, + ExecInput, + }; + use reth_db::{ + cursor::DbCursorRO, + mdbx::{cursor::Cursor, RW}, + tables, + transaction::{DbTx, DbTxMut}, + AccountHistory, DatabaseEnv, + }; + use reth_interfaces::test_utils::generators::{self, random_block}; + use reth_primitives::{ + hex_literal::hex, keccak256, Account, Bytecode, ChainSpecBuilder, PruneMode, PruneModes, + SealedBlock, H160, MAINNET, U256, + }; + use reth_provider::{ + AccountExtReader, BlockWriter, DatabaseProviderRW, ProviderFactory, ReceiptProvider, + StorageReader, + }; + use reth_revm::Factory; + use reth_rlp::Decodable; + use std::sync::Arc; + + #[tokio::test] + async fn test_prune() { + let test_tx = TestTransaction::default(); + let factory = Arc::new(ProviderFactory::new(test_tx.tx.as_ref(), MAINNET.clone())); + + let provider = factory.provider_rw().unwrap(); + let tip = 66; + let input = ExecInput { + target: Some(tip), + /// The progress of this stage the last time it was executed. + checkpoint: None, + }; + let mut genesis_rlp = hex!("f901faf901f5a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa045571b40ae66ca7480791bbb2887286e4e4c4b1b298b191c889d6959023a32eda056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000808502540be400808000a00000000000000000000000000000000000000000000000000000000000000000880000000000000000c0c0").as_slice(); + let genesis = SealedBlock::decode(&mut genesis_rlp).unwrap(); + let mut block_rlp = hex!("f90262f901f9a075c371ba45999d87f4542326910a11af515897aebce5265d3f6acd1f1161f82fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa098f2dcd87c8ae4083e7017a05456c14eea4b1db2032126e27b3b1563d57d7cc0a08151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcba03f4e5c2ec5b2170b711d97ee755c160457bb58d8daa338e835ec02ae6860bbabb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018502540be40082a8798203e800a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f863f861800a8405f5e10094100000000000000000000000000000000000000080801ba07e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37a05f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509bc0").as_slice(); + let block = SealedBlock::decode(&mut block_rlp).unwrap(); + provider.insert_block(genesis, None).unwrap(); + provider.insert_block(block.clone(), None).unwrap(); + + // Fill with bogus blocks to respect PruneMode distance. + let mut head = block.hash; + let mut rng = generators::rng(); + for block_number in 2..=tip { + let nblock = random_block(&mut rng, block_number, Some(head), Some(0), Some(0)); + head = nblock.hash; + provider.insert_block(nblock, None).unwrap(); + } + provider.commit().unwrap(); + + // insert pre state + let provider = factory.provider_rw().unwrap(); + let code = hex!("5a465a905090036002900360015500"); + let code_hash = keccak256(hex!("5a465a905090036002900360015500")); + provider + .tx_ref() + .put::( + H160(hex!("1000000000000000000000000000000000000000")), + Account { nonce: 0, balance: U256::ZERO, bytecode_hash: Some(code_hash) }, + ) + .unwrap(); + provider + .tx_ref() + .put::( + H160(hex!("a94f5374fce5edbc8e2a8697c15331677e6ebf0b")), + Account { + nonce: 0, + balance: U256::from(0x3635c9adc5dea00000u128), + bytecode_hash: None, + }, + ) + .unwrap(); + provider + .tx_ref() + .put::(code_hash, Bytecode::new_raw(code.to_vec().into())) + .unwrap(); + provider.commit().unwrap(); + + let check_pruning = |factory: Arc>, + prune_modes: PruneModes, + expect_num_receipts: usize, + expect_num_acc_changesets: usize, + expect_num_storage_changesets: usize| async move { + let provider: DatabaseProviderRW<'_, &DatabaseEnv> = factory.provider_rw().unwrap(); + + // Check execution and create receipts and changesets according to the pruning + // configuration + let mut execution_stage = ExecutionStage::new( + Factory::new(Arc::new(ChainSpecBuilder::mainnet().berlin_activated().build())), + ExecutionStageThresholds { max_blocks: Some(100), max_changes: None }, + prune_modes, + ); + + execution_stage.execute(&provider, input).await.unwrap(); + assert_eq!( + provider.receipts_by_block(1.into()).unwrap().unwrap().len(), + expect_num_receipts + ); + + assert_eq!( + provider.changed_storages_and_blocks_with_range(0..=1000).unwrap().len(), + expect_num_storage_changesets + ); + + assert_eq!( + provider.changed_accounts_and_blocks_with_range(0..=1000).unwrap().len(), + expect_num_acc_changesets + ); + + // Check AccountHistory + let mut acc_indexing_stage = + IndexAccountHistoryStage { prune_modes, ..Default::default() }; + + if let Some(PruneMode::Full) = prune_modes.account_history { + // Full is not supported + assert!(acc_indexing_stage.execute(&provider, input).await.is_err()); + } else { + acc_indexing_stage.execute(&provider, input).await.unwrap(); + let mut account_history: Cursor<'_, RW, AccountHistory> = + provider.tx_ref().cursor_read::().unwrap(); + assert_eq!(account_history.walk(None).unwrap().count(), expect_num_acc_changesets); + } + + // Check StorageHistory + let mut storage_indexing_stage = + IndexStorageHistoryStage { prune_modes, ..Default::default() }; + + if let Some(PruneMode::Full) = prune_modes.storage_history { + // Full is not supported + assert!(acc_indexing_stage.execute(&provider, input).await.is_err()); + } else { + storage_indexing_stage.execute(&provider, input).await.unwrap(); + + let mut storage_history = + provider.tx_ref().cursor_read::().unwrap(); + assert_eq!( + storage_history.walk(None).unwrap().count(), + expect_num_storage_changesets + ); + } + }; + + // In an unpruned configuration there is 1 receipt, 3 changed accounts and 1 changed + // storage. + let mut prune = PruneModes::none(); + check_pruning(factory.clone(), prune, 1, 3, 1).await; + + prune.receipts = Some(PruneMode::Full); + prune.account_history = Some(PruneMode::Full); + prune.storage_history = Some(PruneMode::Full); + // This will result in error for account_history and storage_history, which is caught. + check_pruning(factory.clone(), prune, 0, 0, 0).await; + + prune.receipts = Some(PruneMode::Before(1)); + prune.account_history = Some(PruneMode::Before(1)); + prune.storage_history = Some(PruneMode::Before(1)); + check_pruning(factory.clone(), prune, 1, 3, 1).await; + + prune.receipts = Some(PruneMode::Before(2)); + prune.account_history = Some(PruneMode::Before(2)); + prune.storage_history = Some(PruneMode::Before(2)); + // The one account is the miner + check_pruning(factory.clone(), prune, 0, 1, 0).await; + + prune.receipts = Some(PruneMode::Distance(66)); + prune.account_history = Some(PruneMode::Distance(66)); + prune.storage_history = Some(PruneMode::Distance(66)); + check_pruning(factory.clone(), prune, 1, 3, 1).await; + + prune.receipts = Some(PruneMode::Distance(64)); + prune.account_history = Some(PruneMode::Distance(64)); + prune.storage_history = Some(PruneMode::Distance(64)); + // The one account is the miner + check_pruning(factory.clone(), prune, 0, 1, 0).await; + } +} diff --git a/crates/stages/src/stages/tx_lookup.rs b/crates/stages/src/stages/tx_lookup.rs index 09e0e6d674f..211266d45d8 100644 --- a/crates/stages/src/stages/tx_lookup.rs +++ b/crates/stages/src/stages/tx_lookup.rs @@ -11,9 +11,9 @@ use reth_db::{ use reth_primitives::{ keccak256, stage::{EntitiesCheckpoint, StageCheckpoint, StageId}, - TransactionSignedNoHash, TxNumber, H256, + PrunePart, TransactionSignedNoHash, TxNumber, H256, }; -use reth_provider::DatabaseProviderRW; +use reth_provider::{BlockReader, DatabaseProviderRW, PruneCheckpointReader}; use tokio::sync::mpsc; use tracing::*; @@ -183,9 +183,20 @@ fn calculate_hash( fn stage_checkpoint( provider: &DatabaseProviderRW<'_, &DB>, -) -> Result { +) -> Result { + let pruned_entries = provider + .get_prune_checkpoint(PrunePart::TransactionLookup)? + .map(|checkpoint| provider.block_body_indices(checkpoint.block_number)) + .transpose()? + .flatten() + // +1 is needed because TxNumber is 0-indexed + .map(|body| body.last_tx_num() + 1) + .unwrap_or_default(); Ok(EntitiesCheckpoint { - processed: provider.tx_ref().entries::()? as u64, + // If `TxHashNumber` table was pruned, we will have a number of entries in it not matching + // the actual number of processed transactions. To fix that, we add the number of pruned + // `TxHashNumber` entries. + processed: provider.tx_ref().entries::()? as u64 + pruned_entries, total: provider.tx_ref().entries::()? as u64, }) } @@ -202,8 +213,13 @@ mod tests { generators, generators::{random_block, random_block_range}, }; - use reth_primitives::{stage::StageUnitCheckpoint, BlockNumber, SealedBlock, H256}; - use reth_provider::{BlockReader, ProviderError, TransactionsProvider}; + use reth_primitives::{ + stage::StageUnitCheckpoint, BlockNumber, PruneCheckpoint, PruneMode, SealedBlock, H256, + MAINNET, + }; + use reth_provider::{ + BlockReader, ProviderError, ProviderFactory, PruneCheckpointWriter, TransactionsProvider, + }; // Implement stage test suite. stage_test_suite_ext!(TransactionLookupTestRunner, transaction_lookup); @@ -321,6 +337,57 @@ mod tests { assert!(runner.validate_execution(first_input, result.ok()).is_ok(), "validation failed"); } + #[test] + fn stage_checkpoint_pruned() { + let tx = TestTransaction::default(); + let mut rng = generators::rng(); + + let blocks = random_block_range(&mut rng, 0..=100, H256::zero(), 0..10); + tx.insert_blocks(blocks.iter(), None).expect("insert blocks"); + + let max_pruned_block = 30; + let max_processed_block = 70; + + let mut tx_hash_numbers = Vec::new(); + let mut tx_hash_number = 0; + for block in &blocks[..=max_processed_block] { + for transaction in &block.body { + if block.number > max_pruned_block { + tx_hash_numbers.push((transaction.hash, tx_hash_number)); + } + tx_hash_number += 1; + } + } + tx.insert_tx_hash_numbers(tx_hash_numbers).expect("insert tx hash numbers"); + + let provider = tx.inner_rw(); + provider + .save_prune_checkpoint( + PrunePart::TransactionLookup, + PruneCheckpoint { + block_number: max_pruned_block as BlockNumber, + prune_mode: PruneMode::Full, + }, + ) + .expect("save stage checkpoint"); + provider.commit().expect("commit"); + + let db = tx.inner_raw(); + let factory = ProviderFactory::new(db.as_ref(), MAINNET.clone()); + let provider = factory.provider_rw().expect("provider rw"); + + assert_eq!( + stage_checkpoint(&provider).expect("stage checkpoint"), + EntitiesCheckpoint { + processed: blocks[..=max_processed_block] + .iter() + .map(|block| block.body.len() as u64) + .sum::(), + total: blocks.iter().map(|block| block.body.len() as u64).sum::() + } + ); + } + struct TransactionLookupTestRunner { tx: TestTransaction, threshold: u64, diff --git a/crates/storage/codecs/derive/src/arbitrary.rs b/crates/storage/codecs/derive/src/arbitrary.rs index 82d35c6e624..455a034c2fd 100644 --- a/crates/storage/codecs/derive/src/arbitrary.rs +++ b/crates/storage/codecs/derive/src/arbitrary.rs @@ -26,7 +26,7 @@ pub fn maybe_generate_tests(args: TokenStream, ast: &DeriveInput) -> TokenStream let mut buf = vec![]; let len = field.clone().to_compact(&mut buf); let (decoded, _) = super::#type_ident::from_compact(&buf, len); - assert!(field == decoded); + assert_eq!(field, decoded); } }); } else if arg.to_string() == "rlp" { diff --git a/crates/storage/db/src/tables/models/accounts.rs b/crates/storage/db/src/tables/models/accounts.rs index c1a50e95bc1..c82b4740901 100644 --- a/crates/storage/db/src/tables/models/accounts.rs +++ b/crates/storage/db/src/tables/models/accounts.rs @@ -64,7 +64,9 @@ impl Compact for AccountBeforeTx { /// [`StorageChangeSet`](crate::tables::StorageChangeSet) /// /// Since it's used as a key, it isn't compressed when encoding it. -#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Ord, PartialOrd)] +#[derive( + Debug, Default, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Ord, PartialOrd, Hash, +)] pub struct BlockNumberAddress(pub (BlockNumber, Address)); impl BlockNumberAddress { diff --git a/crates/storage/db/src/tables/models/sharded_key.rs b/crates/storage/db/src/tables/models/sharded_key.rs index a38c3af3a31..5dedd349eb6 100644 --- a/crates/storage/db/src/tables/models/sharded_key.rs +++ b/crates/storage/db/src/tables/models/sharded_key.rs @@ -1,5 +1,7 @@ //! Sharded key +use std::hash::Hash; + use crate::{ table::{Decode, Encode}, DatabaseError, @@ -74,3 +76,13 @@ where Ok(ShardedKey::new(key, highest_tx_number)) } } + +impl Hash for ShardedKey +where + T: Hash, +{ + fn hash(&self, state: &mut H) { + self.key.hash(state); + self.highest_block_number.hash(state); + } +} diff --git a/crates/storage/db/src/tables/models/storage_sharded_key.rs b/crates/storage/db/src/tables/models/storage_sharded_key.rs index 984933d1f17..15e6736599f 100644 --- a/crates/storage/db/src/tables/models/storage_sharded_key.rs +++ b/crates/storage/db/src/tables/models/storage_sharded_key.rs @@ -19,7 +19,9 @@ pub const NUM_OF_INDICES_IN_SHARD: usize = 2_000; /// `Address | Storagekey | 200` -> data is from transition 0 to 200. /// /// `Address | StorageKey | 300` -> data is from transition 201 to 300. -#[derive(Debug, Default, Clone, Eq, Ord, PartialOrd, PartialEq, AsRef, Serialize, Deserialize)] +#[derive( + Debug, Default, Clone, Eq, Ord, PartialOrd, PartialEq, AsRef, Serialize, Deserialize, Hash, +)] pub struct StorageShardedKey { /// Storage account address. pub address: H160, diff --git a/crates/storage/provider/Cargo.toml b/crates/storage/provider/Cargo.toml index 25b1fd3ccdd..89ee64066b9 100644 --- a/crates/storage/provider/Cargo.toml +++ b/crates/storage/provider/Cargo.toml @@ -25,7 +25,7 @@ tracing.workspace = true # misc auto_impl = "1.0" -itertools = "0.10" +itertools.workspace = true pin-project.workspace = true derive_more = "0.99" parking_lot = "0.12" @@ -43,3 +43,8 @@ tempfile = "3.3" [features] test-utils = ["reth-rlp"] +optimism = [ + "reth-primitives/optimism", + "reth-interfaces/optimism", + "reth-revm-primitives/optimism" +] diff --git a/crates/storage/provider/src/chain.rs b/crates/storage/provider/src/chain.rs index b5d596c0164..1633d360f06 100644 --- a/crates/storage/provider/src/chain.rs +++ b/crates/storage/provider/src/chain.rs @@ -136,6 +136,11 @@ impl Chain { Self { state, blocks: block_num_hash } } + /// Returns length of the chain. + pub fn len(&self) -> usize { + self.blocks.len() + } + /// Get all receipts for the given block. pub fn receipts_by_block_hash(&self, block_hash: BlockHash) -> Option<&[Receipt]> { let num = self.block_number(block_hash)?; diff --git a/crates/storage/provider/src/post_state/mod.rs b/crates/storage/provider/src/post_state/mod.rs index 846012bffad..300b7e27d6e 100644 --- a/crates/storage/provider/src/post_state/mod.rs +++ b/crates/storage/provider/src/post_state/mod.rs @@ -79,7 +79,7 @@ pub struct PostState { /// The receipt(s) of the executed transaction(s). receipts: BTreeMap>, /// Pruning configuration. - prune_targets: PruneModes, + prune_modes: PruneModes, } impl PostState { @@ -94,8 +94,8 @@ impl PostState { } /// Add a pruning configuration. - pub fn add_prune_targets(&mut self, prune_targets: PruneModes) { - self.prune_targets = prune_targets; + pub fn add_prune_modes(&mut self, prune_modes: PruneModes) { + self.prune_modes = prune_modes; } /// Return the current size of the poststate. @@ -518,6 +518,7 @@ impl PostState { pub fn write_history_to_db<'a, TX: DbTxMut<'a> + DbTx<'a>>( &mut self, tx: &TX, + tip: BlockNumber, ) -> Result<(), DbError> { // Write storage changes tracing::trace!(target: "provider::post_state", "Writing storage changes"); @@ -560,6 +561,10 @@ impl PostState { } } + if self.prune_modes.should_prune_storage_history(block_number, tip) { + continue + } + for (slot, old_value) in storage.storage { tracing::trace!(target: "provider::post_state", ?storage_id, ?slot, ?old_value, "Storage changed"); storage_changeset_cursor.append_dup( @@ -576,6 +581,10 @@ impl PostState { for (block_number, account_changes) in std::mem::take(&mut self.account_changes).inner.into_iter() { + if self.prune_modes.should_prune_account_history(block_number, tip) { + continue + } + for (address, info) in account_changes.into_iter() { tracing::trace!(target: "provider::post_state", block_number, ?address, old = ?info, "Account changed"); account_changeset_cursor @@ -592,7 +601,7 @@ impl PostState { tx: &TX, tip: BlockNumber, ) -> Result<(), DbError> { - self.write_history_to_db(tx)?; + self.write_history_to_db(tx, tip)?; // Write new storage state tracing::trace!(target: "provider::post_state", len = self.storage.len(), "Writing new storage state"); @@ -644,12 +653,12 @@ impl PostState { // Write the receipts of the transactions if not pruned tracing::trace!(target: "provider::post_state", len = self.receipts.len(), "Writing receipts"); - if !self.receipts.is_empty() && self.prune_targets.receipts != Some(PruneMode::Full) { + if !self.receipts.is_empty() && self.prune_modes.receipts != Some(PruneMode::Full) { let mut bodies_cursor = tx.cursor_read::()?; let mut receipts_cursor = tx.cursor_write::()?; for (block, receipts) in self.receipts { - if self.prune_targets.should_prune_receipts(block, tip) { + if self.prune_modes.should_prune_receipts(block, tip) { continue } diff --git a/crates/storage/provider/src/providers/database/provider.rs b/crates/storage/provider/src/providers/database/provider.rs index 1f871cfa29a..446065457c3 100644 --- a/crates/storage/provider/src/providers/database/provider.rs +++ b/crates/storage/provider/src/providers/database/provider.rs @@ -640,7 +640,7 @@ impl<'this, TX: DbTxMut<'this> + DbTx<'this>> DatabaseProvider<'this, TX> { &self, keys: impl IntoIterator, batch_size: usize, - batch_callback: impl Fn(usize), + mut batch_callback: impl FnMut(usize), ) -> std::result::Result where T: Table, @@ -650,8 +650,9 @@ impl<'this, TX: DbTxMut<'this> + DbTx<'this>> DatabaseProvider<'this, TX> { let mut deleted = 0; for key in keys { - cursor.seek_exact(key)?; - cursor.delete_current()?; + if cursor.seek_exact(key)?.is_some() { + cursor.delete_current()?; + } deleted += 1; if deleted % batch_size == 0 { diff --git a/crates/storage/provider/src/providers/state/historical.rs b/crates/storage/provider/src/providers/state/historical.rs index f62ab7b98c1..458a4c6421e 100644 --- a/crates/storage/provider/src/providers/state/historical.rs +++ b/crates/storage/provider/src/providers/state/historical.rs @@ -5,8 +5,10 @@ use crate::{ use reth_db::{ cursor::{DbCursorRO, DbDupCursorRO}, models::{storage_sharded_key::StorageShardedKey, ShardedKey}, + table::Table, tables, transaction::DbTx, + BlockNumberList, }; use reth_interfaces::Result; use reth_primitives::{ @@ -17,11 +19,11 @@ use std::marker::PhantomData; /// State provider for a given transition id which takes a tx reference. /// /// Historical state provider reads the following tables: -/// [tables::AccountHistory] -/// [tables::Bytecodes] -/// [tables::StorageHistory] -/// [tables::AccountChangeSet] -/// [tables::StorageChangeSet] +/// - [tables::AccountHistory] +/// - [tables::Bytecodes] +/// - [tables::StorageHistory] +/// - [tables::AccountChangeSet] +/// - [tables::StorageChangeSet] pub struct HistoricalStateProviderRef<'a, 'b, TX: DbTx<'a>> { /// Transaction tx: &'b TX, @@ -32,7 +34,7 @@ pub struct HistoricalStateProviderRef<'a, 'b, TX: DbTx<'a>> { } pub enum HistoryInfo { - NotWritten, + NotYetWritten, InChangeset(u64), InPlainState, } @@ -47,24 +49,7 @@ impl<'a, 'b, TX: DbTx<'a>> HistoricalStateProviderRef<'a, 'b, TX> { pub fn account_history_lookup(&self, address: Address) -> Result { // history key to search IntegerList of block number changesets. let history_key = ShardedKey::new(address, self.block_number); - let mut cursor = self.tx.cursor_read::()?; - - if let Some(chunk) = - cursor.seek(history_key)?.filter(|(key, _)| key.key == address).map(|x| x.1 .0) - { - let chunk = chunk.enable_rank(); - let rank = chunk.rank(self.block_number as usize); - if rank == 0 && !cursor.prev()?.is_some_and(|(key, _)| key.key == address) { - return Ok(HistoryInfo::NotWritten) - } - if rank < chunk.len() { - Ok(HistoryInfo::InChangeset(chunk.select(rank) as u64)) - } else { - Ok(HistoryInfo::InPlainState) - } - } else { - Ok(HistoryInfo::NotWritten) - } + self.history_info::(history_key, |key| key.key == address) } /// Lookup a storage key in the StorageHistory table @@ -75,29 +60,47 @@ impl<'a, 'b, TX: DbTx<'a>> HistoricalStateProviderRef<'a, 'b, TX> { ) -> Result { // history key to search IntegerList of block number changesets. let history_key = StorageShardedKey::new(address, storage_key, self.block_number); - let mut cursor = self.tx.cursor_read::()?; + self.history_info::(history_key, |key| { + key.address == address && key.sharded_key.key == storage_key + }) + } - if let Some(chunk) = cursor - .seek(history_key)? - .filter(|(key, _)| key.address == address && key.sharded_key.key == storage_key) - .map(|x| x.1 .0) - { + fn history_info(&self, key: K, key_filter: impl Fn(&K) -> bool) -> Result + where + T: Table, + { + let mut cursor = self.tx.cursor_read::()?; + + // Lookup the history chunk in the history index. If they key does not appear in the + // index, the first chunk for the next key will be returned so we filter out chunks that + // have a different key. + if let Some(chunk) = cursor.seek(key)?.filter(|(key, _)| key_filter(key)).map(|x| x.1 .0) { let chunk = chunk.enable_rank(); + + // Get the rank of the first entry after our block. let rank = chunk.rank(self.block_number as usize); - if rank == 0 && - !cursor.prev()?.is_some_and(|(key, _)| { - key.address == address && key.sharded_key.key == storage_key - }) - { - return Ok(HistoryInfo::NotWritten) + + // If our block is before the first entry in the index chunk, it might be before + // the first write ever. To check, we look at the previous entry and check if the + // key is the same. + // This check is worth it, the `cursor.prev()` check is rarely triggered (the if will + // short-circuit) and when it passes we save a full seek into the changeset/plain state + // table. + if rank == 0 && !cursor.prev()?.is_some_and(|(key, _)| key_filter(&key)) { + // The key is written to, but only after our block. + return Ok(HistoryInfo::NotYetWritten) } if rank < chunk.len() { + // The chunk contains an entry for a write after our block, return it. Ok(HistoryInfo::InChangeset(chunk.select(rank) as u64)) } else { + // The chunk does not contain an entry for a write after our block. This can only + // happen if this is the last chunk and so we need to look in the plain state. Ok(HistoryInfo::InPlainState) } } else { - Ok(HistoryInfo::NotWritten) + // The key has not been written to at all. + Ok(HistoryInfo::NotYetWritten) } } } @@ -106,7 +109,7 @@ impl<'a, 'b, TX: DbTx<'a>> AccountReader for HistoricalStateProviderRef<'a, 'b, /// Get basic account information. fn basic_account(&self, address: Address) -> Result> { match self.account_history_lookup(address)? { - HistoryInfo::NotWritten => Ok(None), + HistoryInfo::NotYetWritten => Ok(None), HistoryInfo::InChangeset(changeset_block_number) => Ok(self .tx .cursor_dup_read::()? @@ -152,7 +155,7 @@ impl<'a, 'b, TX: DbTx<'a>> StateProvider for HistoricalStateProviderRef<'a, 'b, /// Get storage. fn storage(&self, address: Address, storage_key: StorageKey) -> Result> { match self.storage_history_lookup(address, storage_key)? { - HistoryInfo::NotWritten => Ok(None), + HistoryInfo::NotYetWritten => Ok(None), HistoryInfo::InChangeset(changeset_block_number) => Ok(Some( self.tx .cursor_dup_read::()? diff --git a/crates/storage/provider/src/test_utils/blocks.rs b/crates/storage/provider/src/test_utils/blocks.rs index 7df40d45e00..6752a0cfb72 100644 --- a/crates/storage/provider/src/test_utils/blocks.rs +++ b/crates/storage/provider/src/test_utils/blocks.rs @@ -125,6 +125,8 @@ fn block1(number: BlockNumber) -> (SealedBlockWithSenders, PostState) { topics: vec![H256::from_low_u64_be(1), H256::from_low_u64_be(2)], data: Bytes::default(), }], + #[cfg(feature = "optimism")] + deposit_nonce: None, }, ); @@ -168,6 +170,8 @@ fn block2(number: BlockNumber, parent_hash: H256) -> (SealedBlockWithSenders, Po topics: vec![H256::from_low_u64_be(3), H256::from_low_u64_be(4)], data: Bytes::default(), }], + #[cfg(feature = "optimism")] + deposit_nonce: None, }, ); diff --git a/crates/tracing/Cargo.toml b/crates/tracing/Cargo.toml index 444f1b23afd..8f652da2592 100644 --- a/crates/tracing/Cargo.toml +++ b/crates/tracing/Cargo.toml @@ -11,5 +11,5 @@ description = "tracing helpers" [dependencies] tracing.workspace = true tracing-subscriber = { version = "0.3", default-features = false, features = ["env-filter", "fmt"] } -tracing-appender = "0.2" +tracing-appender.workspace = true tracing-journald = "0.3" diff --git a/crates/tracing/src/lib.rs b/crates/tracing/src/lib.rs index 1695c336e2e..074b6dc9f07 100644 --- a/crates/tracing/src/lib.rs +++ b/crates/tracing/src/lib.rs @@ -68,6 +68,7 @@ where /// /// The boxed layer and a guard is returned. When the guard is dropped the buffer for the log /// file is immediately flushed to disk. Any events after the guard is dropped may be missed. +#[must_use = "tracing guard must be kept alive to flush events to disk"] pub fn file( filter: EnvFilter, dir: impl AsRef, diff --git a/crates/transaction-pool/Cargo.toml b/crates/transaction-pool/Cargo.toml index 6866cf1ea20..4f86ef60198 100644 --- a/crates/transaction-pool/Cargo.toml +++ b/crates/transaction-pool/Cargo.toml @@ -44,12 +44,21 @@ auto_impl = "1.0" # testing rand = { workspace = true, optional = true } paste = { version = "1.0", optional = true } +proptest = { version = "1.0", optional = true } [dev-dependencies] paste = "1.0" rand = "0.8" +proptest = "1.0" +criterion = "0.5" [features] default = ["serde"] serde = ["dep:serde"] test-utils = ["rand", "paste", "serde"] +arbitrary = ["proptest", "reth-primitives/arbitrary"] +optimism = ["reth-primitives/optimism"] + +[[bench]] +name = "reorder" +harness = false diff --git a/crates/transaction-pool/benches/reorder.rs b/crates/transaction-pool/benches/reorder.rs new file mode 100644 index 00000000000..129f1c17ff5 --- /dev/null +++ b/crates/transaction-pool/benches/reorder.rs @@ -0,0 +1,220 @@ +use criterion::{ + black_box, criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, +}; +use proptest::{ + prelude::*, + strategy::{Strategy, ValueTree}, + test_runner::TestRunner, +}; +use reth_transaction_pool::test_utils::MockTransaction; + +/// Transaction Pool trait for benching. +pub trait BenchTxPool: Default { + fn add_transaction(&mut self, tx: MockTransaction); + fn reorder(&mut self, base_fee: u64); +} + +pub fn txpool_reordering(c: &mut Criterion) { + let mut group = c.benchmark_group("Transaction Pool Reordering"); + + for seed_size in [1_000, 10_000, 50_000, 100_000] { + for input_size in [10, 100, 1_000] { + let (txs, new_txs, base_fee) = generate_test_data(seed_size, input_size); + + use implementations::*; + + // Vanilla sorting of unsorted collection + txpool_reordering_bench::( + &mut group, + "VecTxPoolSortStable", + txs.clone(), + new_txs.clone(), + base_fee, + ); + + // Unstable sorting of unsorted collection + txpool_reordering_bench::( + &mut group, + "VecTxPoolSortUnstable", + txs.clone(), + new_txs.clone(), + base_fee, + ); + + // BinaryHeap that is resorted on each update + txpool_reordering_bench::( + &mut group, + "BinaryHeapTxPool", + txs, + new_txs, + base_fee, + ); + } + } +} + +fn txpool_reordering_bench( + group: &mut BenchmarkGroup, + description: &str, + seed: Vec, + new_txs: Vec, + base_fee: u64, +) { + let setup = || { + let mut txpool = T::default(); + txpool.reorder(base_fee); + + for tx in seed.iter() { + txpool.add_transaction(tx.clone()); + } + (txpool, new_txs.clone()) + }; + + let group_id = format!( + "txpool | seed size: {} | input size: {} | {}", + seed.len(), + new_txs.len(), + description + ); + group.bench_function(group_id, |b| { + b.iter_with_setup(setup, |(mut txpool, new_txs)| { + black_box({ + // Reorder with new base fee + let bigger_base_fee = base_fee.saturating_add(10); + txpool.reorder(bigger_base_fee); + + // Reorder with new base fee after adding transactions. + for new_tx in new_txs { + txpool.add_transaction(new_tx); + } + let smaller_base_fee = base_fee.saturating_sub(10); + txpool.reorder(smaller_base_fee); + }) + }); + }); +} + +fn generate_test_data( + seed_size: usize, + input_size: usize, +) -> (Vec, Vec, u64) { + let config = ProptestConfig::default(); + let mut runner = TestRunner::new(config); + + let txs = prop::collection::vec(any::(), seed_size) + .new_tree(&mut runner) + .unwrap() + .current(); + + let new_txs = prop::collection::vec(any::(), input_size) + .new_tree(&mut runner) + .unwrap() + .current(); + + let base_fee = any::().new_tree(&mut runner).unwrap().current(); + + (txs, new_txs, base_fee) +} + +mod implementations { + use super::*; + use reth_transaction_pool::PoolTransaction; + use std::collections::BinaryHeap; + + /// This implementation appends the transactions and uses [Vec::sort_by] function for sorting. + #[derive(Default)] + pub struct VecTxPoolSortStable { + inner: Vec, + } + + impl BenchTxPool for VecTxPoolSortStable { + fn add_transaction(&mut self, tx: MockTransaction) { + self.inner.push(tx); + } + + fn reorder(&mut self, base_fee: u64) { + self.inner.sort_by(|a, b| { + a.effective_tip_per_gas(base_fee) + .expect("exists") + .cmp(&b.effective_tip_per_gas(base_fee).expect("exists")) + }) + } + } + + /// This implementation appends the transactions and uses [Vec::sort_unstable_by] function for + /// sorting. + #[derive(Default)] + pub struct VecTxPoolSortUnstable { + inner: Vec, + } + + impl BenchTxPool for VecTxPoolSortUnstable { + fn add_transaction(&mut self, tx: MockTransaction) { + self.inner.push(tx); + } + + fn reorder(&mut self, base_fee: u64) { + self.inner.sort_unstable_by(|a, b| { + a.effective_tip_per_gas(base_fee) + .expect("exists") + .cmp(&b.effective_tip_per_gas(base_fee).expect("exists")) + }) + } + } + + struct MockTransactionWithPriority { + tx: MockTransaction, + priority: u128, + } + + impl PartialEq for MockTransactionWithPriority { + fn eq(&self, other: &Self) -> bool { + self.priority.eq(&other.priority) + } + } + + impl Eq for MockTransactionWithPriority {} + + impl PartialOrd for MockTransactionWithPriority { + fn partial_cmp(&self, other: &Self) -> Option { + self.priority.partial_cmp(&other.priority) + } + } + + impl Ord for MockTransactionWithPriority { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.priority.cmp(&other.priority) + } + } + + /// This implementation uses BinaryHeap which is drained and reconstructed on each reordering. + #[derive(Default)] + pub struct BinaryHeapTxPool { + inner: BinaryHeap, + base_fee: Option, + } + + impl BenchTxPool for BinaryHeapTxPool { + fn add_transaction(&mut self, tx: MockTransaction) { + let priority = self + .base_fee + .as_ref() + .map(|bf| tx.effective_tip_per_gas(*bf).expect("set")) + .unwrap_or_default(); + self.inner.push(MockTransactionWithPriority { tx, priority }); + } + + fn reorder(&mut self, base_fee: u64) { + self.base_fee = Some(base_fee); + + let drained = self.inner.drain(); + self.inner = BinaryHeap::from_iter(drained.map(|mock| { + let priority = mock.tx.effective_tip_per_gas(base_fee).expect("set"); + MockTransactionWithPriority { tx: mock.tx, priority } + })); + } + } +} + +criterion_group!(reorder, txpool_reordering); +criterion_main!(reorder); diff --git a/crates/transaction-pool/src/config.rs b/crates/transaction-pool/src/config.rs index faf0c0156b4..aa6896305b4 100644 --- a/crates/transaction-pool/src/config.rs +++ b/crates/transaction-pool/src/config.rs @@ -7,6 +7,12 @@ pub const TXPOOL_SUBPOOL_MAX_TXS_DEFAULT: usize = 10_000; /// The default maximum allowed size of the given subpool. pub const TXPOOL_SUBPOOL_MAX_SIZE_MB_DEFAULT: usize = 20; +/// Default price bump (in %) for the transaction pool underpriced check. +pub const DEFAULT_PRICE_BUMP: u128 = 10; + +/// Replace blob price bump (in %) for the transaction pool underpriced check. +pub const REPLACE_BLOB_PRICE_BUMP: u128 = 100; + /// Configuration options for the Transaction pool. #[derive(Debug, Clone)] pub struct PoolConfig { @@ -18,6 +24,8 @@ pub struct PoolConfig { pub queued_limit: SubPoolLimit, /// Max number of executable transaction slots guaranteed per account pub max_account_slots: usize, + /// Price bump (in %) for the transaction pool underpriced check. + pub price_bump: u128, } impl Default for PoolConfig { @@ -27,6 +35,7 @@ impl Default for PoolConfig { basefee_limit: Default::default(), queued_limit: Default::default(), max_account_slots: TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER, + price_bump: PriceBumpConfig::default().default_price_bump, } } } @@ -57,3 +66,21 @@ impl Default for SubPoolLimit { } } } + +/// Price bump config (in %) for the transaction pool underpriced check. +#[derive(Debug, Clone)] +pub struct PriceBumpConfig { + /// Default price bump (in %) for the transaction pool underpriced check. + pub default_price_bump: u128, + /// Replace blob price bump (in %) for the transaction pool underpriced check. + pub replace_blob_tx_price_bump: u128, +} + +impl Default for PriceBumpConfig { + fn default() -> Self { + Self { + default_price_bump: DEFAULT_PRICE_BUMP, + replace_blob_tx_price_bump: REPLACE_BLOB_PRICE_BUMP, + } + } +} diff --git a/crates/transaction-pool/src/lib.rs b/crates/transaction-pool/src/lib.rs index d4c25f047e7..8c4ccc502db 100644 --- a/crates/transaction-pool/src/lib.rs +++ b/crates/transaction-pool/src/lib.rs @@ -10,8 +10,7 @@ rust_2018_idioms, unreachable_pub, missing_debug_implementations, - rustdoc::broken_intra_doc_links, - unused_crate_dependencies + rustdoc::broken_intra_doc_links )] #![doc(test( no_crate_inject, @@ -154,11 +153,12 @@ use tracing::{instrument, trace}; pub use crate::{ config::{ - PoolConfig, SubPoolLimit, TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER, - TXPOOL_SUBPOOL_MAX_SIZE_MB_DEFAULT, TXPOOL_SUBPOOL_MAX_TXS_DEFAULT, + PoolConfig, PriceBumpConfig, SubPoolLimit, DEFAULT_PRICE_BUMP, REPLACE_BLOB_PRICE_BUMP, + TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER, TXPOOL_SUBPOOL_MAX_SIZE_MB_DEFAULT, + TXPOOL_SUBPOOL_MAX_TXS_DEFAULT, }, error::PoolResult, - ordering::{GasCostOrdering, TransactionOrdering}, + ordering::{CoinbaseTipOrdering, Priority, TransactionOrdering}, pool::{ state::SubPool, AllTransactionsEvents, FullTransactionEvent, TransactionEvent, TransactionEvents, @@ -190,27 +190,6 @@ mod traits; /// Common test helpers for mocking a pool pub mod test_utils; -// TX_SLOT_SIZE is used to calculate how many data slots a single transaction -// takes up based on its size. The slots are used as DoS protection, ensuring -// that validating a new transaction remains a constant operation (in reality -// O(maxslots), where max slots are 4 currently). -pub(crate) const TX_SLOT_SIZE: usize = 32 * 1024; - -// TX_MAX_SIZE is the maximum size a single transaction can have. This field has -// non-trivial consequences: larger transactions are significantly harder and -// more expensive to propagate; larger transactions also take more resources -// to validate whether they fit into the pool or not. -pub(crate) const TX_MAX_SIZE: usize = 4 * TX_SLOT_SIZE; //128KB - -// Maximum bytecode to permit for a contract -pub(crate) const MAX_CODE_SIZE: usize = 24576; - -// Maximum initcode to permit in a creation transaction and create instructions -pub(crate) const MAX_INIT_CODE_SIZE: usize = 2 * MAX_CODE_SIZE; - -// Price bump (in %) for the transaction pool underpriced check -pub(crate) const PRICE_BUMP: u128 = 10; - /// A shareable, generic, customizable `TransactionPool` implementation. #[derive(Debug)] pub struct Pool { @@ -281,12 +260,12 @@ where } impl - Pool, GasCostOrdering> + Pool, CoinbaseTipOrdering> where Client: StateProviderFactory + Clone + 'static, { /// Returns a new [Pool] that uses the default [EthTransactionValidator] when validating - /// [PooledTransaction]s and ords via [GasCostOrdering] + /// [PooledTransaction]s and ords via [CoinbaseTipOrdering] /// /// # Example /// @@ -306,7 +285,7 @@ where validator: EthTransactionValidator, config: PoolConfig, ) -> Self { - Self::new(validator, GasCostOrdering::default(), config) + Self::new(validator, CoinbaseTipOrdering::default(), config) } } diff --git a/crates/transaction-pool/src/ordering.rs b/crates/transaction-pool/src/ordering.rs index 40ee1700661..ffa185f1f0e 100644 --- a/crates/transaction-pool/src/ordering.rs +++ b/crates/transaction-pool/src/ordering.rs @@ -2,6 +2,26 @@ use crate::traits::PoolTransaction; use reth_primitives::U256; use std::{fmt, marker::PhantomData}; +/// Priority of the transaction that can be missing. +/// +/// Transactions with missing priorities are ranked lower. +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)] +pub enum Priority { + /// The value of the priority of the transaction. + Value(T), + /// Missing priority due to ordering internals. + None, +} + +impl From> for Priority { + fn from(value: Option) -> Self { + match value { + Some(val) => Priority::Value(val), + None => Priority::None, + } + } +} + /// Transaction ordering trait to determine the order of transactions. /// /// Decides how transactions should be ordered within the pool, depending on a `Priority` value. @@ -11,42 +31,53 @@ pub trait TransactionOrdering: Send + Sync + 'static { /// Priority of a transaction. /// /// Higher is better. - type Priority: Ord + Clone + Default + fmt::Debug + Send + Sync; + type PriorityValue: Ord + Clone + Default + fmt::Debug + Send + Sync; /// The transaction type to determine the priority of. type Transaction: PoolTransaction; /// Returns the priority score for the given transaction. - fn priority(&self, transaction: &Self::Transaction) -> Self::Priority; + fn priority( + &self, + transaction: &Self::Transaction, + base_fee: u64, + ) -> Priority; } /// Default ordering for the pool. /// -/// The transactions are ordered by their gas cost. The higher the gas cost, -/// the higher the priority of this transaction is. +/// The transactions are ordered by their coinbase tip. +/// The higher the coinbase tip is, the higher the priority of the transaction. #[derive(Debug)] #[non_exhaustive] -pub struct GasCostOrdering(PhantomData); +pub struct CoinbaseTipOrdering(PhantomData); -impl TransactionOrdering for GasCostOrdering +impl TransactionOrdering for CoinbaseTipOrdering where T: PoolTransaction + 'static, { - type Priority = U256; + type PriorityValue = U256; type Transaction = T; - fn priority(&self, transaction: &Self::Transaction) -> Self::Priority { - transaction.gas_cost() + /// Source: . + /// + /// NOTE: The implementation is incomplete for missing base fee. + fn priority( + &self, + transaction: &Self::Transaction, + base_fee: u64, + ) -> Priority { + transaction.effective_tip_per_gas(base_fee).map(U256::from).into() } } -impl Default for GasCostOrdering { +impl Default for CoinbaseTipOrdering { fn default() -> Self { Self(Default::default()) } } -impl Clone for GasCostOrdering { +impl Clone for CoinbaseTipOrdering { fn clone(&self) -> Self { Self::default() } diff --git a/crates/transaction-pool/src/pool/best.rs b/crates/transaction-pool/src/pool/best.rs index 071063aeda5..c5190327f09 100644 --- a/crates/transaction-pool/src/pool/best.rs +++ b/crates/transaction-pool/src/pool/best.rs @@ -1,7 +1,6 @@ use crate::{ - identifier::TransactionId, - pool::pending::{PendingTransaction, PendingTransactionRef}, - PoolTransaction, TransactionOrdering, ValidPoolTransaction, + identifier::TransactionId, pool::pending::PendingTransaction, PoolTransaction, + TransactionOrdering, ValidPoolTransaction, }; use reth_primitives::H256 as TxHash; use std::{ @@ -54,12 +53,12 @@ impl Iterator for BestTransactionsWithBasefee { pub(crate) struct BestTransactions { /// Contains a copy of _all_ transactions of the pending pool at the point in time this /// iterator was created. - pub(crate) all: BTreeMap>>, + pub(crate) all: BTreeMap>, /// Transactions that can be executed right away: these have the expected nonce. /// /// Once an `independent` transaction with the nonce `N` is returned, it unlocks `N+1`, which /// then can be moved from the `all` set to the `independent` set. - pub(crate) independent: BTreeSet>, + pub(crate) independent: BTreeSet>, /// There might be the case where a yielded transactions is invalid, this will track it. pub(crate) invalid: HashSet, } @@ -74,7 +73,7 @@ impl BestTransactions { /// /// Note: for a transaction with nonce higher than the current on chain nonce this will always /// return an ancestor since all transaction in this pool are gapless. - pub(crate) fn ancestor(&self, id: &TransactionId) -> Option<&Arc>> { + pub(crate) fn ancestor(&self, id: &TransactionId) -> Option<&PendingTransaction> { self.all.get(&id.unchecked_ancestor()?) } } @@ -106,7 +105,7 @@ impl Iterator for BestTransactions { // Insert transactions that just got unlocked. if let Some(unlocked) = self.all.get(&best.unlocks()) { - self.independent.insert(unlocked.transaction.clone()); + self.independent.insert(unlocked.clone()); } return Some(best.transaction) @@ -133,7 +132,7 @@ mod tests { for nonce in 0..num_tx { let tx = tx.clone().rng_hash().with_nonce(nonce); let valid_tx = f.validated(tx); - pool.add_transaction(Arc::new(valid_tx)); + pool.add_transaction(Arc::new(valid_tx), 0); } let mut best = pool.best(); @@ -159,7 +158,7 @@ mod tests { for nonce in 0..num_tx { let tx = tx.clone().rng_hash().with_nonce(nonce); let valid_tx = f.validated(tx); - pool.add_transaction(Arc::new(valid_tx)); + pool.add_transaction(Arc::new(valid_tx), 0); } let mut best = pool.best(); diff --git a/crates/transaction-pool/src/pool/parked.rs b/crates/transaction-pool/src/pool/parked.rs index 1965fe8c8c1..740af02a971 100644 --- a/crates/transaction-pool/src/pool/parked.rs +++ b/crates/transaction-pool/src/pool/parked.rs @@ -306,10 +306,11 @@ pub(crate) struct QueuedOrd(Arc>); impl_ord_wrapper!(QueuedOrd); +// TODO: temporary solution for ordering the queued pool. impl Ord for QueuedOrd { fn cmp(&self, other: &Self) -> Ordering { - // Higher cost is better - self.gas_cost().cmp(&other.gas_cost()).then_with(|| + // Higher price is better + self.max_fee_per_gas().cmp(&self.max_fee_per_gas()).then_with(|| // Lower timestamp is better other.timestamp.cmp(&self.timestamp)) } diff --git a/crates/transaction-pool/src/pool/pending.rs b/crates/transaction-pool/src/pool/pending.rs index dd77997dcc7..0ec5be69b56 100644 --- a/crates/transaction-pool/src/pool/pending.rs +++ b/crates/transaction-pool/src/pool/pending.rs @@ -1,7 +1,7 @@ use crate::{ identifier::TransactionId, pool::{best::BestTransactions, size::SizeTracker}, - TransactionOrdering, ValidPoolTransaction, + Priority, TransactionOrdering, ValidPoolTransaction, }; use crate::pool::best::BestTransactionsWithBasefee; @@ -30,14 +30,14 @@ pub(crate) struct PendingPool { /// This way we can determine when transactions where submitted to the pool. submission_id: u64, /// _All_ Transactions that are currently inside the pool grouped by their identifier. - by_id: BTreeMap>>, + by_id: BTreeMap>, /// _All_ transactions sorted by priority - all: BTreeSet>, + all: BTreeSet>, /// Independent transactions that can be included directly and don't require other /// transactions. /// /// Sorted by their scoring value. - independent_transactions: BTreeSet>, + independent_transactions: BTreeSet>, /// Keeps track of the size of this pool. /// /// See also [`PoolTransaction::size`](crate::traits::PoolTransaction::size). @@ -103,19 +103,19 @@ impl PendingPool { pub(crate) fn best_with_unlocked( &self, unlocked: Vec>>, + base_fee: u64, ) -> BestTransactions { let mut best = self.best(); let mut submission_id = self.submission_id; for tx in unlocked { submission_id += 1; debug_assert!(!best.all.contains_key(tx.id()), "transaction already included"); - let priority = self.ordering.priority(&tx.transaction); + let priority = self.ordering.priority(&tx.transaction, base_fee); let tx_id = *tx.id(); - let transaction = PendingTransactionRef { submission_id, transaction: tx, priority }; + let transaction = PendingTransaction { submission_id, transaction: tx, priority }; if best.ancestor(&tx_id).is_none() { best.independent.insert(transaction.clone()); } - let transaction = Arc::new(PendingTransaction { transaction }); best.all.insert(tx_id, transaction); } @@ -126,25 +126,32 @@ impl PendingPool { pub(crate) fn all( &self, ) -> impl Iterator>> + '_ { - self.by_id.values().map(|tx| tx.transaction.transaction.clone()) + self.by_id.values().map(|tx| tx.transaction.clone()) } - /// Removes all transactions and their dependent transaction from the subpool that no longer - /// satisfy the given basefee (`tx.fee < basefee`) + /// Updates the pool with the new base fee. Reorders transactions by new priorities. Removes + /// from the subpool all transactions and their dependents that no longer satisfy the given + /// base fee (`tx.fee < base_fee`). /// /// Note: the transactions are not returned in a particular order. - pub(crate) fn enforce_basefee( + /// + /// # Returns + /// + /// Removed transactions that no longer satisfy the base fee. + pub(crate) fn update_base_fee( &mut self, - basefee: u64, + base_fee: u64, ) -> Vec>> { + // Create a collection for txs to remove . let mut to_remove = Vec::new(); + // Iterate over transactions, find the ones we need to remove and update others in place. { - let mut iter = self.by_id.iter().peekable(); + let mut iter = self.by_id.iter_mut().peekable(); while let Some((id, tx)) = iter.next() { - if tx.transaction.transaction.max_fee_per_gas() < basefee as u128 { - // this transaction no longer satisfies the basefee: remove it and all its - // descendants + if tx.transaction.max_fee_per_gas() < base_fee as u128 { + // This transaction no longer satisfies the basefee: remove it and all its + // descendants. to_remove.push(*id); 'this: while let Some((peek, _)) = iter.peek() { if peek.sender != id.sender { @@ -153,6 +160,13 @@ impl PendingPool { to_remove.push(**peek); iter.next(); } + } else { + // Update the transaction with new priority. + let new_priority = + self.ordering.priority(&tx.transaction.transaction, base_fee); + tx.priority = new_priority; + + self.all.insert(tx.clone()); } } } @@ -162,6 +176,10 @@ impl PendingPool { removed.push(self.remove_transaction(&id).expect("transaction exists")); } + // Clear ordered lists since the priority would be changed. + self.independent_transactions.clear(); + self.all.clear(); + removed } @@ -169,7 +187,7 @@ impl PendingPool { /// /// Note: for a transaction with nonce higher than the current on chain nonce this will always /// return an ancestor since all transaction in this pool are gapless. - fn ancestor(&self, id: &TransactionId) -> Option<&Arc>> { + fn ancestor(&self, id: &TransactionId) -> Option<&PendingTransaction> { self.by_id.get(&id.unchecked_ancestor()?) } @@ -178,33 +196,34 @@ impl PendingPool { /// # Panics /// /// if the transaction is already included - pub(crate) fn add_transaction(&mut self, tx: Arc>) { + pub(crate) fn add_transaction( + &mut self, + tx: Arc>, + base_fee: u64, + ) { assert!( !self.by_id.contains_key(tx.id()), "transaction already included {:?}", self.by_id.contains_key(tx.id()) ); - let tx_id = *tx.id(); - let submission_id = self.next_id(); - - let priority = self.ordering.priority(&tx.transaction); - // keep track of size self.size_of += tx.size(); - let transaction = PendingTransactionRef { submission_id, transaction: tx, priority }; + let tx_id = *tx.id(); + + let submission_id = self.next_id(); + let priority = self.ordering.priority(&tx.transaction, base_fee); + let tx = PendingTransaction { submission_id, transaction: tx, priority }; // If there's __no__ ancestor in the pool, then this transaction is independent, this is // guaranteed because this pool is gapless. if self.ancestor(&tx_id).is_none() { - self.independent_transactions.insert(transaction.clone()); + self.independent_transactions.insert(tx.clone()); } - self.all.insert(transaction.clone()); - - let transaction = Arc::new(PendingTransaction { transaction }); + self.all.insert(tx.clone()); - self.by_id.insert(tx_id, transaction); + self.by_id.insert(tx_id, tx); } /// Removes a _mined_ transaction from the pool. @@ -216,7 +235,7 @@ impl PendingPool { ) -> Option>> { // mark the next as independent if it exists if let Some(unlocked) = self.by_id.get(&id.descendant()) { - self.independent_transactions.insert(unlocked.transaction.clone()); + self.independent_transactions.insert(unlocked.clone()); }; self.remove_transaction(id) } @@ -229,10 +248,10 @@ impl PendingPool { id: &TransactionId, ) -> Option>> { let tx = self.by_id.remove(id)?; - self.all.remove(&tx.transaction); - self.size_of -= tx.transaction.transaction.size(); - self.independent_transactions.remove(&tx.transaction); - Some(tx.transaction.transaction.clone()) + self.all.remove(&tx); + self.size_of -= tx.transaction.size(); + self.independent_transactions.remove(&tx); + Some(tx.transaction.clone()) } fn next_id(&mut self) -> u64 { @@ -266,34 +285,22 @@ impl PendingPool { /// A transaction that is ready to be included in a block. pub(crate) struct PendingTransaction { - /// Reference to the actual transaction. - pub(crate) transaction: PendingTransactionRef, -} - -impl Clone for PendingTransaction { - fn clone(&self) -> Self { - Self { transaction: self.transaction.clone() } - } -} - -/// A transaction that is ready to be included in a block. -pub(crate) struct PendingTransactionRef { /// Identifier that tags when transaction was submitted in the pool. pub(crate) submission_id: u64, /// Actual transaction. pub(crate) transaction: Arc>, /// The priority value assigned by the used `Ordering` function. - pub(crate) priority: T::Priority, + pub(crate) priority: Priority, } -impl PendingTransactionRef { +impl PendingTransaction { /// The next transaction of the sender: `nonce + 1` pub(crate) fn unlocks(&self) -> TransactionId { self.transaction.transaction_id.descendant() } } -impl Clone for PendingTransactionRef { +impl Clone for PendingTransaction { fn clone(&self) -> Self { Self { submission_id: self.submission_id, @@ -303,21 +310,21 @@ impl Clone for PendingTransactionRef { } } -impl Eq for PendingTransactionRef {} +impl Eq for PendingTransaction {} -impl PartialEq for PendingTransactionRef { +impl PartialEq for PendingTransaction { fn eq(&self, other: &Self) -> bool { self.cmp(other) == Ordering::Equal } } -impl PartialOrd for PendingTransactionRef { +impl PartialOrd for PendingTransaction { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } -impl Ord for PendingTransactionRef { +impl Ord for PendingTransaction { fn cmp(&self, other: &Self) -> Ordering { // This compares by `priority` and only if two tx have the exact same priority this compares // the unique `submission_id`. This ensures that transactions with same priority are not @@ -338,15 +345,15 @@ mod tests { let mut f = MockTransactionFactory::default(); let mut pool = PendingPool::new(MockOrdering::default()); let tx = f.validated_arc(MockTransaction::eip1559().inc_price()); - pool.add_transaction(tx.clone()); + pool.add_transaction(tx.clone(), 0); assert!(pool.by_id.contains_key(tx.id())); assert_eq!(pool.len(), 1); - let removed = pool.enforce_basefee(0); + let removed = pool.update_base_fee(0); assert!(removed.is_empty()); - let removed = pool.enforce_basefee((tx.max_fee_per_gas() + 1) as u64); + let removed = pool.update_base_fee((tx.max_fee_per_gas() + 1) as u64); assert_eq!(removed.len(), 1); assert!(pool.is_empty()); } @@ -357,10 +364,10 @@ mod tests { let mut pool = PendingPool::new(MockOrdering::default()); let t = MockTransaction::eip1559().inc_price_by(10); let root_tx = f.validated_arc(t.clone()); - pool.add_transaction(root_tx.clone()); + pool.add_transaction(root_tx.clone(), 0); let descendant_tx = f.validated_arc(t.inc_nonce().decr_price()); - pool.add_transaction(descendant_tx.clone()); + pool.add_transaction(descendant_tx.clone(), 0); assert!(pool.by_id.contains_key(root_tx.id())); assert!(pool.by_id.contains_key(descendant_tx.id())); @@ -368,14 +375,14 @@ mod tests { assert_eq!(pool.independent_transactions.len(), 1); - let removed = pool.enforce_basefee(0); + let removed = pool.update_base_fee(0); assert!(removed.is_empty()); // two dependent tx in the pool with decreasing fee { let mut pool2 = pool.clone(); - let removed = pool2.enforce_basefee((descendant_tx.max_fee_per_gas() + 1) as u64); + let removed = pool2.update_base_fee((descendant_tx.max_fee_per_gas() + 1) as u64); assert_eq!(removed.len(), 1); assert_eq!(pool2.len(), 1); // descendant got popped @@ -384,7 +391,7 @@ mod tests { } // remove root transaction via fee - let removed = pool.enforce_basefee((root_tx.max_fee_per_gas() + 1) as u64); + let removed = pool.update_base_fee((root_tx.max_fee_per_gas() + 1) as u64); assert_eq!(removed.len(), 2); assert!(pool.is_empty()); } diff --git a/crates/transaction-pool/src/pool/txpool.rs b/crates/transaction-pool/src/pool/txpool.rs index cd0d607387d..29220699876 100644 --- a/crates/transaction-pool/src/pool/txpool.rs +++ b/crates/transaction-pool/src/pool/txpool.rs @@ -13,8 +13,7 @@ use crate::{ AddedPendingTransaction, AddedTransaction, OnNewCanonicalStateOutcome, }, traits::{BlockInfo, PoolSize}, - PoolConfig, PoolResult, PoolTransaction, TransactionOrdering, ValidPoolTransaction, PRICE_BUMP, - U256, + PoolConfig, PoolResult, PoolTransaction, TransactionOrdering, ValidPoolTransaction, U256, }; use fnv::FnvHashMap; use reth_primitives::{ @@ -149,7 +148,8 @@ impl TxPool { } Ordering::Greater => { // increased base fee: recheck pending pool and remove all that are no longer valid - for tx in self.pending_pool.enforce_basefee(pending_basefee) { + let removed = self.pending_pool.update_base_fee(pending_basefee); + for tx in removed { let to = { let tx = self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set"); @@ -162,7 +162,8 @@ impl TxPool { } Ordering::Less => { // decreased base fee: recheck basefee pool and promote all that are now valid - for tx in self.basefee_pool.enforce_basefee(pending_basefee) { + let removed = self.basefee_pool.enforce_basefee(pending_basefee); + for tx in removed { let to = { let tx = self.all_transactions.txs.get_mut(tx.id()).expect("tx exists in set"); @@ -183,6 +184,7 @@ impl TxPool { let BlockInfo { last_seen_block_hash, last_seen_block_number, pending_basefee } = info; self.all_transactions.last_seen_block_hash = last_seen_block_hash; self.all_transactions.last_seen_block_number = last_seen_block_number; + self.all_transactions.pending_basefee = pending_basefee; self.update_basefee(pending_basefee) } @@ -211,7 +213,10 @@ impl TxPool { // base fee decreased, we need to move transactions from the basefee pool to the // pending pool let unlocked = self.basefee_pool.satisfy_base_fee_transactions(basefee); - Box::new(self.pending_pool.best_with_unlocked(unlocked)) + Box::new( + self.pending_pool + .best_with_unlocked(unlocked, self.all_transactions.pending_basefee), + ) } } } @@ -545,7 +550,7 @@ impl TxPool { self.queued_pool.add_transaction(tx); } SubPool::Pending => { - self.pending_pool.add_transaction(tx); + self.pending_pool.add_transaction(tx, self.all_transactions.pending_basefee); } SubPool::BaseFee => { self.basefee_pool.add_transaction(tx); @@ -1105,7 +1110,7 @@ impl AllTransactions { if Self::is_underpriced( transaction.as_ref(), entry.get().transaction.as_ref(), - PRICE_BUMP, + PoolConfig::default().price_bump, ) { return Err(InsertErr::Underpriced { transaction: pool_tx.transaction, diff --git a/crates/transaction-pool/src/test_utils/mock.rs b/crates/transaction-pool/src/test_utils/mock.rs index 62be5c66ef1..c03a45168e1 100644 --- a/crates/transaction-pool/src/test_utils/mock.rs +++ b/crates/transaction-pool/src/test_utils/mock.rs @@ -4,7 +4,7 @@ use crate::{ identifier::{SenderIdentifiers, TransactionId}, pool::txpool::TxPool, traits::TransactionOrigin, - PoolTransaction, TransactionOrdering, ValidPoolTransaction, + PoolTransaction, Priority, TransactionOrdering, ValidPoolTransaction, }; use paste::paste; use rand::{ @@ -14,7 +14,7 @@ use rand::{ use reth_primitives::{ constants::MIN_PROTOCOL_BASE_FEE, hex, Address, FromRecoveredTransaction, IntoRecoveredTransaction, Signature, Transaction, TransactionKind, TransactionSigned, - TransactionSignedEcRecovered, TxEip1559, TxHash, TxLegacy, TxType, H256, U128, U256, + TransactionSignedEcRecovered, TxEip1559, TxEip2930, TxHash, TxLegacy, TxType, H256, U128, U256, }; use std::{ops::Range, sync::Arc, time::Instant}; @@ -22,6 +22,15 @@ pub(crate) type MockTxPool = TxPool; pub type MockValidTx = ValidPoolTransaction; +#[cfg(feature = "optimism")] +use reth_primitives::DEPOSIT_TX_TYPE; + +#[cfg(feature = "optimism")] +use reth_primitives::TxDeposit; + +#[cfg(feature = "optimism")] +use reth_primitives::Bytes; + /// Create an empty `TxPool` pub(crate) fn mock_tx_pool() -> MockTxPool { MockTxPool::new(Default::default(), Default::default()) @@ -32,6 +41,10 @@ macro_rules! set_value { ($this:ident => $field:ident) => { let new_value = $field; match $this { + #[cfg(feature = "optimism")] + MockTransaction::DepositTx { ref mut $field, .. } => { + *$field = new_value; + } MockTransaction::Legacy { ref mut $field, .. } => { *$field = new_value; } @@ -46,6 +59,8 @@ macro_rules! set_value { macro_rules! get_value { ($this:ident => $field:ident) => { match $this { + #[cfg(feature = "optimism")] + MockTransaction::DepositTx { $field, .. } => $field, MockTransaction::Legacy { $field, .. } => $field, MockTransaction::Eip1559 { $field, .. } => $field, } @@ -99,6 +114,18 @@ pub enum MockTransaction { to: TransactionKind, value: U256, }, + #[cfg(feature = "optimism")] + DepositTx { + hash: H256, + sender: Address, + nonce: u64, + to: TransactionKind, + mint: Option, + gas_limit: u64, + is_system_transaction: bool, + input: Bytes, + value: U256, + }, } // === impl MockTransaction === @@ -185,6 +212,8 @@ impl MockTransaction { pub fn set_gas_price(&mut self, val: u128) -> &mut Self { match self { + #[cfg(feature = "optimism")] + MockTransaction::DepositTx { .. } => {} MockTransaction::Legacy { gas_price, .. } => { *gas_price = val; } @@ -198,6 +227,8 @@ impl MockTransaction { pub fn with_gas_price(mut self, val: u128) -> Self { match self { + #[cfg(feature = "optimism")] + MockTransaction::DepositTx { .. } => {} MockTransaction::Legacy { ref mut gas_price, .. } => { *gas_price = val; } @@ -215,6 +246,8 @@ impl MockTransaction { pub fn get_gas_price(&self) -> u128 { match self { + #[cfg(feature = "optimism")] + MockTransaction::DepositTx { .. } => 0, MockTransaction::Legacy { gas_price, .. } => *gas_price, MockTransaction::Eip1559 { max_fee_per_gas, .. } => *max_fee_per_gas, } @@ -299,6 +332,8 @@ impl MockTransaction { impl PoolTransaction for MockTransaction { fn hash(&self) -> &TxHash { match self { + #[cfg(feature = "optimism")] + MockTransaction::DepositTx { hash, .. } => hash, MockTransaction::Legacy { hash, .. } => hash, MockTransaction::Eip1559 { hash, .. } => hash, } @@ -306,6 +341,8 @@ impl PoolTransaction for MockTransaction { fn sender(&self) -> Address { match self { + #[cfg(feature = "optimism")] + MockTransaction::DepositTx { sender, .. } => *sender, MockTransaction::Legacy { sender, .. } => *sender, MockTransaction::Eip1559 { sender, .. } => *sender, } @@ -313,6 +350,8 @@ impl PoolTransaction for MockTransaction { fn nonce(&self) -> u64 { match self { + #[cfg(feature = "optimism")] + MockTransaction::DepositTx { nonce, .. } => *nonce, MockTransaction::Legacy { nonce, .. } => *nonce, MockTransaction::Eip1559 { nonce, .. } => *nonce, } @@ -320,6 +359,8 @@ impl PoolTransaction for MockTransaction { fn cost(&self) -> U256 { match self { + #[cfg(feature = "optimism")] + MockTransaction::DepositTx { .. } => U256::ZERO, MockTransaction::Legacy { gas_price, value, gas_limit, .. } => { U256::from(*gas_limit) * U256::from(*gas_price) + *value } @@ -329,17 +370,6 @@ impl PoolTransaction for MockTransaction { } } - fn gas_cost(&self) -> U256 { - match self { - MockTransaction::Legacy { gas_price, gas_limit, .. } => { - U256::from(*gas_limit) * U256::from(*gas_price) - } - MockTransaction::Eip1559 { max_fee_per_gas, gas_limit, .. } => { - U256::from(*gas_limit) * U256::from(*max_fee_per_gas) - } - } - } - fn gas_limit(&self) -> u64 { self.get_gas_limit() } @@ -348,11 +378,15 @@ impl PoolTransaction for MockTransaction { match self { MockTransaction::Legacy { gas_price, .. } => *gas_price, MockTransaction::Eip1559 { max_fee_per_gas, .. } => *max_fee_per_gas, + #[cfg(feature = "optimism")] + MockTransaction::DepositTx { .. } => 0, } } fn max_priority_fee_per_gas(&self) -> Option { match self { + #[cfg(feature = "optimism")] + MockTransaction::DepositTx { .. } => None, MockTransaction::Legacy { .. } => None, MockTransaction::Eip1559 { max_priority_fee_per_gas, .. } => { Some(*max_priority_fee_per_gas) @@ -360,8 +394,32 @@ impl PoolTransaction for MockTransaction { } } + fn effective_tip_per_gas(&self, base_fee: u64) -> Option { + let base_fee = base_fee as u128; + let max_fee_per_gas = self.max_fee_per_gas(); + if max_fee_per_gas < base_fee { + return None + } + + let fee = max_fee_per_gas - base_fee; + if let Some(priority_fee) = self.max_priority_fee_per_gas() { + return Some(fee.min(priority_fee)) + } + + Some(fee) + } + + fn priority_fee_or_price(&self) -> u128 { + match self { + MockTransaction::Legacy { gas_price, .. } => *gas_price, + MockTransaction::Eip1559 { max_priority_fee_per_gas, .. } => *max_priority_fee_per_gas, + } + } + fn kind(&self) -> &TransactionKind { match self { + #[cfg(feature = "optimism")] + MockTransaction::DepositTx { to, .. } => to, MockTransaction::Legacy { to, .. } => to, MockTransaction::Eip1559 { to, .. } => to, } @@ -373,6 +431,8 @@ impl PoolTransaction for MockTransaction { fn tx_type(&self) -> u8 { match self { + #[cfg(feature = "optimism")] + MockTransaction::DepositTx { .. } => TxType::DEPOSIT.into(), MockTransaction::Legacy { .. } => TxType::Legacy.into(), MockTransaction::Eip1559 { .. } => TxType::EIP1559.into(), } @@ -393,6 +453,27 @@ impl FromRecoveredTransaction for MockTransaction { let transaction = tx.into_signed(); let hash = transaction.hash(); match transaction.transaction { + #[cfg(feature = "optimism")] + Transaction::Deposit(TxDeposit { + source_hash, + from, + to, + mint, + value, + gas_limit, + is_system_transaction, + input, + }) => MockTransaction::DepositTx { + nonce: 0u64, + hash: source_hash, + sender: from, + to, + mint, + value: U256::from(value), + gas_limit, + is_system_transaction, + input, + }, Transaction::Legacy(TxLegacy { chain_id, nonce, @@ -461,6 +542,66 @@ impl IntoRecoveredTransaction for MockTransaction { } } +#[cfg(any(test, feature = "arbitrary"))] +impl proptest::arbitrary::Arbitrary for MockTransaction { + type Parameters = (); + type Strategy = proptest::strategy::BoxedStrategy; + + fn arbitrary_with(_: Self::Parameters) -> Self::Strategy { + use proptest::prelude::{any, Strategy}; + + any::<(Transaction, Address, H256)>() + .prop_map(|(tx, sender, tx_hash)| match &tx { + Transaction::Legacy(TxLegacy { + nonce, + gas_price, + gas_limit, + to, + value, + input, + .. + }) | + Transaction::Eip2930(TxEip2930 { + nonce, + gas_price, + gas_limit, + to, + value, + input, + .. + }) => MockTransaction::Legacy { + sender, + hash: tx_hash, + nonce: *nonce, + gas_price: *gas_price, + gas_limit: *gas_limit, + to: *to, + value: U256::from(*value), + }, + Transaction::Eip1559(TxEip1559 { + nonce, + gas_limit, + max_fee_per_gas, + max_priority_fee_per_gas, + to, + value, + input, + .. + }) => MockTransaction::Eip1559 { + sender, + hash: tx_hash, + nonce: *nonce, + max_fee_per_gas: *max_fee_per_gas, + max_priority_fee_per_gas: *max_priority_fee_per_gas, + gas_limit: *gas_limit, + to: *to, + value: U256::from(*value), + }, + }) + .boxed() + } +} + #[derive(Default)] pub struct MockTransactionFactory { pub(crate) ids: SenderIdentifiers, @@ -513,11 +654,15 @@ impl MockTransactionFactory { pub struct MockOrdering; impl TransactionOrdering for MockOrdering { - type Priority = U256; + type PriorityValue = U256; type Transaction = MockTransaction; - fn priority(&self, transaction: &Self::Transaction) -> Self::Priority { - transaction.gas_cost() + fn priority( + &self, + transaction: &Self::Transaction, + base_fee: u64, + ) -> Priority { + transaction.effective_tip_per_gas(base_fee).map(U256::from).into() } } @@ -559,5 +704,5 @@ fn test_mock_priority() { let o = MockOrdering; let lo = MockTransaction::eip1559().with_gas_limit(100_000); let hi = lo.next().inc_price(); - assert!(o.priority(&hi) > o.priority(&lo)); + assert!(o.priority(&hi, 0) > o.priority(&lo, 0)); } diff --git a/crates/transaction-pool/src/traits.rs b/crates/transaction-pool/src/traits.rs index 9d3319832ab..407a4b03521 100644 --- a/crates/transaction-pool/src/traits.rs +++ b/crates/transaction-pool/src/traits.rs @@ -470,12 +470,6 @@ pub trait PoolTransaction: /// For legacy transactions: `gas_price * gas_limit + tx_value`. fn cost(&self) -> U256; - /// Returns the gas cost for this transaction. - /// - /// For EIP-1559 transactions: `max_fee_per_gas * gas_limit`. - /// For legacy transactions: `gas_price * gas_limit`. - fn gas_cost(&self) -> U256; - /// Amount of gas that should be used in executing this transaction. This is paid up-front. fn gas_limit(&self) -> u64; @@ -491,6 +485,16 @@ pub trait PoolTransaction: /// This will return `None` for non-EIP1559 transactions fn max_priority_fee_per_gas(&self) -> Option; + /// Returns the effective tip for this transaction. + /// + /// 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; + + /// Returns the max priority fee per gas if the transaction is an EIP-1559 transaction, and + /// otherwise returns the gas price. + fn priority_fee_or_price(&self) -> u128; + /// Returns the transaction's [`TransactionKind`], which is the address of the recipient or /// [`TransactionKind::Create`] if the transaction is a contract creation. fn kind(&self) -> &TransactionKind; @@ -525,13 +529,27 @@ pub struct PooledTransaction { /// For EIP-1559 transactions: `max_fee_per_gas * gas_limit + tx_value`. /// For legacy transactions: `gas_price * gas_limit + tx_value`. pub(crate) cost: U256, - - /// For EIP-1559 transactions: `max_fee_per_gas * gas_limit`. - /// For legacy transactions: `gas_price * gas_limit`. - pub(crate) gas_cost: U256, } impl PooledTransaction { + /// Create new instance of [Self]. + pub fn new(transaction: TransactionSignedEcRecovered) -> Self { + let gas_cost = match &transaction.transaction { + Transaction::Legacy(t) => U256::from(t.gas_price) * U256::from(t.gas_limit), + Transaction::Eip2930(t) => U256::from(t.gas_price) * U256::from(t.gas_limit), + Transaction::Eip1559(t) => U256::from(t.max_fee_per_gas) * U256::from(t.gas_limit), + #[cfg(feature = "optimism")] + Transaction::Deposit(t) => { + // Gas price is always set to 0 for deposits in order to zero out ETH refunds, + // because they already pay for their gas on L1. + U256::ZERO + } + }; + let cost = gas_cost + U256::from(transaction.value()); + + Self { transaction, cost } + } + /// Return the reference to the underlying transaction. pub fn transaction(&self) -> &TransactionSignedEcRecovered { &self.transaction @@ -562,14 +580,6 @@ impl PoolTransaction for PooledTransaction { self.cost } - /// Returns the gas cost for this transaction. - /// - /// For EIP-1559 transactions: `max_fee_per_gas * gas_limit + tx_value`. - /// For legacy transactions: `gas_price * gas_limit + tx_value`. - fn gas_cost(&self) -> U256 { - self.gas_cost - } - /// Amount of gas that should be used in executing this transaction. This is paid up-front. fn gas_limit(&self) -> u64 { self.transaction.gas_limit() @@ -585,6 +595,8 @@ impl PoolTransaction for PooledTransaction { Transaction::Legacy(tx) => tx.gas_price, Transaction::Eip2930(tx) => tx.gas_price, Transaction::Eip1559(tx) => tx.max_fee_per_gas, + #[cfg(feature = "optimism")] + Transaction::Deposit(_) => 0, } } @@ -596,9 +608,25 @@ impl PoolTransaction for PooledTransaction { Transaction::Legacy(_) => None, Transaction::Eip2930(_) => None, Transaction::Eip1559(tx) => Some(tx.max_priority_fee_per_gas), + #[cfg(feature = "optimism")] + Transaction::Deposit(_) => None, } } + /// Returns the effective tip for this transaction. + /// + /// 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(base_fee) + } + + /// Returns the max priority fee per gas if the transaction is an EIP-1559 transaction, and + /// otherwise returns the gas price. + fn priority_fee_or_price(&self) -> u128 { + self.transaction.priority_fee_or_price() + } + /// Returns the transaction's [`TransactionKind`], which is the address of the recipient or /// [`TransactionKind::Create`] if the transaction is a contract creation. fn kind(&self) -> &TransactionKind { @@ -628,14 +656,7 @@ impl PoolTransaction for PooledTransaction { impl FromRecoveredTransaction for PooledTransaction { fn from_recovered_transaction(tx: TransactionSignedEcRecovered) -> Self { - let gas_cost = match &tx.transaction { - Transaction::Legacy(t) => U256::from(t.gas_price) * U256::from(t.gas_limit), - Transaction::Eip2930(t) => U256::from(t.gas_price) * U256::from(t.gas_limit), - Transaction::Eip1559(t) => U256::from(t.max_fee_per_gas) * U256::from(t.gas_limit), - }; - let cost = gas_cost + U256::from(tx.value()); - - PooledTransaction { transaction: tx, cost, gas_cost } + PooledTransaction::new(tx) } } diff --git a/crates/transaction-pool/src/validate/constants.rs b/crates/transaction-pool/src/validate/constants.rs new file mode 100644 index 00000000000..040087bdb02 --- /dev/null +++ b/crates/transaction-pool/src/validate/constants.rs @@ -0,0 +1,17 @@ +/// TX_SLOT_SIZE is used to calculate how many data slots a single transaction +/// takes up based on its size. The slots are used as DoS protection, ensuring +/// that validating a new transaction remains a constant operation (in reality +/// O(maxslots), where max slots are 4 currently). +pub const TX_SLOT_SIZE: usize = 32 * 1024; + +/// TX_MAX_SIZE is the maximum size a single transaction can have. This field has +/// non-trivial consequences: larger transactions are significantly harder and +/// more expensive to propagate; larger transactions also take more resources +/// to validate whether they fit into the pool or not. +pub const TX_MAX_SIZE: usize = 4 * TX_SLOT_SIZE; // 128KB + +/// Maximum bytecode to permit for a contract +pub const MAX_CODE_SIZE: usize = 24576; + +/// Maximum initcode to permit in a creation transaction and create instructions +pub const MAX_INIT_CODE_SIZE: usize = 2 * MAX_CODE_SIZE; diff --git a/crates/transaction-pool/src/validate/eth.rs b/crates/transaction-pool/src/validate/eth.rs index 2a4bbbf8ec2..bfcf04028ad 100644 --- a/crates/transaction-pool/src/validate/eth.rs +++ b/crates/transaction-pool/src/validate/eth.rs @@ -3,8 +3,11 @@ use crate::{ error::InvalidPoolTransactionError, traits::{PoolTransaction, TransactionOrigin}, - validate::{task::ValidationJobSender, TransactionValidatorError, ValidationTask}, - TransactionValidationOutcome, TransactionValidator, MAX_INIT_CODE_SIZE, TX_MAX_SIZE, + validate::{ + task::ValidationJobSender, TransactionValidatorError, ValidationTask, MAX_INIT_CODE_SIZE, + TX_MAX_SIZE, + }, + TransactionValidationOutcome, TransactionValidator, }; use reth_primitives::{ constants::ETHEREUM_BLOCK_GAS_LIMIT, ChainSpec, InvalidTransactionError, EIP1559_TX_TYPE_ID, @@ -137,6 +140,8 @@ pub struct EthTransactionValidatorBuilder { /// /// Default is 1 additional_tasks: usize, + /// Toggle to determine if a local transaction should be propagated + propagate_local_transactions: bool, } impl EthTransactionValidatorBuilder { @@ -150,6 +155,8 @@ impl EthTransactionValidatorBuilder { block_gas_limit: ETHEREUM_BLOCK_GAS_LIMIT, minimum_priority_fee: None, additional_tasks: 1, + // default to true, can potentially take this as a param in the future + propagate_local_transactions: true, } } @@ -185,6 +192,23 @@ impl EthTransactionValidatorBuilder { self.eip1559 = eip1559; self } + /// Sets toggle to propagate transactions received locally by this client (e.g + /// transactions from eth_Sendtransaction to this nodes' RPC server) + /// + /// If set to false, only transactions received by network peers (via + /// p2p) will be marked as propagated in the local transaction pool and returned on a + /// GetPooledTransactions p2p request + pub fn set_propagate_local_transactions(mut self, propagate_local_txs: bool) -> Self { + self.propagate_local_transactions = propagate_local_txs; + self + } + /// Disables propagating transactions recieved locally by this client + /// + /// For more information, check docs for set_propagate_local_transactions + pub fn no_local_transaction_propagation(mut self) -> Self { + self.propagate_local_transactions = false; + self + } /// Sets a minimum priority fee that's enforced for acceptance into the pool. pub fn with_minimum_priority_fee(mut self, minimum_priority_fee: u128) -> Self { @@ -219,6 +243,7 @@ impl EthTransactionValidatorBuilder { block_gas_limit, minimum_priority_fee, additional_tasks, + propagate_local_transactions, } = self; let inner = EthTransactionValidatorInner { @@ -229,6 +254,7 @@ impl EthTransactionValidatorBuilder { eip1559, block_gas_limit, minimum_priority_fee, + propagate_local_transactions, _marker: Default::default(), }; @@ -274,6 +300,8 @@ struct EthTransactionValidatorInner { minimum_priority_fee: Option, /// Marker for the transaction type _marker: PhantomData, + /// Toggle to determine if a local transaction should be propagated + propagate_local_transactions: bool, } // === impl EthTransactionValidatorInner === @@ -432,7 +460,9 @@ where balance: account.balance, state_nonce: account.nonce, transaction, - propagate: true, + // by this point assume all external transactions should be propagated + propagate: matches!(origin, TransactionOrigin::External) || + self.propagate_local_transactions, } } } diff --git a/crates/transaction-pool/src/validate/mod.rs b/crates/transaction-pool/src/validate/mod.rs index 2e96099f935..aabe58845cc 100644 --- a/crates/transaction-pool/src/validate/mod.rs +++ b/crates/transaction-pool/src/validate/mod.rs @@ -10,6 +10,7 @@ use reth_primitives::{ }; use std::{fmt, time::Instant}; +mod constants; mod eth; mod task; @@ -19,6 +20,9 @@ pub use eth::{EthTransactionValidator, EthTransactionValidatorBuilder}; /// A spawnable task that performs transaction validation. pub use task::ValidationTask; +/// Validation constants. +pub use constants::{MAX_CODE_SIZE, MAX_INIT_CODE_SIZE, TX_MAX_SIZE, TX_SLOT_SIZE}; + /// A Result type returned after checking a transaction's validity. #[derive(Debug)] pub enum TransactionValidationOutcome { @@ -164,14 +168,6 @@ impl ValidPoolTransaction { self.transaction.cost() } - /// Returns the effective tip for this transaction. - /// - /// For EIP-1559 transactions: `max_fee_per_gas * gas_limit`. - /// For legacy transactions: `gas_price * gas_limit`. - pub fn gas_cost(&self) -> U256 { - self.transaction.gas_cost() - } - /// Returns the EIP-1559 Max base fee the caller is willing to pay. /// /// For legacy transactions this is `gas_price`. @@ -179,6 +175,20 @@ impl ValidPoolTransaction { self.transaction.max_fee_per_gas() } + /// Returns the effective tip for this transaction. + /// + /// For EIP-1559 transactions: `min(max_fee_per_gas - base_fee, max_priority_fee_per_gas)`. + /// For legacy transactions: `gas_price - base_fee`. + pub fn effective_tip_per_gas(&self, base_fee: u64) -> Option { + self.transaction.effective_tip_per_gas(base_fee) + } + + /// Returns the max priority fee per gas if the transaction is an EIP-1559 transaction, and + /// otherwise returns the gas price. + pub fn priority_fee_or_price(&self) -> u128 { + self.transaction.priority_fee_or_price() + } + /// Maximum amount of gas that the transaction is allowed to consume. pub fn gas_limit(&self) -> u64 { self.transaction.gas_limit() diff --git a/crates/trie/src/updates.rs b/crates/trie/src/updates.rs index b60df4aa78a..dc5c086e0d7 100644 --- a/crates/trie/src/updates.rs +++ b/crates/trie/src/updates.rs @@ -22,7 +22,7 @@ pub enum TrieKey { } /// The operation to perform on the trie. -#[derive(Debug, Clone)] +#[derive(PartialEq, Eq, Debug, Clone)] pub enum TrieOp { /// Delete the node entry. Delete, diff --git a/examples/db-access.rs b/examples/db-access.rs index 5b8aec77f4f..60089dba75b 100644 --- a/examples/db-access.rs +++ b/examples/db-access.rs @@ -186,8 +186,8 @@ fn receipts_provider_example eyre::Result<()> { let pool = reth_transaction_pool::Pool::new( OkValidator::default(), - GasCostOrdering::default(), + CoinbaseTipOrdering::default(), Default::default(), ); diff --git a/testing/ef-tests/Cargo.toml b/testing/ef-tests/Cargo.toml index d415acde44c..7145273a931 100644 --- a/testing/ef-tests/Cargo.toml +++ b/testing/ef-tests/Cargo.toml @@ -23,5 +23,4 @@ tokio = "1.28.1" walkdir = "2.3.3" serde = "1.0.163" serde_json.workspace = true -thiserror.workspace = true -serde_bytes = "0.11.9" \ No newline at end of file +thiserror.workspace = true \ No newline at end of file diff --git a/testing/ef-tests/src/models.rs b/testing/ef-tests/src/models.rs index 52da1fc0259..989a0dd9e7f 100644 --- a/testing/ef-tests/src/models.rs +++ b/testing/ef-tests/src/models.rs @@ -36,20 +36,6 @@ pub struct BlockchainTest { #[serde(default)] /// Engine spec. pub self_engine: SealEngine, - #[serde(rename = "_info")] - #[allow(unused)] - info: BlockchainTestInfo, -} - -#[derive(Debug, PartialEq, Eq, Deserialize)] -struct BlockchainTestInfo { - #[serde(rename = "filling-rpc-server")] - #[allow(unused)] - // One test has an invalid string in this field, which breaks our CI: - // https://github.com/ethereum/tests/blob/6c252923bdd1bd5a70f680df1214f866f76839db/GeneralStateTests/stTransactionTest/ValueOverflow.json#L5 - // By using `serde_bytes::ByteBuf`, we ignore the validation of this field as a string. - // TODO(alexey): remove when `ethereum/tests` is fixed - filling_rpc_server: serde_bytes::ByteBuf, } /// A block header in an Ethereum blockchain test. @@ -114,6 +100,8 @@ impl From
for SealedHeader { parent_hash: value.parent_hash, logs_bloom: value.bloom, withdrawals_root: value.withdrawals_root, + blob_gas_used: None, + excess_blob_gas: None, }; header.seal(value.hash) }