diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index ddf45554aa..7b877a9730 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -14,7 +14,7 @@ jobs: codspeed: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: submodules: true - uses: dtolnay/rust-toolchain@stable diff --git a/.github/workflows/book.yml b/.github/workflows/book.yml index 919f41802f..7d5ebc6cad 100644 --- a/.github/workflows/book.yml +++ b/.github/workflows/book.yml @@ -17,7 +17,7 @@ jobs: name: test steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Install mdbook run: | @@ -42,7 +42,7 @@ jobs: name: lint steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Install mdbook-linkcheck run: | @@ -58,7 +58,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: fetch-depth: 0 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4c003b11bd..e7a23aeaf0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,11 +23,16 @@ jobs: rust: ["1.88", "stable", "nightly"] flags: ["--no-default-features", "", "--all-features"] steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} - uses: Swatinem/rust-cache@v2 + - name: Install GMP (all-features) + if: contains(matrix.flags, '--all-features') + run: | + sudo apt-get update + sudo apt-get install -y libgmp-dev - run: cargo test --workspace ${{ matrix.flags }} check-no-std: @@ -40,7 +45,7 @@ jobs: target: ["riscv32imac-unknown-none-elf", "riscv64imac-unknown-none-elf"] features: [""] steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable with: targets: ${{ matrix.target }} @@ -58,7 +63,7 @@ jobs: matrix: features: ["", "serde", "std", "std,map-foldhash"] steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable - run: cargo check --no-default-features -p revm --features=${{ matrix.features }} @@ -67,12 +72,16 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 30 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable - uses: taiki-e/install-action@cargo-hack - uses: Swatinem/rust-cache@v2 with: cache-on-failure: true + - name: Install GMP + run: | + sudo apt-get update + sudo apt-get install -y libgmp-dev - name: cargo hack run: cargo hack check --feature-powerset --depth 1 @@ -81,10 +90,14 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 30 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable with: components: clippy + - name: Install GMP + run: | + sudo apt-get update + sudo apt-get install -y libgmp-dev - run: cargo clippy --workspace --all-targets --all-features env: RUSTFLAGS: -Dwarnings @@ -94,10 +107,14 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 30 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable with: components: rust-docs + - name: Install GMP + run: | + sudo apt-get update + sudo apt-get install -y libgmp-dev - run: cargo doc --workspace --all-features --no-deps --document-private-items env: RUSTDOCFLAGS: "--cfg docsrs -D warnings" @@ -107,7 +124,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 30 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@stable with: components: rustfmt @@ -117,7 +134,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 20 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: run zepter run: | cargo install zepter -f --locked @@ -128,5 +145,5 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 30 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: crate-ci/typos@v1 diff --git a/.github/workflows/ethereum-tests.yml b/.github/workflows/ethereum-tests.yml index c8d6f99f27..862c4345ec 100644 --- a/.github/workflows/ethereum-tests.yml +++ b/.github/workflows/ethereum-tests.yml @@ -20,7 +20,7 @@ jobs: target: [i686-unknown-linux-gnu, x86_64-unknown-linux-gnu] steps: - name: Checkout sources - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Install toolchain uses: dtolnay/rust-toolchain@stable diff --git a/.github/workflows/release-plz.yml b/.github/workflows/release-plz.yml index 8390d785d2..1de762c943 100644 --- a/.github/workflows/release-plz.yml +++ b/.github/workflows/release-plz.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 0 - name: Install Rust toolchain diff --git a/CHANGELOG.md b/CHANGELOG.md index 94bc483c07..8451e94b3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,182 @@ Because this is workspace with multi libraries, tags will be simplified, and with this document you can match version of project with git tag. +# v102 +date: 14.11.2025 + +Fix for pre berlin selfdestruct oog introduced in v99/v100 tag (revm 32.0.0). + +* `revm-context-interface`: 13.0.0 -> 13.1.0 (✓ API compatible changes) +* `revm-context`: 12.0.0 -> 12.1.0 (✓ API compatible changes) +* `revm-database`: 9.0.5 -> 9.0.6 (✓ API compatible changes) +* `revm-interpreter`: 31.0.0 -> 31.1.0 (✓ API compatible changes) +* `revm-inspector`: 14.0.0 -> 14.1.0 (✓ API compatible changes) +* `revm`: 33.0.0 -> 33.1.0 (✓ API compatible changes) +* `revme`: 10.0.0 -> 10.0.2 (✓ API compatible changes) +* `op-revm`: 14.0.0 -> 14.1.0 (✓ API compatible changes) +* `revm-ee-tests`: 0.1.0 +* `revm-handler`: 14.0.0 -> 14.1.0 +* `revm-statetest-types`: 13.0.0 -> 13.1.0 + +# v101 +date: 13.11.2025 + +Patch for Inspector so full_log will be called inside loop. + +* `revm-inspector`: 14.0.0 -> 14.0.1 (✓ API compatible changes) +* `revm-ee-tests`: 0.1.0 +* `revm`: 33.0.0 -> 33.0.1 +* `revm-statetest-types`: 13.0.0 -> 13.0.1 +* `revme`: 10.0.0 -> 10.0.1 +* `op-revm`: 14.0.0 -> 14.0.1 + +# v100 +date: 12.11.2025 + +Bumping major version for `revm-context-interface` sa it is breaking change. +Host selfdestruct function got changed in v99 + +* `revm-context-interface`: 12.1.0 -> 13.0.0 (⚠ API breaking changes) +* `revm-context`: 11.1.0 -> 12.0.0 (✓ API compatible changes) +* `revm-interpreter`: 30.0.0 -> 31.0.0 (✓ API compatible changes) +* `revm-precompile`: 30.0.0 -> 31.0.0 (✓ API compatible changes) +* `revm-handler`: 13.0.0 -> 14.0.0 (✓ API compatible changes) +* `revm-inspector`: 13.0.0 -> 14.0.0 (✓ API compatible changes) +* `op-revm`: 13.0.0 -> 14.0.0 (✓ API compatible changes) +* `revm-ee-tests`: 0.1.0 +* `revm`: 32.0.0 -> 33.0.0 +* `revm-statetest-types`: 12.0.0 -> 13.0.0 +* `revme`: 9.1.0 -> 10.0.0 + +revm-context-interface@13.0.0 revm-context@12.0.0 revm-interpreter@31.0.0 revm-precompile@31.0.0 revm-handler@14.0.0 revm-inspector@14.0.0 +op-revm@14.0.0 revm@33.0.0 revm-statetest-types@13.0.0 revme@10.0.0 + +# v99 +date 10.11.2025 + +Maintainance release. + +* `revm-context-interface`: 12.0.1 -> 12.1.0 (✓ API compatible changes) +* `revm-context`: 11.0.2 -> 11.1.0 (✓ API compatible changes) +* `revm-interpreter`: 29.0.1 -> 30.0.0 (✓ API compatible changes) +* `revm-precompile`: 29.0.1 -> 30.0.0 (✓ API compatible changes) +* `revm-handler`: 12.0.2 -> 13.0.0 (✓ API compatible changes) +* `revm-inspector`: 12.0.2 -> 13.0.0 (✓ API compatible changes) +* `op-revm`: 12.0.2 -> 13.0.0 (✓ API compatible changes) +* `revm-ee-tests`: 0.1.0 +* `revm`: 31.0.2 -> 32.0.0 +* `revm-statetest-types`: 11.0.2 -> 12.0.0 +* `revme`: 9.0.2 -> 9.1.0 + + + +# v98 +date: 10.11.2025 + +Patch for loading of account on storage fetch, needed for op-reth + +* `revm-database`: 9.0.4 -> 9.0.5 (✓ API compatible changes) +* `op-revm`: 12.0.1 -> 12.0.2 (✓ API compatible changes) +* `revm-ee-tests`: 0.1.0 +* `revm-context`: 11.0.1 -> 11.0.2 +* `revm-handler`: 12.0.1 -> 12.0.2 +* `revm-inspector`: 12.0.1 -> 12.0.2 +* `revm`: 31.0.1 -> 31.0.2 +* `revm-statetest-types`: 11.0.1 -> 11.0.2 +* `revme`: 9.0.1 -> 9.0.2 + +# v97 +date 07.11.2025 + +Patch release for a bug fix. + +* `revm-primitives`: 21.0.1 -> 21.0.2 (✓ API compatible changes) +* `revm-context`: 11.0.0 -> 11.0.1 (✓ API compatible changes) +* `revm-statetest-types`: 11.0.0 -> 11.0.1 (✓ API compatible changes) +* `revme`: 9.0.0 -> 9.0.1 (✓ API compatible changes) +* `revm-ee-tests`: 0.1.0 +* `revm-bytecode`: 7.1.0 -> 7.1.1 +* `revm-state`: 8.1.0 -> 8.1.1 +* `revm-database-interface`: 8.0.4 -> 8.0.5 +* `revm-context-interface`: 12.0.0 -> 12.0.1 +* `revm-database`: 9.0.3 -> 9.0.4 +* `revm-interpreter`: 29.0.0 -> 29.0.1 +* `revm-precompile`: 29.0.0 -> 29.0.1 +* `revm-handler`: 12.0.0 -> 12.0.1 +* `revm-inspector`: 12.0.0 -> 12.0.1 +* `revm`: 31.0.0 -> 31.0.1 +* `op-revm`: 12.0.0 -> 12.0.1 + +# v96 +date 30.10.2025 + +Regular release. + +* `revm-bytecode`: 7.0.2 -> 7.1.0 (✓ API compatible changes) +* `revm-state`: 8.0.2 -> 8.1.0 (✓ API compatible changes) +* `revm-context-interface`: 11.1.2 -> 12.0.0 (⚠ API breaking changes) +* `revm-context`: 10.1.2 -> 11.0.0 (⚠ API breaking changes) +* `revm-interpreter`: 28.0.0 -> 28.0.1 (✓ API compatible changes) +* `revm-precompile`: 28.1.1 -> 29.0.0 (⚠ API breaking changes) +* `revm-handler`: 11.2.0 -> 11.3.0 (✓ API compatible changes) +* `revm-inspector`: 11.2.0 -> 11.2.1 (✓ API compatible changes) +* `revm`: 30.2.0 -> 30.2.1 (✓ API compatible changes) +* `revme`: 8.3.0 -> 8.3.1 (✓ API compatible changes) +* `op-revm`: 11.2.0 -> 12.0.0 (⚠ API breaking changes) +* `revm-ee-tests`: 0.1.0 +* `revm-database-interface`: 8.0.3 -> 8.0.4 +* `revm-database`: 9.0.2 -> 9.0.3 +* `revm-statetest-types`: 10.2.0 -> 10.2.1 + +# v95 +date: 29.10.2025 + +op-revm bump + +* `op-revm`: 11.2.0 -> 11.3.0 + +# v94 +date: 22.10.2025 + +op-revm bump. + +* `op-revm`: 11.1.2 -> 11.2.0 + +# v93 +date: 17.10.2025 + +Small release with one breaking change. Bytecode in CallInput is now optional + +* `revm-interpreter`: 27.0.2 -> 28.0.0 (⚠ API breaking changes) +* `revm-handler`: 11.1.2 -> 11.2.0 (✓ API compatible changes) +* `revm-ee-tests`: 0.1.0 +* `revm-inspector`: 11.1.2 -> 11.1.3 +* `revm`: 30.1.2 -> 30.1.3 +* `revm-statetest-types`: 10.1.2 -> 10.1.3 +* `revme`: 8.2.2 -> 8.2.3 +* `op-revm`: 11.1.2 -> 11.1.3 + +revm-handler revm-inspector revm revm-statetest-types revme op-revm + +# v92 +date 15.10.2025 + +Patch release that would revert JumpTable serde serialization/deserialization to before v90 release + +* `revm-bytecode`: 7.0.1 -> 7.0.2 (✓ API compatible changes) +* `revm-ee-tests`: 0.1.0 +* `revm-state`: 8.0.1 -> 8.0.2 +* `revm-database-interface`: 8.0.2 -> 8.0.3 +* `revm-context-interface`: 11.1.1 -> 11.1.2 +* `revm-context`: 10.1.1 -> 10.1.2 +* `revm-database`: 9.0.1 -> 9.0.2 +* `revm-interpreter`: 27.0.1 -> 27.0.2 +* `revm-handler`: 11.1.1 -> 11.1.2 +* `revm-inspector`: 11.1.1 -> 11.1.2 +* `revm`: 30.1.1 -> 30.1.2 +* `revm-statetest-types`: 10.1.1 -> 10.1.2 +* `revme`: 8.2.1 -> 8.2.2 +* `op-revm`: 11.1.1 -> 11.1.2 + # v91 date: 15.10.2025 diff --git a/Cargo.lock b/Cargo.lock index 98fdaa7102..b5b14c0432 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,21 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "addr2line" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - [[package]] name = "ahash" version = "0.8.12" @@ -31,9 +16,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] @@ -46,9 +31,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy-chains" -version = "0.2.9" +version = "0.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8ff73a143281cb77c32006b04af9c047a6b8fe5860e85a88ad325328965355" +checksum = "25db5bcdd086f0b1b9610140a12c59b757397be90bd130d8d836fc8da0815a34" dependencies = [ "alloy-primitives", "num_enum", @@ -57,9 +42,9 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "1.0.27" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7345077623aaa080fc06735ac13b8fa335125c8550f9c4f64135a5bf6f79967" +checksum = "12870ab65b131f609257436935047eec3cfabee8809732f6bf5a69fe2a18cf2e" dependencies = [ "alloy-eips", "alloy-primitives", @@ -68,6 +53,7 @@ dependencies = [ "alloy-trie", "alloy-tx-macros", "auto_impl", + "borsh", "c-kzg", "derive_more", "either", @@ -76,15 +62,16 @@ dependencies = [ "rand 0.8.5", "secp256k1 0.30.0", "serde", + "serde_json", "serde_with", "thiserror", ] [[package]] name = "alloy-consensus-any" -version = "1.0.27" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501f83565d28bdb9d6457dd3b5d646e19db37709d0f27608a26a1839052ddade" +checksum = "47c66b14d2187de0c4efe4ef678aaa57a6a34cccdbea3a0773627fac9bd128f4" dependencies = [ "alloy-consensus", "alloy-eips", @@ -109,33 +96,47 @@ dependencies = [ [[package]] name = "alloy-eip2930" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b82752a889170df67bbb36d42ca63c531eb16274f0d7299ae2a680facba17bd" +checksum = "9441120fa82df73e8959ae0e4ab8ade03de2aaae61be313fbf5746277847ce25" dependencies = [ "alloy-primitives", "alloy-rlp", + "borsh", "serde", ] [[package]] name = "alloy-eip7702" -version = "0.6.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d4769c6ffddca380b0070d71c8b7f30bed375543fe76bb2f74ec0acf4b7cd16" +checksum = "2919c5a56a1007492da313e7a3b6d45ef5edc5d33416fdec63c0d7a2702a0d20" dependencies = [ "alloy-primitives", "alloy-rlp", + "borsh", "k256", "serde", "thiserror", ] +[[package]] +name = "alloy-eip7928" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6adac476434bf024279164dcdca299309f0c7d1e3557024eb7a83f8d9d01c6b5" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "borsh", + "serde", +] + [[package]] name = "alloy-eips" -version = "1.0.27" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c219a87fb386a75780ddbdbbced242477321887e426b0f946c05815ceabe5e09" +checksum = "f076d25ddfcd2f1cbcc234e072baf97567d1df0e3fccdc1f8af8cc8b18dc6299" dependencies = [ "alloy-eip2124", "alloy-eip2930", @@ -144,6 +145,7 @@ dependencies = [ "alloy-rlp", "alloy-serde", "auto_impl", + "borsh", "c-kzg", "derive_more", "either", @@ -155,9 +157,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "1.3.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "125a1c373261b252e53e04d6e92c37d881833afc1315fceab53fd46045695640" +checksum = "84e3cf01219c966f95a460c95f1d4c30e12f6c18150c21a30b768af2a2a29142" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -167,9 +169,9 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "1.0.27" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "334555c323fa2bb98f1d4c242b62da9de8c715557a2ed680a76cefbcac19fefd" +checksum = "250dbd8496f04eabe997e6e4c5186a0630b8bc3dbe7552e1fd917d491ef811e9" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -182,9 +184,9 @@ dependencies = [ [[package]] name = "alloy-network" -version = "1.0.27" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7ea377c9650203d7a7da9e8dee7f04906b49a9253f554b110edd7972e75ef34" +checksum = "fd45cdac957d1fa1d0c18f54f262350eb72f1adc38dd1f8b15f33f0747c6a60c" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -208,9 +210,9 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "1.0.27" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f9ab9a9e92c49a357edaee2d35deea0a32ac8f313cfa37448f04e7e029c9d9" +checksum = "fba5c43e055effb5bd33dbc74b1ab7fe0f367d8801a25af9e7c716b3ef5e440b" dependencies = [ "alloy-consensus", "alloy-eips", @@ -221,9 +223,9 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "1.3.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc9485c56de23438127a731a6b4c87803d49faf1a7068dcd1d8768aca3a9edb9" +checksum = "f6a0fb18dd5fb43ec5f0f6a20be1ce0287c79825827de5744afaa6c957737c33" dependencies = [ "alloy-rlp", "arbitrary", @@ -232,9 +234,9 @@ dependencies = [ "const-hex", "derive_more", "foldhash", - "getrandom 0.3.3", - "hashbrown 0.15.5", - "indexmap 2.11.0", + "getrandom 0.3.4", + "hashbrown 0.16.1", + "indexmap 2.13.0", "itoa", "k256", "keccak-asm", @@ -242,6 +244,7 @@ dependencies = [ "proptest", "proptest-derive", "rand 0.9.2", + "rapidhash", "ruint", "rustc-hash", "serde", @@ -251,9 +254,9 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "1.0.27" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a85361c88c16116defbd98053e3d267054d6b82729cdbef0236f7881590f924" +checksum = "9e87a90cacc27dffd91fa6440145934a782227d31b9876444c5924d3607084ea" dependencies = [ "alloy-chains", "alloy-consensus", @@ -307,14 +310,14 @@ checksum = "64b728d511962dda67c1bc7ea7c03736ec275ed2cf4c35d9585298ac9ccf3b73" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "alloy-rpc-client" -version = "1.0.27" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743fc964abb0106e454e9e8683fb0809fb32940270ef586a58e913531360b302" +checksum = "57a65bb9060e43e9738bbd7c30d742ed962d609f2123a665bbdab7e6e0f13fd3" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -335,9 +338,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-any" -version = "1.0.27" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97372c51a14a804fb9c17010e3dd6c117f7866620b264e24b64d2259be44bcdf" +checksum = "8faa6f22068857f58579271b15e042f4725ad35cdce2ed4778ba32ffd3102b92" dependencies = [ "alloy-consensus-any", "alloy-rpc-types-eth", @@ -346,9 +349,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "1.0.27" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "672286c19528007df058bafd82c67e23247b4b3ebbc538cbddc705a82d8a930f" +checksum = "1ec734cce11f7fe889950b36b51589397528b26beb6f890834a2131ee9f174d7" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -367,9 +370,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "1.0.27" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aae653f049267ae7e040eab6c9b9a417064ca1a6cb21e3dd59b9f1131ef048f" +checksum = "27f076bfd74fccc63d50546e1765359736357a953de2eb778b7b6191571735e6" dependencies = [ "alloy-primitives", "serde", @@ -378,9 +381,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "1.0.27" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97cedce202f848592b96f7e891503d3adb33739c4e76904da73574290141b93" +checksum = "d80748c209a68421ab6f737828ce6ede7543569a5cad099c1ec16fc1baa05620" dependencies = [ "alloy-primitives", "async-trait", @@ -393,9 +396,9 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "1.0.27" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83ae7d854db5b7cdd5b9ed7ad13d1e5e034cdd8be85ffef081f61dc6c9e18351" +checksum = "17eb1eb39351b4bf20bb0710d8d3a91eb7918d3f3de2f3835f556842e33865cb" dependencies = [ "alloy-consensus", "alloy-network", @@ -409,41 +412,41 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "1.3.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d20d867dcf42019d4779519a1ceb55eba8d7f3d0e4f0a89bcba82b8f9eb01e48" +checksum = "09eb18ce0df92b4277291bbaa0ed70545d78b02948df756bbd3d6214bf39a218" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "alloy-sol-macro-expander" -version = "1.3.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b74e91b0b553c115d14bd0ed41898309356dc85d0e3d4b9014c4e7715e48c8ad" +checksum = "95d9fa2daf21f59aa546d549943f10b5cce1ae59986774019fbedae834ffe01b" dependencies = [ "alloy-sol-macro-input", "const-hex", "heck", - "indexmap 2.11.0", + "indexmap 2.13.0", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", "syn-solidity", "tiny-keccak", ] [[package]] name = "alloy-sol-macro-input" -version = "1.3.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84194d31220803f5f62d0a00f583fd3a062b36382e2bea446f1af96727754565" +checksum = "9396007fe69c26ee118a19f4dee1f5d1d6be186ea75b3881adf16d87f8444686" dependencies = [ "const-hex", "dunce", @@ -451,15 +454,15 @@ dependencies = [ "macro-string", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", "syn-solidity", ] [[package]] name = "alloy-sol-type-parser" -version = "1.3.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe8c27b3cf6b2bb8361904732f955bc7c05e00be5f469cec7e2280b6167f3ff0" +checksum = "af67a0b0dcebe14244fc92002cd8d96ecbf65db4639d479f5fcd5805755a4c27" dependencies = [ "serde", "winnow", @@ -467,9 +470,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "1.3.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5383d34ea00079e6dd89c652bcbdb764db160cef84e6250926961a0b2295d04" +checksum = "09aeea64f09a7483bdcd4193634c7e5cf9fd7775ee767585270cd8ce2d69dc95" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -479,12 +482,11 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "1.0.27" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08b383bc903c927635e39e1dae7df2180877d93352d1abd389883665a598afc" +checksum = "4a0c1a0288cdff6ee2b2c2c98ab42889d221ca8a9ee4120ede59b5449e0dcb20" dependencies = [ "alloy-json-rpc", - "alloy-primitives", "auto_impl", "base64", "derive_more", @@ -503,9 +505,9 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "1.0.27" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e58dee1f7763ef302074b645fc4f25440637c09a60e8de234b62993f06c0ae3" +checksum = "36dfa207caf6b528b9466c714626f5b2dfd5e8d4595a74631d5670672dac102b" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -518,9 +520,9 @@ dependencies = [ [[package]] name = "alloy-trie" -version = "0.9.1" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3412d52bb97c6c6cc27ccc28d4e6e8cf605469101193b50b0bd5813b1f990b5" +checksum = "428aa0f0e0658ff091f8f667c406e034b431cb10abd39de4f507520968acc499" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -534,23 +536,16 @@ dependencies = [ [[package]] name = "alloy-tx-macros" -version = "1.0.27" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d14809f908822dbff0dc472c77ca4aa129ab12e22fd9bff2dd1ef54603e68e3d" +checksum = "bb0d567f4830dea921868c7680004ae0c7f221b05e6477db6c077c7953698f56" dependencies = [ - "alloy-primitives", "darling", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - [[package]] name = "android_system_properties" version = "0.1.5" @@ -568,9 +563,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.20" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", @@ -583,9 +578,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" @@ -598,29 +593,29 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.10" +version = "3.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "anyhow" -version = "1.0.99" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "approx" @@ -770,7 +765,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" dependencies = [ "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -808,7 +803,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -897,7 +892,7 @@ checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -964,7 +959,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -975,7 +970,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -1002,7 +997,7 @@ checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -1011,27 +1006,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" -[[package]] -name = "az" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" - -[[package]] -name = "backtrace" -version = "0.3.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-targets 0.52.6", -] - [[package]] name = "base16ct" version = "0.2.0" @@ -1046,18 +1020,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" - -[[package]] -name = "bincode" -version = "1.3.3" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "bit-set" @@ -1076,15 +1041,15 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bitcoin-io" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" +checksum = "2dee39a0ee5b4095224a0cfc6bf4cc1baf0f9624b96b367e53b66d974e51d953" [[package]] name = "bitcoin_hashes" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" +checksum = "26ec84b80c482df901772e931a9a681e26a1b9ee2302edeff23cb30328745c8b" dependencies = [ "bitcoin-io", "hex-conservative", @@ -1092,11 +1057,11 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.9.3" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -1123,9 +1088,9 @@ dependencies = [ [[package]] name = "blst" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fd49896f12ac9b6dcd7a5998466b9b58263a695a3dd1ecc1aaca2e12a90b080" +checksum = "dcdb4c7013139a150f9fc55d123186dbfaba0d912817466282c73ac49e71fb45" dependencies = [ "cc", "glob", @@ -1133,11 +1098,34 @@ dependencies = [ "zeroize", ] +[[package]] +name = "borsh" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" +dependencies = [ + "borsh-derive", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0686c856aa6aac0c4498f936d7d6a02df690f614c03e4d906d1018062b5c5e2c" +dependencies = [ + "once_cell", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.114", +] + [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" [[package]] name = "byte-slice-cast" @@ -1153,18 +1141,18 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" dependencies = [ "serde", ] [[package]] name = "c-kzg" -version = "2.1.4" +version = "2.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "137a2a2878ed823ef1bd73e5441e245602aae5360022113b8ad259ca4b5b8727" +checksum = "e00bf4b112b07b505472dbefd19e37e53307e2bfed5a79e0cc161d58ccd0e687" dependencies = [ "blst", "cc", @@ -1183,18 +1171,19 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.34" +version = "1.2.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42bc4aea80032b7bf409b0bc7ccad88853858911b7713a8062fdc0623867bedc" +checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3" dependencies = [ + "find-msvc-tools", "shlex", ] [[package]] name = "cfg-if" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" @@ -1204,11 +1193,10 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.41" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" dependencies = [ - "android-tzdata", "iana-time-zone", "num-traits", "serde", @@ -1244,9 +1232,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.46" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c5e4fcf9c21d2e544ca1ee9d8552de13019a42aa7dbf32747fa7aaf1df76e57" +checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" dependencies = [ "clap_builder", "clap_derive", @@ -1254,9 +1242,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.46" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fecb53a0e6fcfb055f686001bc2e2592fa527efaf38dbe81a6a9563562e57d41" +checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" dependencies = [ "anstream", "anstyle", @@ -1266,56 +1254,58 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.45" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "clap_lex" -version = "0.7.5" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" +checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" [[package]] name = "codspeed" -version = "3.0.5" +version = "4.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35584c5fcba8059780748866387fb97c5a203bcfc563fc3d0790af406727a117" +checksum = "5f0d98d97fd75ca4489a1a0997820a6521531085e7c8a98941bd0e1264d567dd" dependencies = [ "anyhow", - "bincode", + "cc", "colored", + "getrandom 0.2.17", "glob", "libc", "nix", "serde", "serde_json", "statrs", - "uuid", ] [[package]] name = "codspeed-criterion-compat" -version = "3.0.5" +version = "4.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78f6c1c6bed5fd84d319e8b0889da051daa361c79b7709c9394dfe1a882bba67" +checksum = "d16fe2db207123f7b3a3b5cfff0c22f99469f7534145f3573f57f4c8a5653c2c" dependencies = [ + "clap", "codspeed", "codspeed-criterion-compat-walltime", "colored", + "regex", ] [[package]] name = "codspeed-criterion-compat-walltime" -version = "3.0.5" +version = "4.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c989289ce6b1cbde72ed560496cb8fbf5aa14d5ef5666f168e7f87751038352e" +checksum = "b035c7f9846b143aeabb3833f5b584023eb97b43ecbff3d997db74c4372df2bd" dependencies = [ "anes", "cast", @@ -1356,28 +1346,27 @@ dependencies = [ [[package]] name = "console" -version = "0.16.0" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e09ced7ebbccb63b4c65413d821f2e00ce54c5ca4514ddc6b3c892fdbcbc69d" +checksum = "03e45a4a8926227e4197636ba97a9fc9b00477e9f4bd711395687c5f0734bec4" dependencies = [ "encode_unicode", "libc", "once_cell", "unicode-width", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "const-hex" -version = "1.15.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dccd746bf9b1038c0507b7cec21eb2b11222db96a2902c96e8c185d6d20fb9c4" +checksum = "3bb320cac8a0750d7f25280aa97b09c26edfe161164238ecbbb31092b079e735" dependencies = [ "cfg-if", "cpufeatures", - "hex", "proptest", - "serde", + "serde_core", ] [[package]] @@ -1388,9 +1377,9 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "const_format" -version = "0.2.34" +version = "0.2.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" +checksum = "7faa7469a93a566e9ccc1c73fe783b4a65c274c5ace346038dca9c39fe0030ad" dependencies = [ "const_format_proc_macros", ] @@ -1406,6 +1395,15 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -1433,9 +1431,9 @@ dependencies = [ [[package]] name = "crc" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" +checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" dependencies = [ "crc-catalog", ] @@ -1511,30 +1509,30 @@ dependencies = [ [[package]] name = "csv" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" +checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" dependencies = [ "csv-core", "itoa", "ryu", - "serde", + "serde_core", ] [[package]] name = "csv-core" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d02f3b0da4c6504f86e9cd789d8dbafab48c2321be74e9987593de5a894d93d" +checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" dependencies = [ "memchr", ] [[package]] name = "darling" -version = "0.20.11" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" dependencies = [ "darling_core", "darling_macro", @@ -1542,27 +1540,28 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.11" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", + "serde", "strsim", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "darling_macro" -version = "0.20.11" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -1591,12 +1590,12 @@ dependencies = [ [[package]] name = "deranged" -version = "0.4.0" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", - "serde", + "serde_core", ] [[package]] @@ -1618,7 +1617,7 @@ checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -1629,27 +1628,29 @@ checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "derive_more" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ + "convert_case", "proc-macro2", "quote", - "syn 2.0.106", + "rustc_version 0.4.1", + "syn 2.0.114", "unicode-xid", ] @@ -1682,7 +1683,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -1721,7 +1722,7 @@ dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -1761,22 +1762,22 @@ checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" [[package]] name = "enum-ordinalize" -version = "4.3.0" +version = "4.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea0dcfa4e54eeb516fe454635a95753ddd39acda650ce703031c6973e315dd5" +checksum = "4a1091a7bb1f8f2c4b28f1fe2cef4980ca2d410a3d727d67ecc3178c9b0800f0" dependencies = [ "enum-ordinalize-derive", ] [[package]] name = "enum-ordinalize-derive" -version = "4.3.1" +version = "4.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" +checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -1787,12 +1788,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -1928,6 +1929,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "find-msvc-tools" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41" + [[package]] name = "fixed-hash" version = "0.8.0" @@ -1948,9 +1955,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" -version = "0.1.5" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" [[package]] name = "foreign-types" @@ -2038,7 +2045,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -2085,9 +2092,9 @@ checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" [[package]] name = "generic-array" -version = "0.14.7" +version = "0.14.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" dependencies = [ "typenum", "version_check", @@ -2096,33 +2103,27 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", "libc", - "wasi 0.11.1+wasi-snapshot-preview1", + "wasi", ] [[package]] name = "getrandom" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "wasip2", ] -[[package]] -name = "gimli" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" - [[package]] name = "glob" version = "0.3.3" @@ -2131,9 +2132,9 @@ checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "gmp-mpfr-sys" -version = "1.6.7" +version = "1.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a636fb6a653382a379ee1e5593dacdc628667994167024c143214cafd40c1a86" +checksum = "60f8970a75c006bb2f8ae79c6768a116dd215fa8346a87aed99bf9d82ca43394" dependencies = [ "libc", "windows-sys 0.60.2", @@ -2152,12 +2153,13 @@ dependencies = [ [[package]] name = "half" -version = "2.6.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" dependencies = [ "cfg-if", "crunchy", + "zerocopy", ] [[package]] @@ -2183,11 +2185,21 @@ name = "hashbrown" version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ "allocator-api2", "equivalent", "foldhash", "serde", + "serde_core", ] [[package]] @@ -2207,15 +2219,12 @@ name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -dependencies = [ - "serde", -] [[package]] name = "hex-conservative" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" +checksum = "fda06d18ac606267c40c04e41b9947729bf8b9efe74bd4e82b61a5f26a510b9f" dependencies = [ "arrayvec", ] @@ -2231,12 +2240,11 @@ dependencies = [ [[package]] name = "http" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" dependencies = [ "bytes", - "fnv", "itoa", ] @@ -2271,9 +2279,9 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "hyper" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" dependencies = [ "atomic-waker", "bytes", @@ -2308,9 +2316,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.16" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" +checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" dependencies = [ "base64", "bytes", @@ -2332,9 +2340,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.63" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -2356,9 +2364,9 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" dependencies = [ "displaydoc", "potential_utf", @@ -2369,9 +2377,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" dependencies = [ "displaydoc", "litemap", @@ -2382,11 +2390,10 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" dependencies = [ - "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", @@ -2397,42 +2404,38 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.0.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" dependencies = [ - "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", "icu_provider", - "potential_utf", "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "2.0.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] name = "icu_provider" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" dependencies = [ "displaydoc", "icu_locale_core", - "stable_deref_trait", - "tinystr", "writeable", "yoke", "zerofrom", @@ -2484,7 +2487,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -2500,21 +2503,22 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.11.0" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "arbitrary", "equivalent", - "hashbrown 0.15.5", + "hashbrown 0.16.1", "serde", + "serde_core", ] [[package]] name = "indicatif" -version = "0.18.0" +version = "0.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a646d946d06bedbbc4cac4c218acf4bbf2d87757a784857025f4d447e4e1cd" +checksum = "9375e112e4b463ec1b1c6c011953545c65a30164fbab5b581df32b3abf0dcb88" dependencies = [ "console", "portable-atomic", @@ -2523,17 +2527,6 @@ dependencies = [ "web-time", ] -[[package]] -name = "io-uring" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" -dependencies = [ - "bitflags", - "cfg-if", - "libc", -] - [[package]] name = "ipnet" version = "2.11.0" @@ -2542,9 +2535,9 @@ checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "iri-string" -version = "0.7.8" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" dependencies = [ "memchr", "serde", @@ -2552,20 +2545,20 @@ dependencies = [ [[package]] name = "is-terminal" -version = "0.4.16" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" +checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "is_terminal_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itertools" @@ -2596,15 +2589,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" dependencies = [ "once_cell", "wasm-bindgen", @@ -2654,9 +2647,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.175" +version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" [[package]] name = "libm" @@ -2666,39 +2659,38 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "linux-raw-sys" -version = "0.9.4" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] name = "lock_api" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.27" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "lru" -version = "0.13.0" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "227748d55f2f0ab4735d87fd623798cb6b664512fe979705f829c9f81c934465" +checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593" dependencies = [ - "hashbrown 0.15.5", + "hashbrown 0.16.1", ] [[package]] @@ -2709,33 +2701,24 @@ checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "memchr" -version = "2.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" - -[[package]] -name = "miniz_oxide" -version = "0.8.9" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", -] +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "mio" -version = "1.0.4" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", - "wasi 0.11.1+wasi-snapshot-preview1", - "windows-sys 0.59.0", + "wasi", + "windows-sys 0.61.2", ] [[package]] @@ -2757,9 +2740,9 @@ dependencies = [ [[package]] name = "nix" -version = "0.29.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ "bitflags", "cfg-if", @@ -2859,9 +2842,9 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" +checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" dependencies = [ "num_enum_derive", "rustversion", @@ -2869,21 +2852,21 @@ dependencies = [ [[package]] name = "num_enum_derive" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" +checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "nybbles" -version = "0.4.3" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63cb50036b1ad148038105af40aaa70ff24d8a14fbc44ae5c914e1348533d12e" +checksum = "7b5676b5c379cf5b03da1df2b3061c4a4e2aa691086a56ac923e08c143f53f59" dependencies = [ "alloy-rlp", "cfg-if", @@ -2893,15 +2876,6 @@ dependencies = [ "smallvec", ] -[[package]] -name = "object" -version = "0.36.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" -dependencies = [ - "memchr", -] - [[package]] name = "once_cell" version = "1.21.3" @@ -2910,9 +2884,9 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "once_cell_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "oorandom" @@ -2922,7 +2896,7 @@ checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" [[package]] name = "op-revm" -version = "11.1.1" +version = "15.0.0" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -2936,9 +2910,9 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.73" +version = "0.10.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" dependencies = [ "bitflags", "cfg-if", @@ -2957,7 +2931,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -2968,9 +2942,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.109" +version = "0.9.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" dependencies = [ "cc", "libc", @@ -3015,14 +2989,14 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "parking_lot" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", "parking_lot_core", @@ -3030,15 +3004,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.11" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -3055,12 +3029,11 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.1" +version = "2.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" +checksum = "2c9eb05c21a464ea704b53158d358a31e6425db2f63a1a7312268b05fe2b75f7" dependencies = [ "memchr", - "thiserror", "ucd-trie", ] @@ -3095,7 +3068,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -3124,7 +3097,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -3194,15 +3167,15 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.11.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" +checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" [[package]] name = "potential_utf" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" dependencies = [ "zerovec", ] @@ -3244,9 +3217,9 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" dependencies = [ "toml_edit", ] @@ -3270,28 +3243,27 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "proc-macro2" -version = "1.0.101" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" dependencies = [ "unicode-ident", ] [[package]] name = "proptest" -version = "1.7.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fcdab19deb5195a31cf7726a210015ff1496ba1464fd42cb4f537b8b01b471f" +checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40" dependencies = [ "bit-set", "bit-vec", "bitflags", - "lazy_static", "num-traits", "rand 0.9.2", "rand_chacha 0.9.0", @@ -3304,13 +3276,13 @@ dependencies = [ [[package]] name = "proptest-derive" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee1c9ac207483d5e7db4940700de86a9aae46ef90c48b57f99fe7edb8345e49" +checksum = "095a99f75c69734802359b682be8daaf8980296731f6470434ea2c652af1dd30" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -3321,9 +3293,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.40" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" dependencies = [ "proc-macro2", ] @@ -3359,7 +3331,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", - "rand_core 0.9.3", + "rand_core 0.9.5", "serde", ] @@ -3380,7 +3352,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -3389,16 +3361,16 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", ] [[package]] name = "rand_core" -version = "0.9.3" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", "serde", ] @@ -3408,7 +3380,17 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" dependencies = [ - "rand_core 0.9.3", + "rand_core 0.9.5", +] + +[[package]] +name = "rapidhash" +version = "4.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d8b5b858a440a0bc02625b62dd95131b9201aa9f69f411195dd4a7cfb1de3d7" +dependencies = [ + "rand 0.9.2", + "rustversion", ] [[package]] @@ -3433,38 +3415,38 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.17" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ "bitflags", ] [[package]] name = "ref-cast" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "regex" -version = "1.11.2" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", @@ -3474,9 +3456,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.10" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", @@ -3485,9 +3467,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "relative-path" @@ -3497,9 +3479,9 @@ checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" [[package]] name = "reqwest" -version = "0.12.23" +version = "0.12.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" dependencies = [ "base64", "bytes", @@ -3533,7 +3515,7 @@ dependencies = [ [[package]] name = "revm" -version = "30.1.1" +version = "34.0.0" dependencies = [ "revm-bytecode", "revm-context", @@ -3546,13 +3528,14 @@ dependencies = [ "revm-precompile", "revm-primitives", "revm-state", + "revm-statetest-types", "serde", "serde_json", ] [[package]] name = "revm-bytecode" -version = "7.0.1" +version = "8.0.0" dependencies = [ "bitvec", "paste", @@ -3564,7 +3547,7 @@ dependencies = [ [[package]] name = "revm-context" -version = "10.1.1" +version = "13.0.0" dependencies = [ "bitvec", "cfg-if", @@ -3580,7 +3563,7 @@ dependencies = [ [[package]] name = "revm-context-interface" -version = "11.1.1" +version = "14.0.0" dependencies = [ "alloy-eip2930", "alloy-eip7702", @@ -3594,7 +3577,7 @@ dependencies = [ [[package]] name = "revm-database" -version = "9.0.1" +version = "10.0.0" dependencies = [ "alloy-eips", "alloy-provider", @@ -3610,13 +3593,14 @@ dependencies = [ [[package]] name = "revm-database-interface" -version = "8.0.2" +version = "9.0.0" dependencies = [ "auto_impl", "either", "revm-primitives", "revm-state", "serde", + "thiserror", "tokio", ] @@ -3636,9 +3620,8 @@ dependencies = [ [[package]] name = "revm-handler" -version = "11.1.1" +version = "15.0.0" dependencies = [ - "alloy-eip7702", "alloy-provider", "alloy-signer", "alloy-signer-local", @@ -3658,7 +3641,7 @@ dependencies = [ [[package]] name = "revm-inspector" -version = "11.1.1" +version = "15.0.0" dependencies = [ "auto_impl", "either", @@ -3675,7 +3658,7 @@ dependencies = [ [[package]] name = "revm-interpreter" -version = "27.0.1" +version = "32.0.0" dependencies = [ "revm-bytecode", "revm-context-interface", @@ -3687,7 +3670,7 @@ dependencies = [ [[package]] name = "revm-precompile" -version = "28.1.1" +version = "32.0.0" dependencies = [ "ark-bls12-381", "ark-bn254", @@ -3701,13 +3684,13 @@ dependencies = [ "c-kzg", "cfg-if", "codspeed-criterion-compat", + "gmp-mpfr-sys", "k256", "p256", "rand 0.9.2", "revm-primitives", "ripemd", "rstest", - "rug", "secp256k1 0.31.1", "sha2", "substrate-bn", @@ -3715,7 +3698,7 @@ dependencies = [ [[package]] name = "revm-primitives" -version = "21.0.1" +version = "22.0.0" dependencies = [ "alloy-primitives", "num_enum", @@ -3725,21 +3708,28 @@ dependencies = [ [[package]] name = "revm-state" -version = "8.0.1" +version = "9.0.0" dependencies = [ + "alloy-eip7928", "bitflags", "revm-bytecode", "revm-primitives", "serde", + "serde_json", ] [[package]] name = "revm-statetest-types" -version = "10.1.1" +version = "14.0.0" dependencies = [ - "alloy-eips", + "alloy-eip7928", "k256", - "revm", + "revm-bytecode", + "revm-context", + "revm-context-interface", + "revm-database", + "revm-primitives", + "revm-state", "serde", "serde_json", "thiserror", @@ -3747,7 +3737,7 @@ dependencies = [ [[package]] name = "revme" -version = "8.2.1" +version = "11.0.0" dependencies = [ "alloy-rlp", "alloy-sol-types", @@ -3759,15 +3749,6 @@ dependencies = [ "k256", "plain_hasher", "revm", - "revm-bytecode", - "revm-context", - "revm-context-interface", - "revm-database", - "revm-database-interface", - "revm-inspector", - "revm-primitives", - "revm-state", - "revm-statetest-types", "serde", "serde_json", "thiserror", @@ -3829,32 +3810,21 @@ dependencies = [ "regex", "relative-path", "rustc_version 0.4.1", - "syn 2.0.106", + "syn 2.0.114", "unicode-ident", ] -[[package]] -name = "rug" -version = "1.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58ad2e973fe3c3214251a840a621812a4f40468da814b1a3d6947d433c2af11f" -dependencies = [ - "az", - "gmp-mpfr-sys", - "libc", - "libm", -] - [[package]] name = "ruint" -version = "1.16.0" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ecb38f82477f20c5c3d62ef52d7c4e536e38ea9b73fb570a20c5cae0e14bcf6" +checksum = "c141e807189ad38a07276942c6623032d3753c8859c146104ac2e4d68865945a" dependencies = [ "alloy-rlp", "arbitrary", "ark-ff 0.3.0", "ark-ff 0.4.2", + "ark-ff 0.5.0", "bytes", "fastrlp 0.3.1", "fastrlp 0.4.0", @@ -3868,7 +3838,7 @@ dependencies = [ "rand 0.9.2", "rlp", "ruint-macro", - "serde", + "serde_core", "valuable", "zeroize", ] @@ -3879,12 +3849,6 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" -[[package]] -name = "rustc-demangle" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" - [[package]] name = "rustc-hash" version = "2.1.1" @@ -3915,27 +3879,27 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver 1.0.26", + "semver 1.0.27", ] [[package]] name = "rustix" -version = "1.0.8" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "rustls-pki-types" -version = "1.12.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" dependencies = [ "zeroize", ] @@ -3948,9 +3912,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "rusty-fork" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" dependencies = [ "fnv", "quick-error", @@ -3960,9 +3924,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.20" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" [[package]] name = "same-file" @@ -3975,11 +3939,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3996,9 +3960,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.0.4" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" +checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2" dependencies = [ "dyn-clone", "ref-cast", @@ -4083,9 +4047,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.14.0" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" dependencies = [ "core-foundation-sys", "libc", @@ -4102,9 +4066,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" [[package]] name = "semver-parser" @@ -4117,35 +4081,46 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "serde_json" -version = "1.0.143" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ - "indexmap 2.11.0", + "indexmap 2.13.0", "itoa", "memchr", - "ryu", "serde", + "serde_core", + "zmij", ] [[package]] @@ -4162,19 +4137,18 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.14.0" +version = "3.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2c45cd61fefa9db6f254525d46e392b852e0e61d9a1fd36e5bd183450a556d5" +checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" dependencies = [ "base64", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.11.0", + "indexmap 2.13.0", "schemars 0.9.0", - "schemars 1.0.4", - "serde", - "serde_derive", + "schemars 1.2.0", + "serde_core", "serde_json", "serde_with_macros", "time", @@ -4182,14 +4156,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.14.0" +version = "3.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de90945e6565ce0d9a25098082ed4ee4002e047cb59892c318d66821e14bb30f" +checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -4282,12 +4256,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -4308,9 +4282,9 @@ dependencies = [ [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "static_assertions" @@ -4352,7 +4326,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -4387,9 +4361,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.106" +version = "2.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" dependencies = [ "proc-macro2", "quote", @@ -4398,14 +4372,14 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "1.3.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0b198d366dbec045acfcd97295eb653a7a2b40e4dc764ef1e79aafcad439d3c" +checksum = "5f92d01b5de07eaf324f7fca61cc6bd3d82bbc1de5b6c963e6fe79e86f36580d" dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -4425,7 +4399,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -4436,35 +4410,35 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.21.0" +version = "3.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e" +checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" dependencies = [ "fastrand", - "getrandom 0.3.3", + "getrandom 0.3.4", "once_cell", "rustix", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "thiserror" -version = "2.0.16" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.16" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -4478,30 +4452,30 @@ dependencies = [ [[package]] name = "time" -version = "0.3.41" +version = "0.3.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "f9e442fc33d7fdb45aa9bfeb312c095964abdf596f7567261062b2a7107aaabd" dependencies = [ "deranged", "itoa", "num-conv", "powerfmt", - "serde", + "serde_core", "time-core", "time-macros", ] [[package]] name = "time-core" -version = "0.1.4" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +checksum = "8b36ee98fd31ec7426d599183e8fe26932a8dc1fb76ddb6214d05493377d34ca" [[package]] name = "time-macros" -version = "0.2.22" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +checksum = "71e552d1249bf61ac2a52db88179fd0673def1e1ad8243a00d9ec9ed71fee3dd" dependencies = [ "num-conv", "time-core", @@ -4518,9 +4492,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ "displaydoc", "zerovec", @@ -4538,31 +4512,28 @@ dependencies = [ [[package]] name = "tokio" -version = "1.47.1" +version = "1.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" dependencies = [ - "backtrace", "bytes", - "io-uring", "libc", "mio", "pin-project-lite", - "slab", "socket2", "tokio-macros", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -4577,9 +4548,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" dependencies = [ "futures-core", "pin-project-lite", @@ -4589,9 +4560,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.16" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" dependencies = [ "bytes", "futures-core", @@ -4602,26 +4573,39 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.11" +version = "0.7.5+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] [[package]] name = "toml_edit" -version = "0.22.27" +version = "0.23.10+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" dependencies = [ - "indexmap 2.11.0", + "indexmap 2.13.0", "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.0.6+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" +dependencies = [ "winnow", ] [[package]] name = "tower" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", @@ -4634,9 +4618,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ "bitflags", "bytes", @@ -4664,9 +4648,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -4675,20 +4659,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", "valuable", @@ -4721,9 +4705,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "ucd-trie" @@ -4751,15 +4735,21 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-ident" -version = "1.0.18" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-width" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "unicode-xid" @@ -4769,15 +4759,15 @@ checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "unit-prefix" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "323402cff2dd658f39ca17c789b502021b3f18707c91cdf22e3838e1b4023817" +checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3" [[package]] name = "url" -version = "2.5.7" +version = "2.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" dependencies = [ "form_urlencoded", "idna", @@ -4797,17 +4787,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" -[[package]] -name = "uuid" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f33196643e165781c20a5ead5582283a7dacbb87855d867fbc2df3f81eddc1be" -dependencies = [ - "getrandom 0.3.3", - "js-sys", - "wasm-bindgen", -] - [[package]] name = "valuable" version = "0.1.1" @@ -4861,45 +4840,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] -name = "wasi" -version = "0.14.2+wasi-0.2.4" +name = "wasip2" +version = "1.0.1+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" dependencies = [ - "wit-bindgen-rt", + "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn 2.0.106", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.50" +version = "0.4.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" dependencies = [ "cfg-if", "js-sys", @@ -4910,9 +4876,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4920,31 +4886,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" dependencies = [ + "bumpalo", "proc-macro2", "quote", - "syn 2.0.106", - "wasm-bindgen-backend", + "syn 2.0.114", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" dependencies = [ "unicode-ident", ] [[package]] name = "wasmtimer" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8d49b5d6c64e8558d9b1b065014426f35c18de636895d24893dbbd329743446" +checksum = "1c598d6b99ea013e35844697fc4670d08339d5cda15588f193c6beedd12f644b" dependencies = [ "futures", "js-sys", @@ -4956,9 +4922,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.77" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" dependencies = [ "js-sys", "wasm-bindgen", @@ -4976,18 +4942,18 @@ dependencies = [ [[package]] name = "winapi-util" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "windows-core" -version = "0.61.2" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ "windows-implement", "windows-interface", @@ -4998,46 +4964,46 @@ dependencies = [ [[package]] name = "windows-implement" -version = "0.60.0" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "windows-interface" -version = "0.59.1" +version = "0.59.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "windows-link" -version = "0.1.3" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-result" -version = "0.3.4" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ "windows-link", ] [[package]] name = "windows-strings" -version = "0.4.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ "windows-link", ] @@ -5057,7 +5023,16 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.3", + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", ] [[package]] @@ -5078,19 +5053,19 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.3" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ "windows-link", - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -5101,9 +5076,9 @@ checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" [[package]] name = "windows_aarch64_msvc" @@ -5113,9 +5088,9 @@ checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_aarch64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" [[package]] name = "windows_i686_gnu" @@ -5125,9 +5100,9 @@ checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" [[package]] name = "windows_i686_gnullvm" @@ -5137,9 +5112,9 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" [[package]] name = "windows_i686_msvc" @@ -5149,9 +5124,9 @@ checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_i686_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" [[package]] name = "windows_x86_64_gnu" @@ -5161,9 +5136,9 @@ checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" [[package]] name = "windows_x86_64_gnullvm" @@ -5173,9 +5148,9 @@ checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" [[package]] name = "windows_x86_64_msvc" @@ -5185,33 +5160,30 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" -version = "0.7.13" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" dependencies = [ "memchr", ] [[package]] -name = "wit-bindgen-rt" -version = "0.39.0" +name = "wit-bindgen" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags", -] +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "writeable" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] name = "wyz" @@ -5224,11 +5196,10 @@ dependencies = [ [[package]] name = "yoke" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" dependencies = [ - "serde", "stable_deref_trait", "yoke-derive", "zerofrom", @@ -5236,34 +5207,34 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.26" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.26" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -5283,35 +5254,35 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", "synstructure", ] [[package]] name = "zeroize" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.4.2" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] name = "zerotrie" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" dependencies = [ "displaydoc", "yoke", @@ -5320,9 +5291,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.4" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ "yoke", "zerofrom", @@ -5331,11 +5302,17 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.114", ] + +[[package]] +name = "zmij" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd8f3f50b848df28f887acb68e41201b5aea6bc8a8dacc00fb40635ff9a72fea" diff --git a/Cargo.toml b/Cargo.toml index c04cfa1a3e..97327f2975 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,36 +41,37 @@ default-members = ["crates/revm"] [workspace.dependencies] # revm -revm = { path = "crates/revm", version = "30.1.1", default-features = false } -primitives = { path = "crates/primitives", package = "revm-primitives", version = "21.0.1", default-features = false } -bytecode = { path = "crates/bytecode", package = "revm-bytecode", version = "7.0.1", default-features = false } -database = { path = "crates/database", package = "revm-database", version = "9.0.1", default-features = false } -database-interface = { path = "crates/database/interface", package = "revm-database-interface", version = "8.0.2", default-features = false } -state = { path = "crates/state", package = "revm-state", version = "8.0.1", default-features = false } -interpreter = { path = "crates/interpreter", package = "revm-interpreter", version = "27.0.1", default-features = false } -inspector = { path = "crates/inspector", package = "revm-inspector", version = "11.1.1", default-features = false } -precompile = { path = "crates/precompile", package = "revm-precompile", version = "28.1.1", default-features = false } -statetest-types = { path = "crates/statetest-types", package = "revm-statetest-types", version = "10.1.1", default-features = false } -context = { path = "crates/context", package = "revm-context", version = "10.1.1", default-features = false } -context-interface = { path = "crates/context/interface", package = "revm-context-interface", version = "11.1.1", default-features = false } -handler = { path = "crates/handler", package = "revm-handler", version = "11.1.1", default-features = false } -op-revm = { path = "crates/op-revm", package = "op-revm", version = "11.1.1", default-features = false } +revm = { path = "crates/revm", version = "34.0.0", default-features = false } +primitives = { path = "crates/primitives", package = "revm-primitives", version = "22.0.0", default-features = false } +bytecode = { path = "crates/bytecode", package = "revm-bytecode", version = "8.0.0", default-features = false } +database = { path = "crates/database", package = "revm-database", version = "10.0.0", default-features = false } +database-interface = { path = "crates/database/interface", package = "revm-database-interface", version = "9.0.0", default-features = false } +state = { path = "crates/state", package = "revm-state", version = "9.0.0", default-features = false } +interpreter = { path = "crates/interpreter", package = "revm-interpreter", version = "32.0.0", default-features = false } +inspector = { path = "crates/inspector", package = "revm-inspector", version = "15.0.0", default-features = false } +precompile = { path = "crates/precompile", package = "revm-precompile", version = "32.0.0", default-features = false } +statetest-types = { path = "crates/statetest-types", package = "revm-statetest-types", version = "14.0.0", default-features = false } +context = { path = "crates/context", package = "revm-context", version = "13.0.0", default-features = false } +context-interface = { path = "crates/context/interface", package = "revm-context-interface", version = "14.0.0", default-features = false } +handler = { path = "crates/handler", package = "revm-handler", version = "15.0.0", default-features = false } +op-revm = { path = "crates/op-revm", package = "op-revm", version = "15.0.0", default-features = false } ee-tests = { path = "crates/ee-tests", package = "revm-ee-tests", version = "0.1.0", default-features = false } # alloy -alloy-eip2930 = { version = "0.2.1", default-features = false } -alloy-eip7702 = { version = "0.6.1", default-features = false } -alloy-primitives = { version = "1.3.1", default-features = false } +alloy-eip2930 = { version = "0.2.3", default-features = false } +alloy-eip7702 = { version = "0.6.3", default-features = false } +alloy-eip7928 = { version = "0.3.0", default-features = false } +alloy-primitives = { version = "1.5.2", default-features = false } # alloy in examples, revme or feature flagged. alloy-rlp = { version = "0.3.12", default-features = false } -alloy-sol-types = { version = "1.3.1", default-features = false } -alloy-consensus = { version = "1.0.27", default-features = false } -alloy-eips = { version = "1.0.27", default-features = false } -alloy-provider = { version = "1.0.27", default-features = false } -alloy-signer = { version = "1.0.27", default-features = false } -alloy-signer-local = { version = "1.0.27", default-features = false } -alloy-transport = { version = "1.0.12", default-features = false } +alloy-sol-types = { version = "1.5.2", default-features = false } +alloy-consensus = { version = "1.4.2", default-features = false } +alloy-eips = { version = "1.4.2", default-features = false } +alloy-provider = { version = "1.4.2", default-features = false } +alloy-signer = { version = "1.4.2", default-features = false } +alloy-signer-local = { version = "1.4.2", default-features = false } +alloy-transport = { version = "1.4.2", default-features = false } # precompiles ark-bls12-381 = { version = "0.5", default-features = false } @@ -80,7 +81,7 @@ ark-ff = { version = "0.5", default-features = false } ark-serialize = { version = "0.5", default-features = false } ark-std = { version = "0.5", default-features = false } aurora-engine-modexp = { version = "1.2", default-features = false } -rug = { version = "1.28.0", default-features = false } +gmp-mpfr-sys = { version = "1.6", default-features = false } blst = "0.3.15" bn = { package = "substrate-bn", version = "0.6", default-features = false } c-kzg = { version = "2.1.4", default-features = false } @@ -96,8 +97,8 @@ paste = "1.0" phf = { version = "0.13", default-features = false } # revme -clap = { version = "4", features = ["derive"] } -criterion = { package = "codspeed-criterion-compat", version = "3.0" } +clap = { version = "4.5", features = ["derive"] } +criterion = { package = "codspeed-criterion-compat", version = "4.2" } # serde serde = { version = "1.0", default-features = false, features = [ @@ -108,22 +109,22 @@ serde_json = { version = "1.0", default-features = false } # misc auto_impl = "1.3.0" -bitflags = { version = "2.9.3", default-features = false } +bitflags = { version = "2.10", default-features = false } cfg-if = { version = "1.0", default-features = false } -derive-where = { version = "1.6.0", default-features = false } +derive-where = { version = "1.6", default-features = false } rand = "0.9" tokio = "1.47" either = { version = "1.15.0", default-features = false } # dev-dependencies -anyhow = "1.0.99" +anyhow = "1.0" eyre = "0.6.12" hash-db = "0.15" indicatif = "0.18" plain_hasher = "0.2" rstest = "0.26.0" serde_derive = "1.0" -thiserror = "2.0" +thiserror = { version = "2.0", default-features = false } triehash = "0.8" walkdir = "2.5" diff --git a/Cross.toml b/Cross.toml index d25de22494..5d1593b9e2 100644 --- a/Cross.toml +++ b/Cross.toml @@ -4,7 +4,7 @@ pre-build = [ ] [target.i686-unknown-linux-gnu] -image = "ghcr.io/cross-rs/i686-unknown-linux-gnu:main" +image = "ghcr.io/cross-rs/i686-unknown-linux-gnu:latest" [target.x86_64-unknown-linux-gnu] -image = "ghcr.io/cross-rs/x86_64-unknown-linux-gnu:main" +image = "ghcr.io/cross-rs/x86_64-unknown-linux-gnu:latest" diff --git a/LICENSE b/LICENSE index be6d350ebe..7edb0405ff 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021-2025 draganrakita +Copyright (c) 2021-2026 draganrakita Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index f8ed1d9698..7acf8d44f6 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -1,4 +1,98 @@ +# v103 tag (revm v34.0.0) + +* BAL (EIP-7928) support added to Database implementations. +* `GasParams` is new struct where you can set dynamic opcode gas params. Initialized and can be found in cfg. + * Gas calculation functions moved from `revm-interpreter` to be part of gas params. + * Gas constants moved from `revm_interpreter`::gas to `revm_context_interface::cfg::gas` +* `CreateInputs` struct fields made private with accessor pattern. + * Use `CreateInputs::created_address()` getter (now cached). +* `Host::selfdestruct` signature changed to support OOG on cold load for target account. +* Inspector `log` function renamed: + * `Inspector::log` renamed to `log` and `log_full`. + * `log_full` default impl calls `log`. + * `log_full` has `Interpreter` input while `log` does not. + * `log` is called in places where Interpreter is not found. +* `PrecompileError::Other` now contains `Cow<'static, str>` instead of `&'static str`. + * Allows setting both `&'static str` (no perf penalty) and `String` if needed. +* `JournaledAccount` struct added for tracking account changes. + * `JournalTr` functions that fetch account now return a ref. + * New function `load_account_mut` returns `JournaledAccount`. +* `JournalTr::load_account_code` deprecated, renamed to `JournalTr::load_account_with_code`. +* `JournalTr::warm_account_and_storage` and `JournalTr::warm_account` removed. + * Access list is now separate from the main Journal EvmState. + * Use `JournalTr::warm_access_list` to import access list. +* Declarative macros `tri!`, `gas_or_fail!`, `otry!` removed from `revm-interpreter`. +* `MemoryGas` API signature changes. +* Removed deprecated methods including `into_plain_state`, `regenerate_hash`. +* `State.bal_state` field added (breaks struct literal constructors). +* `DatabaseCommitExt::drain_balances` and `increment_balances` added. +* First precompile error now bubbles up with detailed error messages. + * New `PrecompileError` variants added. + +# 102 tag ( revm v33.1.0) + +No breaking changes + +# 101 tag ( revm v33.1.0) + +No breaking changes + +# v100 tag (revm v33.0.0) + +* Additionally to v99 version: + * `Host::selfdestruct` function got changed to support oog on cold load for target account. + +# v99 tag ( revm v32.0.0 ) + +(yanked version) + +* Added support for transmitting Logs set from precompiles. + * `Inspector::log` function got renamed to `log` and `log_full` + * `log_full` default impl will call `log` + * difference is that `log_full` has `Interpreter` input while `log` does not + and `log` will be called in places where Interpreter is not found. +* `PrecompileError` now contains `Other` as `Cow<'static, str>` + * It allows setting both `&'static str` that is without perf penalty and `String` if needed. + +# v98 tag + +No breaking changes. + +# v97 tag + +No breaking changes. + +# v96 tag + +No breaking changes. + +# v95 tag ( revm v31.0.0) + +* Cfg added `memory_limit()` function +* `JournaledAccount` have been added that wraps account changes, touching and creating journal entry. + * Past function that fetches account now return a ref and new function `load_account_mut` now return `JournaledAccount` + * `JournalEntry` type is added to `JournalTr` so JournaledAccount can create it. +* `JournalTr::load_account_code` is deprecated/renamed to `JournalTr::load_account_with_code` +* `JournalTr::warm_account_and_storage` and `JournalTr::warm_account` are removed as access list is now separate from the main + Journal EvmState. Function that imports access list to the Journal is `JournalTr::warm_access_list` + +# v94 tag ( op-revm ) + +No breaking changes + +# v93 tag ( op-revm ) + +No breaking changes + +# v93 tag ( revm v30.1.0) + +No breaking changes + +# v92 tag ( revm v30.1.2) + +No breaking changes + # v91 tag ( revm v30.1.1) No breaking changes @@ -95,4 +189,4 @@ No breaking changes # v67 tag (revm v21.0.0) -> v68 tag ( revm v22.0.0) * No code breaking changes -* alloy-primitives bumped to v1.0.0 and we had a major bump because of it. \ No newline at end of file +* alloy-primitives bumped to v1.0.0 and we had a major bump because of it. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000..045d46d844 --- /dev/null +++ b/Makefile @@ -0,0 +1,42 @@ +.PHONY: build test fmt fmt-fix clippy lint docs docs-nightly check-no-std eth-tests ci help + +.DEFAULT_GOAL := help + +build: ## Compile the workspace + cargo build --workspace + +test: ## Run tests (default features) + cargo test --workspace + +fmt: ## Check formatting + cargo fmt --all --check + +fmt-fix: ## Apply formatting + cargo fmt --all + +clippy: ## Run Clippy with all features, deny warnings + RUSTFLAGS="-Dwarnings" cargo clippy --workspace --all-targets --all-features + +lint: fmt clippy ## Check formatting + Clippy + +docs: ## Build docs (stable, ci.yml docs job) + RUSTDOCFLAGS="--cfg docsrs -D warnings" cargo doc --workspace --all-features --no-deps --document-private-items + +docs-nightly: ## Build docs with index page (nightly, book.yml build job) + RUSTDOCFLAGS="--enable-index-page -Zunstable-options" cargo +nightly doc --all --no-deps + +check-no-std: ## Check no_std on riscv32/riscv64 (requires targets via rustup) + cargo check --target riscv32imac-unknown-none-elf --no-default-features + cargo check --target riscv32imac-unknown-none-elf -p op-revm --no-default-features + cargo check --target riscv32imac-unknown-none-elf -p revm-database --no-default-features + cargo check --target riscv64imac-unknown-none-elf --no-default-features + cargo check --target riscv64imac-unknown-none-elf -p op-revm --no-default-features + cargo check --target riscv64imac-unknown-none-elf -p revm-database --no-default-features + +eth-tests: ## Run Ethereum state/blockchain tests (downloads fixtures if needed) + ./scripts/run-tests.sh + +ci: lint docs test check-no-std ## Run the full local CI suite + +help: ## Show available targets + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' diff --git a/README.md b/README.md index 82bbb64d5c..237cd5aef8 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Revm offers two primary applications: firstly, it functions as an executor where ### How to use: -Here is a straightforward example of using the Execution API: It allows us to create an Ethereum Virtual Machine (EVM) and execute transactions. Additionally, it can be utilized to generate traces with the inspector or more complex example of foundry cheatcodes. +Here is a straightforward example of using the Execution API: It allows us to create an Ethereum Virtual Machine (EVM) and execute transactions. Additionally, it can be utilized to generate traces with the inspector or more complex examples of foundry cheatcodes. ```rust,ignore let mut evm = Context::mainnet().with_block(block).build_mainnet(); @@ -49,7 +49,7 @@ The full list of projects that use Revm is available in the [awesome-revm](https The [book](https://bluealloy.github.io/revm/) and [`Architecture and API`](https://bluealloy.github.io/revm/architecture.html) page is the best starting resource. -Some quick links can be found here. Some point to code documentation or the book. code docs are there to explain usage of a particular part of the code where the book is to get more of an overview of the architecture or how components/projects fit together. +Some quick links can be found here. Some point to code documentation or the book. Code docs are there to explain usage of a particular part of the code where the book is to get more of an overview of the architecture or how components/projects fit together. * [How to build and use revm](https://bluealloy.github.io/revm/dev.html) * [Architecture overview](https://bluealloy.github.io/revm/architecture.html) @@ -69,8 +69,8 @@ The Minimum Supported Rust Version (MSRV) may be updated at any time, so we can ### Community: For questions please open a github issue or join the public [telegram group](https://t.me/+Ig4WDWOzikA3MzA0) -### Licence -Revm is licensed under MIT Licence. +### License +Revm is licensed under MIT License. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in these crates by you, shall be licensed as above, without any additional terms or conditions. diff --git a/bins/revme/CHANGELOG.md b/bins/revme/CHANGELOG.md index 89ad00610f..92ae68ad14 100644 --- a/bins/revme/CHANGELOG.md +++ b/bins/revme/CHANGELOG.md @@ -7,6 +7,80 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [11.0.0](https://github.com/bluealloy/revm/compare/revme-v10.0.2...revme-v11.0.0) - 2026-01-15 + +### Added + +- move GasParams to Cfg ([#3229](https://github.com/bluealloy/revm/pull/3229)) +- Propagate `map-foldhash` Feature Through Dependency Chain ([#3252](https://github.com/bluealloy/revm/pull/3252)) +- BAL EIP-7928 ([#3070](https://github.com/bluealloy/revm/pull/3070)) +- DatabaseCommitExt + increment_balances ([#3195](https://github.com/bluealloy/revm/pull/3195)) +- sort accounts by address in blockchaintest output ([#3182](https://github.com/bluealloy/revm/pull/3182)) + +### Fixed + +- incorrect bytecode value in blockchain test error output ([#3288](https://github.com/bluealloy/revm/pull/3288)) +- use expected_exception instead of error field for unexpected_success status ([#3244](https://github.com/bluealloy/revm/pull/3244)) +- deduplicate post-state validation error handling ([#3228](https://github.com/bluealloy/revm/pull/3228)) +- *(revme)* incorrect debug log message in btest ([#3233](https://github.com/bluealloy/revm/pull/3233)) +- *(statetest)* use spec-aware blob base fee update fraction ([#3210](https://github.com/bluealloy/revm/pull/3210)) + +### Other + +- apply improvements from ai-bot labeled PRs ([#3297](https://github.com/bluealloy/revm/pull/3297)) +- *(revme)* consolidate find_all_json_tests into dir_utils ([#3262](https://github.com/bluealloy/revm/pull/3262)) +- happy new year, 2026 licence ([#3272](https://github.com/bluealloy/revm/pull/3272)) +- *(revme)* use unwrap_or_default for non-UTF8 path safety ([#3259](https://github.com/bluealloy/revm/pull/3259)) +- sort storage keys and test files in blockchaintest output ([#3186](https://github.com/bluealloy/revm/pull/3186)) +- *(revme)* extract JSON printing helper in blockchaintest ([#3257](https://github.com/bluealloy/revm/pull/3257)) +- remove redundant clone calls ([#3258](https://github.com/bluealloy/revm/pull/3258)) +- re-export statetest-types from revm crate behind test-types feature ([#3247](https://github.com/bluealloy/revm/pull/3247)) +- *(fmt)* merge all imports ([#3184](https://github.com/bluealloy/revm/pull/3184)) + +## [10.0.2](https://github.com/bluealloy/revm/compare/revme-v10.0.0...revme-v10.0.2) - 2025-11-14 + +### Other + +- update Cargo.lock dependencies + +## [10.0.0](https://github.com/bluealloy/revm/compare/revme-v9.0.2...revme-v10.0.0) - 2025-11-10 + +### Other + +- updated the following local packages: revm, revm-statetest-types + +## [9.0.2](https://github.com/bluealloy/revm/compare/revme-v9.0.1...revme-v9.0.2) - 2025-11-10 + +### Other + +- updated the following local packages: revm, revm-statetest-types + +## [9.0.1](https://github.com/bluealloy/revm/compare/revme-v9.0.0...revme-v9.0.1) - 2025-11-07 + +### Fixed + +- *(revme)* use primitive hashmap in statetest ([#3137](https://github.com/bluealloy/revm/pull/3137)) + +## [9.0.0](https://github.com/bluealloy/revm/compare/revme-v8.3.0...revme-v9.0.0) - 2025-10-30 + +### Other + +- consolidate revme imports ([#3088](https://github.com/bluealloy/revm/pull/3088)) +- Update blockchaintest.rs ([#3107](https://github.com/bluealloy/revm/pull/3107)) +- typo eip4788 ([#3111](https://github.com/bluealloy/revm/pull/3111)) + +## [8.3.0](https://github.com/bluealloy/revm/compare/revme-v8.2.2...revme-v8.3.0) - 2025-10-17 + +### Other + +- updated the following local packages: revm-inspector, revm, revm-statetest-types + +## [8.2.2](https://github.com/bluealloy/revm/compare/revme-v8.2.1...revme-v8.2.2) - 2025-10-15 + +### Other + +- updated the following local packages: revm-bytecode, revm-state, revm-database-interface, revm-context-interface, revm-context, revm-database, revm-inspector, revm, revm-statetest-types + ## [8.2.1](https://github.com/bluealloy/revm/compare/revme-v8.2.0...revme-v8.2.1) - 2025-10-15 ### Other diff --git a/bins/revme/Cargo.toml b/bins/revme/Cargo.toml index bfa8da575e..93dc009532 100644 --- a/bins/revme/Cargo.toml +++ b/bins/revme/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "revme" description = "Rust Ethereum Virtual Machine Executable" -version = "8.2.1" +version = "11.0.0" authors.workspace = true edition.workspace = true keywords.workspace = true @@ -11,16 +11,16 @@ rust-version.workspace = true [dependencies] # revm -revm = { workspace = true, features = ["std", "hashbrown", "c-kzg", "blst"] } -primitives.workspace = true -database.workspace = true -database-interface.workspace = true -state.workspace = true -bytecode = { workspace = true, features = ["std", "parse"] } -context.workspace = true -context-interface.workspace = true -inspector = { workspace = true, features = ["std", "tracer"] } -statetest-types.workspace = true +revm = { workspace = true, features = [ + "std", + "c-kzg", + "blst", + "tracer", + "parse", + "test-types", +] } + +# criterion criterion.workspace = true # alloy @@ -41,8 +41,10 @@ k256 = { workspace = true, features = ["ecdsa"] } csv = "1.1.6" [features] +default = ["map-foldhash"] # Optionally enable gmp because it doesn't work on i686 github actions runners gmp = ["revm/gmp"] +map-foldhash = ["revm/map-foldhash"] [[bench]] name = "evm" diff --git a/bins/revme/LICENSE b/bins/revme/LICENSE index be6d350ebe..7edb0405ff 100644 --- a/bins/revme/LICENSE +++ b/bins/revme/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021-2025 draganrakita +Copyright (c) 2021-2026 draganrakita Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/bins/revme/src/cmd/bench/analysis.rs b/bins/revme/src/cmd/bench/analysis.rs index e12bc97e96..816d5ea83b 100644 --- a/bins/revme/src/cmd/bench/analysis.rs +++ b/bins/revme/src/cmd/bench/analysis.rs @@ -1,8 +1,8 @@ -use context::TxEnv; use criterion::Criterion; -use database::{BenchmarkDB, BENCH_CALLER, BENCH_TARGET}; use revm::{ bytecode::Bytecode, + context::TxEnv, + database::{BenchmarkDB, BENCH_CALLER, BENCH_TARGET}, primitives::{bytes, hex, Bytes, TxKind}, Context, ExecuteEvm, MainBuilder, MainContext, }; diff --git a/bins/revme/src/cmd/bench/burntpix.rs b/bins/revme/src/cmd/bench/burntpix.rs index ba7a14f85f..1537dcb12c 100644 --- a/bins/revme/src/cmd/bench/burntpix.rs +++ b/bins/revme/src/cmd/bench/burntpix.rs @@ -1,8 +1,6 @@ pub mod static_data; -use context::TxEnv; use criterion::Criterion; -use primitives::{StorageKey, StorageValue}; use static_data::{ BURNTPIX_ADDRESS_ONE, BURNTPIX_ADDRESS_THREE, BURNTPIX_ADDRESS_TWO, BURNTPIX_BYTECODE_FOUR, BURNTPIX_BYTECODE_ONE, BURNTPIX_BYTECODE_THREE, BURNTPIX_BYTECODE_TWO, BURNTPIX_MAIN_ADDRESS, @@ -10,10 +8,11 @@ use static_data::{ }; use alloy_sol_types::{sol, SolCall}; -use database::{CacheDB, BENCH_CALLER}; use revm::{ + context::TxEnv, + database::{CacheDB, BENCH_CALLER}, database_interface::EmptyDB, - primitives::{hex, keccak256, Address, Bytes, TxKind, B256, U256}, + primitives::{hex, keccak256, Address, Bytes, StorageKey, StorageValue, TxKind, B256, U256}, state::{AccountInfo, Bytecode}, Context, ExecuteEvm, MainBuilder, MainContext, }; @@ -44,7 +43,7 @@ pub fn run(criterion: &mut Criterion) { let tx = TxEnv::builder() .caller(BENCH_CALLER) .kind(TxKind::Call(BURNTPIX_MAIN_ADDRESS)) - .data(run_call_data.clone().into()) + .data(run_call_data.into()) .gas_limit(u64::MAX) .build() .unwrap(); @@ -103,9 +102,12 @@ pub fn svg(filename: String, svg_data: &[u8]) -> Result<(), Box> { const DEFAULT_SEED: &str = "0"; const DEFAULT_ITERATIONS: &str = "0x4E20"; // 20_000 iterations fn try_init_env_vars() -> Result<(u32, U256), Box> { - let seed_from_env = std::env::var("SEED").unwrap_or(DEFAULT_SEED.to_string()); + // Use lazy default to avoid unnecessary allocation when env is set + let seed_from_env = std::env::var("SEED").unwrap_or_else(|_| DEFAULT_SEED.to_string()); let seed: u32 = try_from_hex_to_u32(&seed_from_env)?; - let iterations_from_env = std::env::var("ITERATIONS").unwrap_or(DEFAULT_ITERATIONS.to_string()); + // Use lazy default to avoid unnecessary allocation when env is set + let iterations_from_env = + std::env::var("ITERATIONS").unwrap_or_else(|_| DEFAULT_ITERATIONS.to_string()); let iterations = U256::from_str(&iterations_from_env)?; Ok((seed, iterations)) } diff --git a/bins/revme/src/cmd/bench/gas_cost_estimator.rs b/bins/revme/src/cmd/bench/gas_cost_estimator.rs index 06db3292a5..26d889233c 100644 --- a/bins/revme/src/cmd/bench/gas_cost_estimator.rs +++ b/bins/revme/src/cmd/bench/gas_cost_estimator.rs @@ -1,8 +1,8 @@ -use context::TxEnv; use criterion::Criterion; -use database::{BenchmarkDB, BENCH_CALLER, BENCH_TARGET}; use revm::{ bytecode::Bytecode, + context::TxEnv, + database::{BenchmarkDB, BENCH_CALLER, BENCH_TARGET}, primitives::{hex, Bytes, TxKind}, Context, ExecuteEvm, MainBuilder, MainContext, }; diff --git a/bins/revme/src/cmd/bench/snailtracer.rs b/bins/revme/src/cmd/bench/snailtracer.rs index d0a0bf5f4e..d9ea2d186a 100644 --- a/bins/revme/src/cmd/bench/snailtracer.rs +++ b/bins/revme/src/cmd/bench/snailtracer.rs @@ -1,9 +1,10 @@ -use context::TxEnv; use criterion::Criterion; -use database::{BenchmarkDB, BENCH_CALLER, BENCH_TARGET}; -use inspector::NoOpInspector; + use revm::{ bytecode::Bytecode, + context::TxEnv, + database::{BenchmarkDB, BENCH_CALLER, BENCH_TARGET}, + inspector::NoOpInspector, primitives::{bytes, hex, Bytes, TxKind}, Context, ExecuteEvm, InspectEvm, MainBuilder, MainContext, }; @@ -12,7 +13,7 @@ pub fn run(criterion: &mut Criterion) { let bytecode = Bytecode::new_raw(Bytes::from(hex::decode(BYTES).unwrap())); let mut evm = Context::mainnet() - .with_db(BenchmarkDB::new_bytecode(bytecode.clone())) + .with_db(BenchmarkDB::new_bytecode(bytecode)) .modify_cfg_chained(|c| c.disable_nonce_check = true) .build_mainnet() .with_inspector(NoOpInspector {}); diff --git a/bins/revme/src/cmd/bench/transfer.rs b/bins/revme/src/cmd/bench/transfer.rs index ac196dfe97..717cc5f8c4 100644 --- a/bins/revme/src/cmd/bench/transfer.rs +++ b/bins/revme/src/cmd/bench/transfer.rs @@ -1,9 +1,9 @@ -use context::{ContextTr, TxEnv}; use criterion::Criterion; -use database::{BenchmarkDB, BENCH_CALLER, BENCH_TARGET, BENCH_TARGET_BALANCE}; use revm::{ bytecode::Bytecode, + context::{ContextTr, TxEnv}, context_interface::JournalTr, + database::{BenchmarkDB, BENCH_CALLER, BENCH_TARGET, BENCH_TARGET_BALANCE}, primitives::{TxKind, U256}, Context, ExecuteEvm, MainBuilder, MainContext, }; diff --git a/bins/revme/src/cmd/bench/transfer_multi.rs b/bins/revme/src/cmd/bench/transfer_multi.rs index d22c467677..7312db41f9 100644 --- a/bins/revme/src/cmd/bench/transfer_multi.rs +++ b/bins/revme/src/cmd/bench/transfer_multi.rs @@ -1,12 +1,12 @@ -use context::TxEnv; use criterion::Criterion; -use database::{InMemoryDB, BENCH_CALLER, BENCH_TARGET}; use revm::{ + context::TxEnv, + database::{InMemoryDB, BENCH_CALLER, BENCH_TARGET}, interpreter::instructions::utility::IntoAddress, primitives::{TxKind, U256}, + state::AccountInfo, Context, ExecuteCommitEvm, ExecuteEvm, MainBuilder, MainContext, }; -use state::AccountInfo; pub fn run(criterion: &mut Criterion) { let mut db = InMemoryDB::default(); diff --git a/bins/revme/src/cmd/blockchaintest.rs b/bins/revme/src/cmd/blockchaintest.rs index 009c299c62..44503babb6 100644 --- a/bins/revme/src/cmd/blockchaintest.rs +++ b/bins/revme/src/cmd/blockchaintest.rs @@ -1,29 +1,37 @@ pub mod post_block; pub mod pre_block; +use crate::dir_utils::find_all_json_tests; use clap::Parser; -use context::ContextTr; -use context_interface::block::BlobExcessGasAndPrice; -use database::states::bundle_state::BundleRetention; -use database::{EmptyDB, State}; -use inspector::inspectors::TracerEip3155; -use primitives::{hardfork::SpecId, hex, Address, HashMap, U256}; -use revm::handler::EvmTr; + +use revm::statetest_types::blockchain::{ + Account, BlockchainTest, BlockchainTestCase, ForkSpec, Withdrawal, +}; use revm::{ - context::cfg::CfgEnv, context_interface::result::HaltReason, Context, MainBuilder, MainContext, + bytecode::Bytecode, + context::{cfg::CfgEnv, ContextTr}, + context_interface::{block::BlobExcessGasAndPrice, result::HaltReason}, + database::{states::bundle_state::BundleRetention, EmptyDB, State}, + handler::EvmTr, + inspector::inspectors::TracerEip3155, + primitives::{hardfork::SpecId, hex, Address, HashMap, U256}, + state::{bal::Bal, AccountInfo}, + Context, Database, ExecuteCommitEvm, ExecuteEvm, InspectEvm, MainBuilder, MainContext, }; -use revm::{Database, ExecuteCommitEvm, ExecuteEvm, InspectEvm}; use serde_json::json; -use state::AccountInfo; -use statetest_types::blockchain::{ - Account, BlockchainTest, BlockchainTestCase, ForkSpec, Withdrawal, +use std::{ + collections::BTreeMap, + fs, + path::{Path, PathBuf}, + sync::Arc, + time::Instant, }; -use std::collections::BTreeMap; -use std::fs; -use std::path::{Path, PathBuf}; -use std::time::Instant; use thiserror::Error; -use walkdir::{DirEntry, WalkDir}; + +/// Panics if the value cannot be serialized to JSON. +fn print_json(value: &T) { + println!("{}", serde_json::to_string(value).unwrap()); +} /// `blockchaintest` subcommand #[derive(Parser, Debug)] @@ -78,22 +86,6 @@ impl Cmd { } } -/// Find all JSON test files in the given path -/// If path is a file, returns it in a vector -/// If path is a directory, recursively finds all .json files -pub fn find_all_json_tests(path: &Path) -> Vec { - if path.is_file() { - vec![path.to_path_buf()] - } else { - WalkDir::new(path) - .into_iter() - .filter_map(Result::ok) - .filter(|e| e.path().extension() == Some("json".as_ref())) - .map(DirEntry::into_path) - .collect() - } -} - /// Run all blockchain tests from the given files fn run_tests( test_files: Vec, @@ -120,7 +112,7 @@ fn run_tests( "status": "skipped", "reason": "known_issue" }); - println!("{}", serde_json::to_string(&output).unwrap()); + print_json(&output); } else if !omit_progress { println!( "Skipping ({}/{}): {}", @@ -160,7 +152,7 @@ fn run_tests( "error": e.to_string(), "status": "failed" }); - println!("{}", serde_json::to_string(&output).unwrap()); + print_json(&output); } else if !omit_progress { eprintln!( "✗ ({}/{}) {} - {}", @@ -189,7 +181,7 @@ fn run_tests( "duration_secs": duration.as_secs_f64(), } }); - println!("{}", serde_json::to_string(&results).unwrap()); + print_json(&results); } else { // Print failed test paths if keep-going was enabled if keep_going && !failed_paths.is_empty() { @@ -235,7 +227,7 @@ fn run_test_file( "file": file_path.display().to_string(), "status": "running" }); - println!("{}", serde_json::to_string(&output).unwrap()); + print_json(&output); } else { println!(" Running: {test_name}"); } @@ -250,7 +242,7 @@ fn run_test_file( "file": file_path.display().to_string(), "status": "passed" }); - println!("{}", serde_json::to_string(&output).unwrap()); + print_json(&output); } test_count += 1; } @@ -262,10 +254,10 @@ fn run_test_file( "status": "failed", "error": e.to_string() }); - println!("{}", serde_json::to_string(&output).unwrap()); + print_json(&output); } return Err(Error::TestExecution { - test_name: test_name.clone(), + test_name, test_path: file_path.to_path_buf(), error: e.to_string(), }); @@ -300,12 +292,12 @@ impl DebugInfo { fn capture_committed_state( state: &State, ) -> HashMap)> { - let mut committed_state = HashMap::new(); + let mut committed_state = HashMap::default(); // Access the cache state to get all accounts for (address, cache_account) in &state.cache.accounts { if let Some(plain_account) = &cache_account.account { - let mut storage = HashMap::new(); + let mut storage = HashMap::default(); for (key, value) in &plain_account.storage { storage.insert(*key, *value); } @@ -324,6 +316,28 @@ fn validate_post_state( debug_info: &DebugInfo, print_env_on_error: bool, ) -> Result<(), TestExecutionError> { + #[allow(clippy::too_many_arguments)] + fn make_failure( + state: &mut State, + debug_info: &DebugInfo, + expected_post_state: &BTreeMap, + print_env_on_error: bool, + address: Address, + field: String, + expected: String, + actual: String, + ) -> Result<(), TestExecutionError> { + if print_env_on_error { + print_error_with_state(debug_info, state, Some(expected_post_state)); + } + Err(TestExecutionError::PostStateValidation { + address, + field, + expected, + actual, + }) + } + for (address, expected_account) in expected_post_state { // Load account from final state let actual_account = state @@ -337,78 +351,79 @@ fn validate_post_state( // Validate balance if info.balance != expected_account.balance { - if print_env_on_error { - print_error_with_state(debug_info, state, Some(expected_post_state)); - } - return Err(TestExecutionError::PostStateValidation { - address: *address, - field: "balance".to_string(), - expected: format!("{}", expected_account.balance), - actual: format!("{}", info.balance), - }); + return make_failure( + state, + debug_info, + expected_post_state, + print_env_on_error, + *address, + "balance".to_string(), + format!("{}", expected_account.balance), + format!("{}", info.balance), + ); } // Validate nonce let expected_nonce = expected_account.nonce.to::(); if info.nonce != expected_nonce { - if print_env_on_error { - print_error_with_state(debug_info, state, Some(expected_post_state)); - } - return Err(TestExecutionError::PostStateValidation { - address: *address, - field: "nonce".to_string(), - expected: format!("{expected_nonce}"), - actual: format!("{}", info.nonce), - }); + return make_failure( + state, + debug_info, + expected_post_state, + print_env_on_error, + *address, + "nonce".to_string(), + format!("{expected_nonce}"), + format!("{}", info.nonce), + ); } // Validate code if present if !expected_account.code.is_empty() { if let Some(actual_code) = &info.code { if actual_code.original_bytes() != expected_account.code { - if print_env_on_error { - print_error_with_state(debug_info, state, Some(expected_post_state)); - } - return Err(TestExecutionError::PostStateValidation { - address: *address, - field: "code".to_string(), - expected: format!("0x{}", hex::encode(&expected_account.code)), - actual: format!("0x{}", hex::encode(actual_code.bytecode())), - }); + return make_failure( + state, + debug_info, + expected_post_state, + print_env_on_error, + *address, + "code".to_string(), + format!("0x{}", hex::encode(&expected_account.code)), + format!("0x{}", hex::encode(actual_code.original_byte_slice())), + ); } } else { - if print_env_on_error { - print_error_with_state(debug_info, state, Some(expected_post_state)); - } - return Err(TestExecutionError::PostStateValidation { - address: *address, - field: "code".to_string(), - expected: format!("0x{}", hex::encode(&expected_account.code)), - actual: "empty".to_string(), - }); + return make_failure( + state, + debug_info, + expected_post_state, + print_env_on_error, + *address, + "code".to_string(), + format!("0x{}", hex::encode(&expected_account.code)), + "empty".to_string(), + ); } } - // Check for unexpected storage entries - for (slot, actual_value) in actual_account - .account - .as_ref() - .map(|a| &a.storage) - .unwrap_or(&HashMap::new()) - .iter() - { - let slot = *slot; - let actual_value = *actual_value; - if !expected_account.storage.contains_key(&slot) && !actual_value.is_zero() { - if print_env_on_error { - print_error_with_state(debug_info, state, Some(expected_post_state)); + // Check for unexpected storage entries. Avoid allocating a temporary HashMap when the account is None. + if let Some(acc) = actual_account.account.as_ref() { + for (slot, actual_value) in &acc.storage { + let slot = *slot; + let actual_value = *actual_value; + if !expected_account.storage.contains_key(&slot) && !actual_value.is_zero() { + return make_failure( + state, + debug_info, + expected_post_state, + print_env_on_error, + *address, + format!("storage_unexpected[{slot}]"), + "0x0".to_string(), + format!("{actual_value}"), + ); } - return Err(TestExecutionError::PostStateValidation { - address: *address, - field: format!("storage_unexpected[{slot}]"), - expected: "0x0".to_string(), - actual: format!("{actual_value}"), - }); } } @@ -418,16 +433,16 @@ fn validate_post_state( let actual_value = actual_value.unwrap_or_default(); if actual_value != *expected_value { - if print_env_on_error { - print_error_with_state(debug_info, state, Some(expected_post_state)); - } - - return Err(TestExecutionError::PostStateValidation { - address: *address, - field: format!("storage_validation[{slot}]"), - expected: format!("{expected_value}"), - actual: format!("{actual_value}"), - }); + return make_failure( + state, + debug_info, + expected_post_state, + print_env_on_error, + *address, + format!("storage_validation[{slot}]"), + format!("{expected_value}"), + format!("{actual_value}"), + ); } } } @@ -450,7 +465,7 @@ fn print_error_with_state( // Print configuration environment eprintln!("\n📋 Configuration Environment:"); - eprintln!(" Spec ID: {:?}", debug_info.cfg_env.spec); + eprintln!(" Spec ID: {:?}", debug_info.cfg_env.spec()); eprintln!(" Chain ID: {}", debug_info.cfg_env.chain_id); eprintln!( " Limit contract code size: {:?}", @@ -470,10 +485,9 @@ fn print_error_with_state( eprintln!(" Difficulty: {}", debug_info.block_env.difficulty); eprintln!(" Prevrandao: {:?}", debug_info.block_env.prevrandao); eprintln!(" Beneficiary: {:?}", debug_info.block_env.beneficiary); - eprintln!( - " Blob excess gas: {:?}", - debug_info.block_env.blob_excess_gas_and_price - ); + let blob = debug_info.block_env.blob_excess_gas_and_price; + eprintln!(" Blob excess gas: {:?}", blob.map(|a| a.excess_blob_gas)); + eprintln!(" Blob gas price: {:?}", blob.map(|a| a.blob_gasprice)); // Print withdrawals if let Some(withdrawals) = &debug_info.withdrawals { @@ -562,45 +576,49 @@ fn print_error_with_state( // Print state comparison eprintln!("\n💾 Pre-State (Initial):"); - for (address, (info, storage)) in &debug_info.pre_state { + // Sort accounts by address for consistent output + let mut sorted_accounts: Vec<_> = debug_info.pre_state.iter().collect(); + sorted_accounts.sort_by_key(|(addr, _)| *addr); + for (address, (info, storage)) in sorted_accounts { eprintln!(" Account {address:?}:"); eprintln!(" Balance: 0x{:x}", info.balance); eprintln!(" Nonce: {}", info.nonce); eprintln!(" Code hash: {:?}", info.code_hash); eprintln!( " Code size: {} bytes", - info.code.as_ref().map_or(0, |c| c.bytecode().len()) + info.code.as_ref().map_or(0, |c| c.len()) ); if !storage.is_empty() { eprintln!(" Storage ({} slots):", storage.len()); - for (key, value) in storage.iter().take(5) { + let mut sorted_storage: Vec<_> = storage.iter().collect(); + sorted_storage.sort_by_key(|(key, _)| *key); + for (key, value) in sorted_storage.iter() { eprintln!(" {key:?} => {value:?}"); } - if storage.len() > 5 { - eprintln!(" ... and {} more slots", storage.len() - 5); - } } } eprintln!("\n📝 Current State (Actual):"); let committed_state = DebugInfo::capture_committed_state(current_state); - for (address, (info, storage)) in &committed_state { + // Sort accounts by address for consistent output + let mut sorted_current: Vec<_> = committed_state.iter().collect(); + sorted_current.sort_by_key(|(addr, _)| *addr); + for (address, (info, storage)) in sorted_current { eprintln!(" Account {address:?}:"); eprintln!(" Balance: 0x{:x}", info.balance); eprintln!(" Nonce: {}", info.nonce); eprintln!(" Code hash: {:?}", info.code_hash); eprintln!( " Code size: {} bytes", - info.code.as_ref().map_or(0, |c| c.bytecode().len()) + info.code.as_ref().map_or(0, |c| c.len()) ); if !storage.is_empty() { eprintln!(" Storage ({} slots):", storage.len()); - for (key, value) in storage.iter().take(5) { + let mut sorted_storage: Vec<_> = storage.iter().collect(); + sorted_storage.sort_by_key(|(key, _)| *key); + for (key, value) in sorted_storage.iter() { eprintln!(" {key:?} => {value:?}"); } - if storage.len() > 5 { - eprintln!(" ... and {} more slots", storage.len() - 5); - } } } @@ -616,12 +634,9 @@ fn print_error_with_state( } if !account.storage.is_empty() { eprintln!(" Storage ({} slots):", account.storage.len()); - for (key, value) in account.storage.iter().take(5) { + for (key, value) in account.storage.iter() { eprintln!(" {key:?} => {value:?}"); } - if account.storage.len() > 5 { - eprintln!(" ... and {} more slots", account.storage.len() - 5); - } } } } @@ -650,10 +665,10 @@ fn execute_blockchain_test( } // Create database with initial state - let mut state = State::builder().build(); + let mut state = State::builder().with_bal_builder().build(); // Capture pre-state for debug info - let mut pre_state_debug = HashMap::new(); + let mut pre_state_debug = HashMap::default(); // Insert genesis state into database let genesis_state = test_case.pre.clone().into_genesis_state(); @@ -661,8 +676,9 @@ fn execute_blockchain_test( let account_info = AccountInfo { balance: account.balance, nonce: account.nonce, - code_hash: primitives::keccak256(&account.code), - code: Some(bytecode::Bytecode::new_raw(account.code.clone())), + code_hash: revm::primitives::keccak256(&account.code), + code: Some(Bytecode::new_raw(account.code.clone())), + account_id: None, }; // Store for debug info @@ -681,7 +697,7 @@ fn execute_blockchain_test( // Setup configuration based on fork let spec_id = fork_to_spec_id(test_case.network); let mut cfg = CfgEnv::default(); - cfg.spec = spec_id; + cfg.set_spec_and_mainnet_gas_params(spec_id); // Genesis block is not used yet. let mut parent_block_hash = Some(test_case.genesis_block_header.hash); @@ -719,10 +735,19 @@ fn execute_blockchain_test( this_excess_blob_gas = None; } + let bal_test = block + .block_access_list + .as_ref() + .and_then(|bal| Bal::try_from(bal.clone()).ok()) + .map(Arc::new); + + //state.set_bal(bal_test); + state.reset_bal_index(); + // Create EVM context for each transaction to ensure fresh state access let evm_context = Context::mainnet() .with_block(&block_env) - .with_cfg(&cfg) + .with_cfg(cfg.clone()) .with_db(&mut state); // Build and execute with EVM - always use inspector when JSON output is enabled @@ -757,7 +782,7 @@ fn execute_blockchain_test( "error": "missing sender", "status": "skipped" }); - println!("{}", serde_json::to_string(&output).unwrap()); + print_json(&output); } else { eprintln!("⚠️ Skipping block {block_idx} due to missing sender"); } @@ -794,7 +819,7 @@ fn execute_blockchain_test( "error": format!("tx env creation error: {e}"), "status": "skipped" }); - println!("{}", serde_json::to_string(&output).unwrap()); + print_json(&output); } else { eprintln!( "⚠️ Skipping block {block_idx} due to transaction env creation error: {e}" @@ -804,6 +829,9 @@ fn execute_blockchain_test( } }; + // bump bal index + evm.db_mut().bump_bal_index(); + // If JSON output requested, output transaction details let execution_result = if json_output { evm.inspect_tx(tx_env.clone()) @@ -840,19 +868,19 @@ fn execute_blockchain_test( test_case.post_state.as_ref(), ); } - let exception = block.expect_exception.clone().unwrap_or_default(); + let expected_exception = block.expect_exception.clone().unwrap_or_default(); if json_output { let output = json!({ "block": block_idx, "tx": tx_idx, - "error": format!("expected failure: {exception}"), + "expected_exception": expected_exception, "gas_used": result.result.gas_used(), "status": "unexpected_success" }); - println!("{}", serde_json::to_string(&output).unwrap()); + print_json(&output); } else { eprintln!( - "⚠️ Skipping block {block_idx} due to expected failure: {exception}" + "⚠️ Skipping block {block_idx}: transaction unexpectedly succeeded (expected failure: {expected_exception})" ); } break; // Skip to next block @@ -892,7 +920,7 @@ fn execute_blockchain_test( "error": format!("{e:?}"), "status": "unexpected_failure" }); - println!("{}", serde_json::to_string(&output).unwrap()); + print_json(&output); } else { eprintln!( "⚠️ Skipping block {block_idx} due to unexpected failure: {e:?}" @@ -907,12 +935,15 @@ fn execute_blockchain_test( "error": format!("{e:?}"), "status": "expected_failure" }); - println!("{}", serde_json::to_string(&output).unwrap()); + print_json(&output); } } } } + // bump bal index + evm.db_mut().bump_bal_index(); + // uncle rewards are not implemented yet post_block::post_block_transition( &mut evm, @@ -926,6 +957,19 @@ fn execute_blockchain_test( .block_hashes .insert(block_env.number.to::(), block_hash.unwrap_or_default()); + if let Some(bal) = state.bal_state.bal_builder.take() { + if let Some(state_bal) = bal_test { + if &bal != state_bal.as_ref() { + println!("Bal mismatch"); + println!("Test bal"); + state_bal.pretty_print(); + println!("Bal:"); + bal.pretty_print(); + return Err(TestExecutionError::BalMismatchError); + } + } + } + parent_block_hash = block_hash; if let Some(excess_blob_gas) = this_excess_blob_gas { parent_excess_blob_gas = excess_blob_gas; @@ -979,7 +1023,8 @@ fn fork_to_spec_id(fork: ForkSpec) -> SpecId { ForkSpec::Cancun | ForkSpec::ShanghaiToCancunAtTime15k => SpecId::CANCUN, ForkSpec::Prague | ForkSpec::CancunToPragueAtTime15k => SpecId::PRAGUE, ForkSpec::Osaka | ForkSpec::PragueToOsakaAtTime15k => SpecId::OSAKA, - _ => SpecId::OSAKA, // For any unknown forks, use latest available + ForkSpec::Amsterdam => SpecId::AMSTERDAM, + _ => SpecId::AMSTERDAM, // For any unknown forks, use latest available } } @@ -997,7 +1042,7 @@ fn skip_test(path: &Path) -> bool { return true; } - let name = path.file_name().unwrap().to_str().unwrap(); + let name = path.file_name().unwrap().to_str().unwrap_or_default(); // Add any problematic tests here that should be skipped matches!( name, @@ -1084,6 +1129,9 @@ pub enum TestExecutionError { gas_used: u64, }, + #[error("BAL error")] + BalMismatchError, + #[error( "Post-state validation failed for {address:?}.{field}: expected {expected}, got {actual}" )] diff --git a/bins/revme/src/cmd/blockchaintest/post_block.rs b/bins/revme/src/cmd/blockchaintest/post_block.rs index 4c85d9b0ee..22dc75a587 100644 --- a/bins/revme/src/cmd/blockchaintest/post_block.rs +++ b/bins/revme/src/cmd/blockchaintest/post_block.rs @@ -1,12 +1,15 @@ -use context::{Block, ContextTr}; -use database::State; -use primitives::{hardfork::SpecId, ONE_ETHER, ONE_GWEI}; -use revm::{handler::EvmTr, Database, SystemCallCommitEvm}; -use statetest_types::blockchain::Withdrawal; +use revm::{ + context::{Block, ContextTr, JournalTr}, + handler::EvmTr, + primitives::{address, hardfork::SpecId, Address, Bytes, ONE_ETHER, ONE_GWEI, U256}, + statetest_types::blockchain::Withdrawal, + Database, DatabaseCommit, SystemCallCommitEvm, +}; /// Post block transition that includes: /// * Block and uncle rewards before the Merge/Paris hardfork. -/// * system calls +/// * Withdrawals (EIP-4895) +/// * Post-block system calls: EIP-7002 (withdrawal requests) and EIP-7251 (consolidation requests) /// /// # Note /// @@ -14,9 +17,8 @@ use statetest_types::blockchain::Withdrawal; #[inline] pub fn post_block_transition< 'a, - DB: Database + 'a, - EVM: SystemCallCommitEvm - + EvmTr>>, + DB: Database + DatabaseCommit + 'a, + EVM: SystemCallCommitEvm + EvmTr>, >( evm: &mut EVM, block: impl Block, @@ -26,24 +28,36 @@ pub fn post_block_transition< // block reward let block_reward = block_reward(spec, 0); if block_reward != 0 { - let _ = evm - .ctx_mut() - .db_mut() - .increment_balances(vec![(block.beneficiary(), block_reward)]); + evm.ctx_mut() + .journal_mut() + .balance_incr(block.beneficiary(), U256::from(block_reward)) + .expect("Db actions to pass"); } // withdrawals if spec.is_enabled_in(SpecId::SHANGHAI) { for withdrawal in withdrawals { evm.ctx_mut() - .db_mut() - .increment_balances(vec![( + .journal_mut() + .balance_incr( withdrawal.address, - withdrawal.amount.to::().saturating_mul(ONE_GWEI), - )]) + withdrawal.amount.saturating_mul(U256::from(ONE_GWEI)), + ) .expect("Db actions to pass"); } } + + evm.commit_inner(); + + // EIP-7002: Withdrawal requests system call + if spec.is_enabled_in(SpecId::PRAGUE) { + system_call_eip7002_withdrawal_request(evm); + } + + // EIP-7251: Consolidation requests system call + if spec.is_enabled_in(SpecId::PRAGUE) { + system_call_eip7251_consolidation_request(evm); + } } /// Block reward for a block. @@ -63,3 +77,34 @@ pub const fn block_reward(spec: SpecId, ommers: usize) -> u128 { reward + (reward >> 5) * ommers as u128 } + +pub const WITHDRAWAL_REQUEST_ADDRESS: Address = + address!("0x00000961Ef480Eb55e80D19ad83579A64c007002"); + +/// EIP-7002: Withdrawal requests system call +pub(crate) fn system_call_eip7002_withdrawal_request( + evm: &mut impl SystemCallCommitEvm, +) { + // empty data is valid for EIP-7002 + let _ = match evm.system_call_commit(WITHDRAWAL_REQUEST_ADDRESS, Bytes::new()) { + Ok(res) => res, + Err(e) => { + panic!("System call failed: {e:?}"); + } + }; +} + +pub const CONSOLIDATION_REQUEST_ADDRESS: Address = + address!("0x0000BBdDc7CE488642fb579F8B00f3a590007251"); + +/// EIP-7251: Consolidation requests system call +pub(crate) fn system_call_eip7251_consolidation_request( + evm: &mut impl SystemCallCommitEvm, +) { + let _ = match evm.system_call_commit(CONSOLIDATION_REQUEST_ADDRESS, Bytes::new()) { + Ok(res) => res, + Err(e) => { + panic!("System call failed: {e:?}"); + } + }; +} diff --git a/bins/revme/src/cmd/blockchaintest/pre_block.rs b/bins/revme/src/cmd/blockchaintest/pre_block.rs index b3611cbbd6..06997d418c 100644 --- a/bins/revme/src/cmd/blockchaintest/pre_block.rs +++ b/bins/revme/src/cmd/blockchaintest/pre_block.rs @@ -1,20 +1,22 @@ //! Pre block state transition -use context::{Block, ContextTr}; -use database::State; -use primitives::{address, hardfork::SpecId, Address, B256}; -use revm::{handler::EvmTr, Database, SystemCallCommitEvm}; +use revm::{ + context::{Block, ContextTr}, + database::Database, + handler::EvmTr, + primitives::{address, hardfork::SpecId, Address, B256}, + DatabaseCommit, SystemCallCommitEvm, +}; /// Pre block state transition /// /// # Note /// -/// Contains only withdrawal processing. And it is missing block hash system call. +/// Contains pre-block system calls: EIP-2935 (blockhash) and EIP-4788 (beacon root). pub fn pre_block_transition< 'a, - DB: Database + 'a, - EVM: SystemCallCommitEvm - + EvmTr>>, + DB: Database + DatabaseCommit + 'a, + EVM: SystemCallCommitEvm + EvmTr>, >( evm: &mut EVM, spec: SpecId, @@ -28,11 +30,15 @@ pub fn pre_block_transition< // blockhash system call if let Some(parent_block_hash) = parent_block_hash { - system_call_eip2935_blockhash(spec, parent_block_hash, evm); + if spec.is_enabled_in(SpecId::PRAGUE) { + system_call_eip2935_blockhash(evm, parent_block_hash); + } } if let Some(parent_beacon_block_root) = parent_beacon_block_root { - system_call_eip4844_beacon_root(spec, parent_beacon_block_root, evm); + if spec.is_enabled_in(SpecId::CANCUN) { + system_call_eip4788_beacon_root(evm, parent_beacon_block_root); + } } } @@ -41,42 +47,28 @@ pub const HISTORY_STORAGE_ADDRESS: Address = address!("0x0000F90827F1C53a10cb7A0 /// Blockhash system callEIP-2935 #[inline] pub(crate) fn system_call_eip2935_blockhash( - spec: SpecId, - parent_block_hash: B256, evm: &mut impl SystemCallCommitEvm, -) -> bool { - if !spec.is_enabled_in(SpecId::PRAGUE) { - return true; - } - + parent_block_hash: B256, +) { let _ = match evm.system_call_commit(HISTORY_STORAGE_ADDRESS, parent_block_hash.0.into()) { Ok(res) => res, Err(e) => { panic!("System call failed: {e:?}"); } }; - - true } pub const BEACON_ROOTS_ADDRESS: Address = address!("000F3df6D732807Ef1319fB7B8bB8522d0Beac02"); /// Beacon root system call EIP-4788 -pub(crate) fn system_call_eip4844_beacon_root( - spec: SpecId, - parent_beacon_block_root: B256, +pub(crate) fn system_call_eip4788_beacon_root( evm: &mut impl SystemCallCommitEvm, -) -> bool { - if !spec.is_enabled_in(SpecId::CANCUN) { - return true; - } - + parent_beacon_block_root: B256, +) { let _ = match evm.system_call_commit(BEACON_ROOTS_ADDRESS, parent_beacon_block_root.0.into()) { Ok(res) => res, Err(e) => { panic!("System call failed: {e:?}"); } }; - - true } diff --git a/bins/revme/src/cmd/evmrunner.rs b/bins/revme/src/cmd/evmrunner.rs index 7a626a83fb..2613007cc4 100644 --- a/bins/revme/src/cmd/evmrunner.rs +++ b/bins/revme/src/cmd/evmrunner.rs @@ -1,15 +1,13 @@ use clap::Parser; -use context::TxEnv; -use database::{BenchmarkDB, BENCH_CALLER, BENCH_TARGET}; -use inspector::{inspectors::TracerEip3155, InspectEvm}; use revm::{ bytecode::{Bytecode, BytecodeDecodeError}, + context::TxEnv, + database::{BenchmarkDB, BENCH_CALLER, BENCH_TARGET}, + inspector::{inspectors::TracerEip3155, InspectEvm}, primitives::{hex, TxKind}, Context, Database, ExecuteEvm, MainBuilder, MainContext, }; -use std::path::PathBuf; -use std::{borrow::Cow, fs}; -use std::{io::Error as IoError, time::Instant}; +use std::{borrow::Cow, fs, io::Error as IoError, path::PathBuf, time::Instant}; #[derive(Debug, thiserror::Error)] pub enum Errors { diff --git a/bins/revme/src/cmd/statetest.rs b/bins/revme/src/cmd/statetest.rs index 3094316d53..5c583c0d18 100644 --- a/bins/revme/src/cmd/statetest.rs +++ b/bins/revme/src/cmd/statetest.rs @@ -4,8 +4,9 @@ pub mod utils; pub use runner::{TestError as Error, TestErrorKind}; +use crate::dir_utils::find_all_json_tests; use clap::Parser; -use runner::{find_all_json_tests, run, TestError}; +use runner::{run, TestError}; use std::path::PathBuf; /// `statetest` subcommand diff --git a/bins/revme/src/cmd/statetest/merkle_trie.rs b/bins/revme/src/cmd/statetest/merkle_trie.rs index c6b6418787..0ac3ffb093 100644 --- a/bins/revme/src/cmd/statetest/merkle_trie.rs +++ b/bins/revme/src/cmd/statetest/merkle_trie.rs @@ -1,11 +1,13 @@ use std::convert::Infallible; use alloy_rlp::{RlpEncodable, RlpMaxEncodedLen}; -use context::result::{EVMError, ExecutionResult, HaltReason, InvalidTransaction}; -use database::{EmptyDB, PlainAccount, State}; use hash_db::Hasher; use plain_hasher::PlainHasher; -use revm::primitives::{keccak256, Address, Log, B256, U256}; +use revm::{ + context::result::{EVMError, ExecutionResult, HaltReason, InvalidTransaction}, + database::{bal::EvmDatabaseError, EmptyDB, PlainAccount, State}, + primitives::{keccak256, Address, Log, B256, U256}, +}; use triehash::sec_trie_root; pub struct TestValidationResult { @@ -14,7 +16,10 @@ pub struct TestValidationResult { } pub fn compute_test_roots( - exec_result: &Result, EVMError>, + exec_result: &Result< + ExecutionResult, + EVMError, InvalidTransaction>, + >, db: &State, ) -> TestValidationResult { TestValidationResult { diff --git a/bins/revme/src/cmd/statetest/runner.rs b/bins/revme/src/cmd/statetest/runner.rs index 3b517c8214..757bfac0d2 100644 --- a/bins/revme/src/cmd/statetest/runner.rs +++ b/bins/revme/src/cmd/statetest/runner.rs @@ -1,20 +1,16 @@ use crate::cmd::statetest::merkle_trie::{compute_test_roots, TestValidationResult}; -use database::State; use indicatif::{ProgressBar, ProgressDrawTarget}; -use inspector::{inspectors::TracerEip3155, InspectCommitEvm}; -use primitives::U256; use revm::{ context::{block::BlockEnv, cfg::CfgEnv, tx::TxEnv}, - context_interface::{ - result::{EVMError, ExecutionResult, HaltReason, InvalidTransaction}, - Cfg, - }, + context_interface::result::{EVMError, ExecutionResult, HaltReason, InvalidTransaction}, + database::{self, bal::EvmDatabaseError}, database_interface::EmptyDB, - primitives::{hardfork::SpecId, Bytes, B256}, + inspector::{inspectors::TracerEip3155, InspectCommitEvm}, + primitives::{hardfork::SpecId, Bytes, B256, U256}, + statetest_types::{SpecName, Test, TestSuite, TestUnit}, Context, ExecuteCommitEvm, MainBuilder, MainContext, }; use serde_json::json; -use statetest_types::{SpecName, Test, TestSuite, TestUnit}; use std::{ convert::Infallible, fmt::Debug, @@ -27,7 +23,6 @@ use std::{ time::{Duration, Instant}, }; use thiserror::Error; -use walkdir::{DirEntry, WalkDir}; /// Error that occurs during test execution #[derive(Debug, Error)] @@ -67,22 +62,6 @@ pub enum TestErrorKind { NoJsonFiles, } -/// Find all JSON test files in the given path -/// If path is a file, returns it in a vector -/// If path is a directory, recursively finds all .json files -pub fn find_all_json_tests(path: &Path) -> Vec { - if path.is_file() { - vec![path.to_path_buf()] - } else { - WalkDir::new(path) - .into_iter() - .filter_map(Result::ok) - .filter(|e| e.path().extension() == Some("json".as_ref())) - .map(DirEntry::into_path) - .collect() - } -} - /// Check if a test should be skipped based on its filename /// Some tests are known to be problematic or take too long fn skip_test(path: &Path) -> bool { @@ -153,7 +132,10 @@ struct DebugContext<'a> { fn build_json_output( test: &Test, test_name: &str, - exec_result: &Result, EVMError>, + exec_result: &Result< + ExecutionResult, + EVMError, InvalidTransaction>, + >, validation: &TestValidationResult, spec: SpecId, error: Option, @@ -176,7 +158,10 @@ fn build_json_output( } fn format_evm_result( - exec_result: &Result, EVMError>, + exec_result: &Result< + ExecutionResult, + EVMError, InvalidTransaction>, + >, ) -> String { match exec_result { Ok(r) => match r { @@ -190,7 +175,10 @@ fn format_evm_result( fn validate_exception( test: &Test, - exec_result: &Result, EVMError>, + exec_result: &Result< + ExecutionResult, + EVMError, InvalidTransaction>, + >, ) -> Result { match (&test.expect_exception, exec_result) { (None, Ok(_)) => Ok(false), // No exception expected, execution succeeded @@ -221,8 +209,11 @@ fn check_evm_execution( test: &Test, expected_output: Option<&Bytes>, test_name: &str, - exec_result: &Result, EVMError>, - db: &mut State, + exec_result: &Result< + ExecutionResult, + EVMError, InvalidTransaction>, + >, + db: &mut database::State, spec: SpecId, print_json_outcome: bool, ) -> Result<(), TestErrorKind> { @@ -329,19 +320,19 @@ pub fn execute_test_suite( continue; } - cfg.spec = spec_name.to_spec_id(); + cfg.set_spec_and_mainnet_gas_params(spec_name.to_spec_id()); // Configure max blobs per spec - if cfg.spec.is_enabled_in(SpecId::OSAKA) { + if cfg.spec().is_enabled_in(SpecId::OSAKA) { cfg.set_max_blobs_per_tx(6); - } else if cfg.spec.is_enabled_in(SpecId::PRAGUE) { + } else if cfg.spec().is_enabled_in(SpecId::PRAGUE) { cfg.set_max_blobs_per_tx(9); } else { cfg.set_max_blobs_per_tx(6); } // Setup block environment for this spec - let block = unit.block_env(&cfg); + let block = unit.block_env(&mut cfg); for (index, test) in tests.iter().enumerate() { // Setup transaction environment @@ -350,8 +341,8 @@ pub fn execute_test_suite( Err(_) if test.expect_exception.is_some() => continue, Err(_) => { return Err(TestError { - name: name.clone(), - path: path.clone(), + name, + path, kind: TestErrorKind::UnknownPrivateKey(unit.transaction.secret_key), }); } @@ -376,8 +367,8 @@ pub fn execute_test_suite( static FAILED: AtomicBool = AtomicBool::new(false); if print_json_outcome || FAILED.swap(true, Ordering::SeqCst) { return Err(TestError { - name: name.clone(), - path: path.clone(), + name, + path, kind: e, }); } @@ -396,8 +387,8 @@ pub fn execute_test_suite( }); return Err(TestError { - path: path.clone(), - name: name.clone(), + path, + name, kind: e, }); } @@ -410,7 +401,8 @@ pub fn execute_test_suite( fn execute_single_test(ctx: TestExecutionContext) -> Result<(), TestErrorKind> { // Prepare state let mut cache = ctx.cache_state.clone(); - cache.set_state_clear_flag(ctx.cfg.spec.is_enabled_in(SpecId::SPURIOUS_DRAGON)); + let spec = ctx.cfg.spec(); + cache.set_state_clear_flag(spec.is_enabled_in(SpecId::SPURIOUS_DRAGON)); let mut state = database::State::builder() .with_cached_prestate(cache) .with_bundle_update() @@ -419,7 +411,7 @@ fn execute_single_test(ctx: TestExecutionContext) -> Result<(), TestErrorKind> { let evm_context = Context::mainnet() .with_block(ctx.block) .with_tx(ctx.tx) - .with_cfg(ctx.cfg) + .with_cfg(ctx.cfg.clone()) .with_db(&mut state); // Execute @@ -438,6 +430,7 @@ fn execute_single_test(ctx: TestExecutionContext) -> Result<(), TestErrorKind> { }; *ctx.elapsed.lock().unwrap() += timer.elapsed(); + let exec_result = exec_result; // Check results check_evm_execution( ctx.test, @@ -445,7 +438,7 @@ fn execute_single_test(ctx: TestExecutionContext) -> Result<(), TestErrorKind> { ctx.name, &exec_result, db, - ctx.cfg.spec(), + *ctx.cfg.spec(), ctx.print_json_outcome, ) } @@ -455,7 +448,7 @@ fn debug_failed_test(ctx: DebugContext) { // Re-run with tracing let mut cache = ctx.cache_state.clone(); - cache.set_state_clear_flag(ctx.cfg.spec.is_enabled_in(SpecId::SPURIOUS_DRAGON)); + cache.set_state_clear_flag(ctx.cfg.spec().is_enabled_in(SpecId::SPURIOUS_DRAGON)); let mut state = database::State::builder() .with_cached_prestate(cache) .with_bundle_update() @@ -465,7 +458,7 @@ fn debug_failed_test(ctx: DebugContext) { .with_db(&mut state) .with_block(ctx.block) .with_tx(ctx.tx) - .with_cfg(ctx.cfg) + .with_cfg(ctx.cfg.clone()) .build_mainnet_with_inspector(TracerEip3155::buffered(stderr()).without_summary()); let exec_result = evm.inspect_tx_commit(ctx.tx); @@ -477,7 +470,7 @@ fn debug_failed_test(ctx: DebugContext) { "\nState after:\n{}", evm.ctx.journaled_state.database.cache.pretty_print() ); - println!("\nSpecification: {:?}", ctx.cfg.spec); + println!("\nSpecification: {:?}", ctx.cfg.spec()); println!("\nTx: {:#?}", ctx.tx); println!("Block: {:#?}", ctx.block); println!("Cfg: {:#?}", ctx.cfg); diff --git a/bins/revme/src/dir_utils.rs b/bins/revme/src/dir_utils.rs index 09406cdd93..5cc2c40a49 100644 --- a/bins/revme/src/dir_utils.rs +++ b/bins/revme/src/dir_utils.rs @@ -1,16 +1,18 @@ use std::path::{Path, PathBuf}; use walkdir::{DirEntry, WalkDir}; +/// Find all JSON test files in the given path. +/// If path is a file, returns it in a vector. +/// If path is a directory, recursively finds all .json files. pub fn find_all_json_tests(path: &Path) -> Vec { - WalkDir::new(path) - .into_iter() - .filter_map(|e| e.ok()) - .filter(|e| { - e.path() - .extension() - .map(|ext| ext == "json") - .unwrap_or(false) - }) - .map(DirEntry::into_path) - .collect::>() + if path.is_file() { + vec![path.to_path_buf()] + } else { + WalkDir::new(path) + .into_iter() + .filter_map(Result::ok) + .filter(|e| e.path().extension() == Some("json".as_ref())) + .map(DirEntry::into_path) + .collect() + } } diff --git a/book/src/architecture.md b/book/src/architecture.md index 9832b1f3cf..52cb073856 100644 --- a/book/src/architecture.md +++ b/book/src/architecture.md @@ -22,7 +22,7 @@ The API is designed around four key execution patterns: - **Execution with commit**: Run transactions and automatically persist state changes - **Execution with inspection**: Run transactions with detailed tracing and observation -[`Evm`](https://docs.rs/revm-context/1.0.0/revm_context/evm/struct.Evm.html) the main structure for executing mainnet ethereum transaction is built with a [`Context`](https://docs.rs/revm-context/latest/revm_context/context/struct.Context.html) and a builder, code for it looks like this: +[`Evm`](https://docs.rs/revm-context/1.0.0/revm_context/evm/struct.Evm.html) is the main structure for executing mainnet ethereum transactions. It is built with a [`Context`](https://docs.rs/revm-context/latest/revm_context/context/struct.Context.html) and a builder, code for it looks like this: ```rust,ignore let mut evm = Context::mainnet().with_block(block).build_mainnet(); @@ -42,10 +42,10 @@ And [`Context`](https://docs.rs/revm-context/latest/revm_context/context/struct. REVM provides four ways to execute transactions through traits (API): -* `transact(tx)` and `replay()` are function of [`ExecuteEvm`](https://docs.rs/revm-handler/latest/revm_handler/api/trait.ExecuteEvm.html) trait that allow execution transactions. They return the status of execution with reason, changed state and in case of failed execution an error. +* `transact(tx)` and `replay()` are functions of [`ExecuteEvm`](https://docs.rs/revm-handler/latest/revm_handler/api/trait.ExecuteEvm.html) trait that allow execution transactions. They return the status of execution with reason, changed state and in case of failed execution an error. * `transact_commit(tx)` and `replay_commit()` are part of [`ExecuteCommitEvm`](https://docs.rs/revm-handler/latest/revm_handler/api/trait.ExecuteCommitEvm.html) that internally commits the state diff to the database and returns status of execution. Database is required to support `DatabaseCommit` trait. * `inspect()`, `inspect_replay(tx)` and a few others are part of [`InspectEvm`](https://docs.rs/revm-inspector/latest/revm_inspector/trait.InspectEvm.html) trait that allow execution with inspection. This is how tracers are called. -* `inspect_commit()`,`inspect_replay_commit(tx)` are part of the [`InspectCommitEvm`](https://docs.rs/revm-inspector/latest/revm_inspector/trait.InspectCommitEvm.html) trait that extends `InspectEvm` to allow committing state diff after tracing. +* `inspect_commit()`, `inspect_replay_commit(tx)` are part of the [`InspectCommitEvm`](https://docs.rs/revm-inspector/latest/revm_inspector/trait.InspectCommitEvm.html) trait that extends `InspectEvm` to allow committing state diff after tracing. For inspection API to be enabled, [`Evm`](https://docs.rs/revm-context/1.0.0/revm_context/evm/struct.Evm.html) needs to be created with inspector. diff --git a/book/src/awesome.md b/book/src/awesome.md index 51f759a196..e90d609778 100644 --- a/book/src/awesome.md +++ b/book/src/awesome.md @@ -19,7 +19,7 @@ A curated list of excellent Revm-related resources. Feel free to contribute to t - [**Trin**](https://github.com/ethereum/trin) is a Rust implementation of a Portal Network client. #### EVM Variants -- [**Optimism**](https://github.com/bluealloy/revm/tree/main/crates/op-revm) is a ethereum L2 network. +- [**Optimism**](https://github.com/bluealloy/revm/tree/main/crates/op-revm) is an ethereum L2 network. - [**Base**](https://www.base.org/) is an Ethereum Layer 2 (L2) chain that offers a safe, low-cost, developer-friendly way to build on-chain - [**Scroll**](https://github.com/scroll-tech/revm) is its own Layer 2 network built on Ethereum (more specifically, a “zero-knowledge rollup”). diff --git a/book/src/contact.md b/book/src/contact.md index bced995c04..ed2072dd7f 100644 --- a/book/src/contact.md +++ b/book/src/contact.md @@ -5,13 +5,12 @@ The git repository can be found at [https://github.com/bluealloy/revm/](https:// For questions please open a github issue or join the public [telegram group](https://t.me/+Ig4WDWOzikA3MzA0) -### Licence +### License -Licensed under MIT Licence. +Licensed under MIT License. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in these crates by you, shall be licensed as above, without any additional terms or conditions. ### Security If there is a security question or finding please contact me directly via email at `dragan0rakita@gmail.com` or on keybase `@draganrakita` - diff --git a/book/src/inspector.md b/book/src/inspector.md index 8388132e0f..dac32f92eb 100644 --- a/book/src/inspector.md +++ b/book/src/inspector.md @@ -27,7 +27,10 @@ pub trait Inspector { fn create_end(&mut self, context: &mut CTX, inputs: &CreateInputs, outcome: &mut CreateOutcome) {} // Event tracing - fn log(&mut self, interp: &mut Interpreter, context: &mut CTX, log: Log) {} + fn log(&mut self, context: &mut CTX, log: Log) {} + fn log_full(&mut self, interp: &mut Interpreter, context: &mut CTX, log: Log) { + self.log(context, log) + } fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {} } ``` @@ -135,4 +138,4 @@ REVM provides several ready-to-use inspectors: - **Development tools**: Contract interaction tracing - **Testing frameworks**: Execution verification and state checking -The Inspector trait makes REVM very observable EVM implementations available, enabling sophisticated tooling and analysis capabilities. \ No newline at end of file +The Inspector trait makes REVM one of the most observable EVM implementations available, enabling sophisticated tooling and analysis capabilities. diff --git a/book/src/revme.md b/book/src/revme.md index bc0818d58b..d82bf2bf33 100644 --- a/book/src/revme.md +++ b/book/src/revme.md @@ -1,6 +1,6 @@ # Revme -Is a binary that allows running statetest and eof validation. +Revme is a binary that allows running statetest and eof validation. ```bash, ignore $: revme --help @@ -27,7 +27,7 @@ Test suites for the latest hardforks can be found in [EEST releases](https://git Revm can run statetest type of tests with `revme` using the following command: `cargo run --release -p revme -- statetest folder_path` -For running EEST tests, we can use the `./scripts/run-tests.sh.` +For running EEST tests, we can use the `./scripts/run-tests.sh`. For legacy tests, we need to first download the repo `git clone https://github.com/ethereum/legacytests` and then run it with `cargo run --release -p revme -- statetest legacytests/Cancun/GeneralStateTests` All statetest that can be run by revme can be found in the `GeneralStateTests` folder. diff --git a/crates/README.md b/crates/README.md index 8bb52201fc..88773fdb42 100644 --- a/crates/README.md +++ b/crates/README.md @@ -1,5 +1,5 @@ Crates version and their description: -* ![revm](https://img.shields.io/crates/v/revm?height=50?label=revm) main crate, it reexports all other crates. +* ![revm](https://img.shields.io/crates/v/revm?height=50?label=revm) main crate. It reexports all other crates. * ![revm-primitives](https://img.shields.io/crates/v/revm-primitives?label=revm-primitives) contains constants and primitives types that revm uses (alloy-primitives) * ![revm-interpreter](https://img.shields.io/crates/v/revm-interpreter?label=revm-interpreter) biggest crate in the project, it contains all instructions * ![revm-precompile](https://img.shields.io/crates/v/revm-precompile?label=revm-precompile) Precompiles defined by ethereum diff --git a/crates/bytecode/CHANGELOG.md b/crates/bytecode/CHANGELOG.md index 0803202fb3..ccac682b66 100644 --- a/crates/bytecode/CHANGELOG.md +++ b/crates/bytecode/CHANGELOG.md @@ -7,6 +7,41 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [8.0.0](https://github.com/bluealloy/revm/compare/revm-bytecode-v7.1.1...revm-bytecode-v8.0.0) - 2026-01-15 + +### Added + +- BAL EIP-7928 ([#3070](https://github.com/bluealloy/revm/pull/3070)) + +### Fixed + +- *(bytecode)* remove duplicate implementation in bytes_ref method ([#3276](https://github.com/bluealloy/revm/pull/3276)) +- the MLOAD opcode can resize memory ([#3266](https://github.com/bluealloy/revm/pull/3266)) + +### Other + +- fix typos, grammar errors, and improve documentation consistency ([#3294](https://github.com/bluealloy/revm/pull/3294)) +- happy new year, 2026 licence ([#3272](https://github.com/bluealloy/revm/pull/3272)) +- the KECCAK256 opcode can resize memory ([#3269](https://github.com/bluealloy/revm/pull/3269)) + +## [7.1.1](https://github.com/bluealloy/revm/compare/revm-bytecode-v7.1.0...revm-bytecode-v7.1.1) - 2025-11-07 + +### Other + +- updated the following local packages: revm-primitives + +## [7.1.0](https://github.com/bluealloy/revm/compare/revm-bytecode-v7.0.2...revm-bytecode-v7.1.0) - 2025-10-30 + +### Added + +- impl `Sealable` for `Bytecode` ([#3118](https://github.com/bluealloy/revm/pull/3118)) + +## [7.0.2](https://github.com/bluealloy/revm/compare/revm-bytecode-v7.0.1...revm-bytecode-v7.0.2) - 2025-10-15 + +### Other + +- use JumpTable old serde format ([#3101](https://github.com/bluealloy/revm/pull/3101)) + ## [7.0.1](https://github.com/bluealloy/revm/compare/revm-bytecode-v7.0.0...revm-bytecode-v7.0.1) - 2025-10-15 ### Fixed diff --git a/crates/bytecode/Cargo.toml b/crates/bytecode/Cargo.toml index e568840731..6f2e1d7955 100644 --- a/crates/bytecode/Cargo.toml +++ b/crates/bytecode/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "revm-bytecode" description = "EVM Bytecodes" -version = "7.0.1" +version = "8.0.0" authors.workspace = true edition.workspace = true keywords.workspace = true diff --git a/crates/bytecode/LICENSE b/crates/bytecode/LICENSE index be6d350ebe..7edb0405ff 100644 --- a/crates/bytecode/LICENSE +++ b/crates/bytecode/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021-2025 draganrakita +Copyright (c) 2021-2026 draganrakita Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/crates/bytecode/src/bytecode.rs b/crates/bytecode/src/bytecode.rs index a6f09b7aa5..259c3c1fa8 100644 --- a/crates/bytecode/src/bytecode.rs +++ b/crates/bytecode/src/bytecode.rs @@ -8,16 +8,19 @@ use crate::{ eip7702::{Eip7702Bytecode, EIP7702_MAGIC_BYTES}, BytecodeDecodeError, JumpTable, LegacyAnalyzedBytecode, LegacyRawBytecode, }; -use primitives::{keccak256, Address, Bytes, B256, KECCAK_EMPTY}; +use primitives::{ + alloy_primitives::Sealable, keccak256, Address, Bytes, OnceLock, B256, KECCAK_EMPTY, +}; +use std::sync::Arc; /// Main bytecode structure with all variants. #[derive(Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum Bytecode { /// EIP-7702 delegated bytecode - Eip7702(Eip7702Bytecode), + Eip7702(Arc), /// The bytecode has been analyzed for valid jump destinations. - LegacyAnalyzed(LegacyAnalyzedBytecode), + LegacyAnalyzed(Arc), } impl Default for Bytecode { @@ -27,11 +30,21 @@ impl Default for Bytecode { } } +impl Sealable for Bytecode { + #[inline] + fn hash_slow(&self) -> B256 { + self.hash_slow() + } +} + impl Bytecode { /// Creates a new legacy analyzed [`Bytecode`] with exactly one STOP opcode. #[inline] pub fn new() -> Self { - Self::LegacyAnalyzed(LegacyAnalyzedBytecode::default()) + static DEFAULT_BYTECODE: OnceLock = OnceLock::new(); + DEFAULT_BYTECODE + .get_or_init(|| Self::LegacyAnalyzed(Arc::new(LegacyAnalyzedBytecode::default()))) + .clone() } /// Returns jump table if bytecode is analyzed. @@ -62,7 +75,7 @@ impl Bytecode { /// Creates a new legacy [`Bytecode`]. #[inline] pub fn new_legacy(raw: Bytes) -> Self { - Self::LegacyAnalyzed(LegacyRawBytecode(raw).into_analyzed()) + Self::LegacyAnalyzed(Arc::new(LegacyRawBytecode(raw).into_analyzed())) } /// Creates a new raw [`Bytecode`]. @@ -78,7 +91,7 @@ impl Bytecode { /// Creates a new EIP-7702 [`Bytecode`] from [`Address`]. #[inline] pub fn new_eip7702(address: Address) -> Self { - Self::Eip7702(Eip7702Bytecode::new(address)) + Self::Eip7702(Arc::new(Eip7702Bytecode::new(address))) } /// Creates a new raw [`Bytecode`]. @@ -90,7 +103,7 @@ impl Bytecode { match prefix { Some(prefix) if prefix == &EIP7702_MAGIC_BYTES => { let eip7702 = Eip7702Bytecode::new_raw(bytes)?; - Ok(Self::Eip7702(eip7702)) + Ok(Self::Eip7702(Arc::new(eip7702))) } _ => Ok(Self::new_legacy(bytes)), } @@ -103,11 +116,11 @@ impl Bytecode { /// For possible panics see [`LegacyAnalyzedBytecode::new`]. #[inline] pub fn new_analyzed(bytecode: Bytes, original_len: usize, jump_table: JumpTable) -> Self { - Self::LegacyAnalyzed(LegacyAnalyzedBytecode::new( + Self::LegacyAnalyzed(Arc::new(LegacyAnalyzedBytecode::new( bytecode, original_len, jump_table, - )) + ))) } /// Returns a reference to the bytecode. @@ -134,10 +147,7 @@ impl Bytecode { /// Returns raw bytes reference. #[inline] pub fn bytes_ref(&self) -> &Bytes { - match self { - Self::LegacyAnalyzed(analyzed) => analyzed.bytecode(), - Self::Eip7702(code) => code.raw(), - } + self.bytecode() } /// Returns raw bytes slice. diff --git a/crates/bytecode/src/iter.rs b/crates/bytecode/src/iter.rs index 7abc25cc0f..e8f7275f87 100644 --- a/crates/bytecode/src/iter.rs +++ b/crates/bytecode/src/iter.rs @@ -119,7 +119,7 @@ mod tests { opcode::STOP, ]; let raw_bytecode = LegacyRawBytecode(Bytes::from(bytecode_data)); - let bytecode = Bytecode::LegacyAnalyzed(raw_bytecode.into_analyzed()); + let bytecode = Bytecode::LegacyAnalyzed(raw_bytecode.into_analyzed_arc()); let opcodes: Vec = bytecode.iter_opcodes().collect(); // We should only see the opcodes, not the immediates assert_eq!( @@ -144,7 +144,7 @@ mod tests { opcode::STOP, ]; let raw_bytecode = LegacyRawBytecode(Bytes::from(bytecode_data)); - let bytecode = Bytecode::LegacyAnalyzed(raw_bytecode.into_analyzed()); + let bytecode = Bytecode::LegacyAnalyzed(raw_bytecode.into_analyzed_arc()); let opcodes: Vec = bytecode.iter_opcodes().collect(); @@ -207,7 +207,7 @@ mod tests { ]; let raw_bytecode = LegacyRawBytecode(Bytes::from(bytecode_data)); - let bytecode = Bytecode::LegacyAnalyzed(raw_bytecode.into_analyzed()); + let bytecode = Bytecode::LegacyAnalyzed(raw_bytecode.into_analyzed_arc()); // Use the iterator directly let iter = BytecodeIterator::new(&bytecode); @@ -255,7 +255,7 @@ mod tests { opcode::STOP, ]; let raw_bytecode = LegacyRawBytecode(Bytes::from(bytecode_data)); - let bytecode = Bytecode::LegacyAnalyzed(raw_bytecode.into_analyzed()); + let bytecode = Bytecode::LegacyAnalyzed(raw_bytecode.into_analyzed_arc()); let mut iter = bytecode.iter_opcodes(); @@ -287,7 +287,7 @@ mod tests { // Empty bytecode (just STOP) let bytecode_data = vec![opcode::STOP]; let raw_bytecode = LegacyRawBytecode(Bytes::from(bytecode_data)); - let bytecode = Bytecode::LegacyAnalyzed(raw_bytecode.into_analyzed()); + let bytecode = Bytecode::LegacyAnalyzed(raw_bytecode.into_analyzed_arc()); let opcodes: Vec = bytecode.iter_opcodes().collect(); assert_eq!(opcodes, vec![opcode::STOP]); diff --git a/crates/bytecode/src/legacy/jump_map.rs b/crates/bytecode/src/legacy/jump_map.rs index 562f53e562..1347b44778 100644 --- a/crates/bytecode/src/legacy/jump_map.rs +++ b/crates/bytecode/src/legacy/jump_map.rs @@ -10,10 +10,8 @@ use std::{fmt::Debug, sync::Arc}; /// /// It is immutable, cheap to clone and memory efficient, with one bit per byte in the bytecode. #[derive(Clone, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize))] pub struct JumpTable { /// Cached pointer to table data to avoid Arc overhead on lookup - #[cfg_attr(feature = "serde", serde(skip))] table_ptr: *const u8, /// Number of bits in the table. len: usize, @@ -65,35 +63,26 @@ impl Default for JumpTable { } } +#[cfg(feature = "serde")] +impl serde::Serialize for JumpTable { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut bitvec = BitVec::::from_vec(self.table.to_vec()); + bitvec.resize(self.len, false); + bitvec.serialize(serializer) + } +} + #[cfg(feature = "serde")] impl<'de> serde::Deserialize<'de> for JumpTable { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { - #[derive(serde::Deserialize)] - - struct JumpTableSerde { - #[serde(alias = "bits")] - len: usize, - #[serde(alias = "data")] - table: Table, - } - - #[derive(serde::Deserialize, Debug)] - #[serde(untagged)] - enum Table { - Data(std::vec::Vec), - Table(Bytes), - } - - let data = JumpTableSerde::deserialize(deserializer)?; - let table = match data.table { - Table::Table(table) => table, - Table::Data(data) => Bytes::from(data), - }; - - Ok(Self::from_bytes_arc(Arc::new(table), data.len)) + let bitvec = BitVec::deserialize(deserializer)?; + Ok(Self::new(bitvec)) } } @@ -235,21 +224,12 @@ mod tests { "data": [5] }"#; - let new_format = r#" - { - "len": 4, - "table": "0x05" - }"#; - let table: JumpTable = serde_json::from_str(legacy_format).expect("Failed to deserialize"); assert_eq!(table.len, 4); assert!(table.is_valid(0)); assert!(!table.is_valid(1)); assert!(table.is_valid(2)); assert!(!table.is_valid(3)); - - let new_table: JumpTable = serde_json::from_str(new_format).expect("Failed to deserialize"); - assert_eq!(new_table, table); } #[test] diff --git a/crates/bytecode/src/legacy/raw.rs b/crates/bytecode/src/legacy/raw.rs index 20ef7ba885..00868430a2 100644 --- a/crates/bytecode/src/legacy/raw.rs +++ b/crates/bytecode/src/legacy/raw.rs @@ -1,6 +1,7 @@ use super::LegacyAnalyzedBytecode; use core::ops::Deref; use primitives::Bytes; +use std::sync::Arc; /// Used only as intermediate representation for legacy bytecode. /// @@ -14,6 +15,11 @@ impl LegacyRawBytecode { pub fn into_analyzed(self) -> LegacyAnalyzedBytecode { LegacyAnalyzedBytecode::analyze(self.0) } + + /// Analyzes the bytecode, instantiating a [`LegacyAnalyzedBytecode`] and wrapping it in an [`Arc`]. + pub fn into_analyzed_arc(self) -> Arc { + Arc::new(self.into_analyzed()) + } } impl From for LegacyRawBytecode { diff --git a/crates/bytecode/src/opcode.rs b/crates/bytecode/src/opcode.rs index 4f1d4247f7..95db0ad2d2 100644 --- a/crates/bytecode/src/opcode.rs +++ b/crates/bytecode/src/opcode.rs @@ -182,9 +182,11 @@ impl OpCode { matches!( *self, OpCode::EXTCODECOPY + | OpCode::MLOAD | OpCode::MSTORE | OpCode::MSTORE8 | OpCode::MCOPY + | OpCode::KECCAK256 | OpCode::CODECOPY | OpCode::CALLDATACOPY | OpCode::RETURNDATACOPY @@ -768,8 +770,9 @@ mod tests { #[test] fn test_modifies_memory() { - assert!(!OpCode::new(MLOAD).unwrap().modifies_memory()); + assert!(OpCode::new(MLOAD).unwrap().modifies_memory()); assert!(OpCode::new(MSTORE).unwrap().modifies_memory()); + assert!(OpCode::new(KECCAK256).unwrap().modifies_memory()); assert!(!OpCode::new(ADD).unwrap().modifies_memory()); } } diff --git a/crates/context/CHANGELOG.md b/crates/context/CHANGELOG.md index 59e0cebe47..c279c0b142 100644 --- a/crates/context/CHANGELOG.md +++ b/crates/context/CHANGELOG.md @@ -7,6 +7,91 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [13.0.0](https://github.com/bluealloy/revm/compare/revm-context-v12.1.0...revm-context-v13.0.0) - 2026-01-15 + +### Added + +- new gas params, tx initial gas and codedeposit ([#3260](https://github.com/bluealloy/revm/pull/3260)) +- move GasParams to Cfg ([#3229](https://github.com/bluealloy/revm/pull/3229)) +- Propagate `map-foldhash` Feature Through Dependency Chain ([#3252](https://github.com/bluealloy/revm/pull/3252)) +- BAL EIP-7928 ([#3070](https://github.com/bluealloy/revm/pull/3070)) +- Gas params ([#3132](https://github.com/bluealloy/revm/pull/3132)) +- JournaledAccount sload/sstore ([#3201](https://github.com/bluealloy/revm/pull/3201)) +- Restrict Database::Error. JournaledAccountTr ([#3199](https://github.com/bluealloy/revm/pull/3199)) + +### Fixed + +- fix API comment ([#3293](https://github.com/bluealloy/revm/pull/3293)) +- *(test)* one gasid name is missing ([#3290](https://github.com/bluealloy/revm/pull/3290)) +- set transaction_id on new account ([#3204](https://github.com/bluealloy/revm/pull/3204)) + +### Other + +- *(gas_params)* add dedicated GasIds for sstore_refund ([#3310](https://github.com/bluealloy/revm/pull/3310)) +- remove redundant clones in gas params defaults ([#3300](https://github.com/bluealloy/revm/pull/3300)) +- fix typos, grammar errors, and improve documentation consistency ([#3294](https://github.com/bluealloy/revm/pull/3294)) +- happy new year, 2026 licence ([#3272](https://github.com/bluealloy/revm/pull/3272)) +- add Display trait for ExecutionResult and related types ([#3267](https://github.com/bluealloy/revm/pull/3267)) +- add Display for HaltReason and OutOfGasError ([#3265](https://github.com/bluealloy/revm/pull/3265)) +- *(cleanup)* remove unused duplicate ContextSetters trait in context crate ([#3225](https://github.com/bluealloy/revm/pull/3225)) +- *(fmt)* merge all imports ([#3184](https://github.com/bluealloy/revm/pull/3184)) + +## [12.1.0](https://github.com/bluealloy/revm/compare/revm-context-v12.0.0...revm-context-v12.1.0) - 2025-11-14 + +### Added + +- Add set_nonce journal entry and fn ([#3163](https://github.com/bluealloy/revm/pull/3163)) + +## [12.0.0](https://github.com/bluealloy/revm/compare/revm-context-v11.0.2...revm-context-v12.0.0) - 2025-11-10 + +### Added + +- generic Context::new ([#3156](https://github.com/bluealloy/revm/pull/3156)) +- process precompile logs to inspector ([#3148](https://github.com/bluealloy/revm/pull/3148)) +- selfdestruct oog on cold load ([#3140](https://github.com/bluealloy/revm/pull/3140)) + +### Fixed + +- use access list to decide if slot is cold ([#3149](https://github.com/bluealloy/revm/pull/3149)) + +### Other + +- merge v98 versions bumps ([#3155](https://github.com/bluealloy/revm/pull/3155)) + +## [11.0.2](https://github.com/bluealloy/revm/compare/revm-context-v11.0.1...revm-context-v11.0.2) - 2025-11-10 + +### Other + +- updated the following local packages: revm-database + +## [11.0.1](https://github.com/bluealloy/revm/compare/revm-context-v11.0.0...revm-context-v11.0.1) - 2025-11-07 + +### Other + +- add test + +## [11.0.0](https://github.com/bluealloy/revm/compare/revm-context-v10.1.2...revm-context-v11.0.0) - 2025-10-30 + +### Added + +- JournaledAccount, a nice way to update and track changes ([#3086](https://github.com/bluealloy/revm/pull/3086)) +- dont load access list immediately ([#3116](https://github.com/bluealloy/revm/pull/3116)) + +### Fixed + +- *(context)* avoid double reference in `Context::all()` ([#3131](https://github.com/bluealloy/revm/pull/3131)) +- hook up Cfg::memory_limit ([#3129](https://github.com/bluealloy/revm/pull/3129)) + +### Other + +- journal transfer fn cleanup ([#3085](https://github.com/bluealloy/revm/pull/3085)) + +## [10.1.2](https://github.com/bluealloy/revm/compare/revm-context-v10.1.1...revm-context-v10.1.2) - 2025-10-15 + +### Other + +- updated the following local packages: revm-bytecode, revm-state, revm-database-interface, revm-context-interface + ## [10.1.1](https://github.com/bluealloy/revm/compare/revm-context-v10.1.0...revm-context-v10.1.1) - 2025-10-15 ### Other diff --git a/crates/context/Cargo.toml b/crates/context/Cargo.toml index 4838714b4e..71aeb2428d 100644 --- a/crates/context/Cargo.toml +++ b/crates/context/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "revm-context" description = "Revm context crates" -version = "10.1.1" +version = "13.0.0" authors.workspace = true edition.workspace = true keywords.workspace = true @@ -82,3 +82,4 @@ optional_fee_charge = [] enable_eip7702 = [] enable_eip7623 = [] require_l1_data_fee_buffer = [] +map-foldhash = ["primitives/map-foldhash", "state/map-foldhash"] diff --git a/crates/context/LICENSE b/crates/context/LICENSE index be6d350ebe..7edb0405ff 100644 --- a/crates/context/LICENSE +++ b/crates/context/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021-2025 draganrakita +Copyright (c) 2021-2026 draganrakita Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/crates/context/interface/CHANGELOG.md b/crates/context/interface/CHANGELOG.md index 93ee11452e..d059eeeded 100644 --- a/crates/context/interface/CHANGELOG.md +++ b/crates/context/interface/CHANGELOG.md @@ -7,6 +7,71 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [14.0.0](https://github.com/bluealloy/revm/compare/revm-context-interface-v13.1.0...revm-context-interface-v14.0.0) - 2026-01-15 + +### Added + +- new gas params, tx initial gas and codedeposit ([#3260](https://github.com/bluealloy/revm/pull/3260)) +- move GasParams to Cfg ([#3229](https://github.com/bluealloy/revm/pull/3229)) +- Gas params ([#3132](https://github.com/bluealloy/revm/pull/3132)) +- JournaledAccount sload/sstore ([#3201](https://github.com/bluealloy/revm/pull/3201)) +- Restrict Database::Error. JournaledAccountTr ([#3199](https://github.com/bluealloy/revm/pull/3199)) + +### Fixed + +- *(test)* one gasid name is missing ([#3290](https://github.com/bluealloy/revm/pull/3290)) + +### Other + +- *(gas_params)* add dedicated GasIds for sstore_refund ([#3310](https://github.com/bluealloy/revm/pull/3310)) +- remove redundant clones in gas params defaults ([#3300](https://github.com/bluealloy/revm/pull/3300)) +- fix typos, grammar errors, and improve documentation consistency ([#3294](https://github.com/bluealloy/revm/pull/3294)) +- happy new year, 2026 licence ([#3272](https://github.com/bluealloy/revm/pull/3272)) +- add Display trait for ExecutionResult and related types ([#3267](https://github.com/bluealloy/revm/pull/3267)) +- add Display for HaltReason and OutOfGasError ([#3265](https://github.com/bluealloy/revm/pull/3265)) +- *(fmt)* merge all imports ([#3184](https://github.com/bluealloy/revm/pull/3184)) + +## [13.1.0](https://github.com/bluealloy/revm/compare/revm-context-interface-v13.0.0...revm-context-interface-v13.1.0) - 2025-11-14 + +### Added + +- Add set_nonce journal entry and fn ([#3163](https://github.com/bluealloy/revm/pull/3163)) +- *(context)* add mark_cold method to JournaledAccount ([#3160](https://github.com/bluealloy/revm/pull/3160)) + +## [13.0.0](https://github.com/bluealloy/revm/compare/revm-context-interface-v12.0.1...revm-context-interface-v13.0.0) - 2025-11-10 + +### Added + +- process precompile logs to inspector ([#3148](https://github.com/bluealloy/revm/pull/3148)) +- selfdestruct oog on cold load ([#3140](https://github.com/bluealloy/revm/pull/3140)) + +### Other + +- merge v98 versions bumps ([#3155](https://github.com/bluealloy/revm/pull/3155)) + +## [12.0.1](https://github.com/bluealloy/revm/compare/revm-context-interface-v12.0.0...revm-context-interface-v12.0.1) - 2025-11-07 + +### Other + +- updated the following local packages: revm-primitives, revm-state, revm-database-interface + +## [12.0.0](https://github.com/bluealloy/revm/compare/revm-context-interface-v11.1.2...revm-context-interface-v12.0.0) - 2025-10-30 + +### Added + +- JournaledAccount, a nice way to update and track changes ([#3086](https://github.com/bluealloy/revm/pull/3086)) +- dont load access list immediately ([#3116](https://github.com/bluealloy/revm/pull/3116)) + +### Fixed + +- hook up Cfg::memory_limit ([#3129](https://github.com/bluealloy/revm/pull/3129)) + +## [11.1.2](https://github.com/bluealloy/revm/compare/revm-context-interface-v11.1.1...revm-context-interface-v11.1.2) - 2025-10-15 + +### Other + +- updated the following local packages: revm-state, revm-database-interface + ## [11.1.1](https://github.com/bluealloy/revm/compare/revm-context-interface-v11.1.0...revm-context-interface-v11.1.1) - 2025-10-15 ### Other diff --git a/crates/context/interface/Cargo.toml b/crates/context/interface/Cargo.toml index f381b407c8..2a71aba3c4 100644 --- a/crates/context/interface/Cargo.toml +++ b/crates/context/interface/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "revm-context-interface" description = "Revm context interface crates" -version = "11.1.1" +version = "14.0.0" authors.workspace = true edition.workspace = true keywords.workspace = true diff --git a/crates/context/interface/LICENSE b/crates/context/interface/LICENSE index be6d350ebe..7edb0405ff 100644 --- a/crates/context/interface/LICENSE +++ b/crates/context/interface/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021-2025 draganrakita +Copyright (c) 2021-2026 draganrakita Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/crates/context/interface/src/cfg.rs b/crates/context/interface/src/cfg.rs index a0c255be9c..dbb6d733d7 100644 --- a/crates/context/interface/src/cfg.rs +++ b/crates/context/interface/src/cfg.rs @@ -1,7 +1,12 @@ //! Configuration for the EVM. Containing [`SpecId`]. + +pub mod gas; +pub mod gas_params; + +pub use gas_params::{GasId, GasParams}; + use auto_impl::auto_impl; -use core::fmt::Debug; -use core::hash::Hash; +use core::{fmt::Debug, hash::Hash}; use primitives::{hardfork::SpecId, Address, TxKind, U256}; /// Configuration for the EVM. @@ -66,14 +71,18 @@ pub trait Cfg { fn is_fee_charge_disabled(&self) -> bool; /// Returns whether the EIP-7702 is enabled. + /// Used for: (1) allowing EIP-7702 tx type before Prague, (2) delegation loading. fn is_eip7702_enabled(&self) -> bool; - /// Returns whether the EIP-7623 is enabled. - fn is_eip7623_enabled(&self) -> bool; - /// Returns whether the L1 data fee buffer check is required. /// When enabled, validates balance >= L2_fee + 2 * L1_fee but only charges L2_fee + L1_fee. fn is_l1_data_fee_buffer_required(&self) -> bool; + + /// Returns the limit in bytes for the memory buffer. + fn memory_limit(&self) -> u64; + + /// Returns the gas params for the EVM. + fn gas_params(&self) -> &GasParams; } /// What bytecode analysis to perform diff --git a/crates/interpreter/src/gas/constants.rs b/crates/context/interface/src/cfg/gas.rs similarity index 57% rename from crates/interpreter/src/gas/constants.rs rename to crates/context/interface/src/cfg/gas.rs index 1739e8ca08..fc3762f299 100644 --- a/crates/interpreter/src/gas/constants.rs +++ b/crates/context/interface/src/cfg/gas.rs @@ -1,3 +1,8 @@ +//! Gas constants and functions for gas calculation. + +use crate::{cfg::GasParams, transaction::AccessListItemTr as _, Transaction, TransactionType}; +use primitives::hardfork::SpecId; + /// Gas cost for operations that consume zero gas. pub const ZERO: u64 = 0; /// Base gas cost for basic operations. @@ -83,6 +88,7 @@ pub const EOF_CREATE_GAS: u64 = 32000; pub const ACCESS_LIST_ADDRESS: u64 = 2400; /// Gas cost for accessing a storage key in the access list (EIP-2930). pub const ACCESS_LIST_STORAGE_KEY: u64 = 1900; + /// Gas cost for SLOAD when accessing a cold storage slot (EIP-2929). pub const COLD_SLOAD_COST: u64 = 2100; /// Gas cost for accessing a cold account (EIP-2929). @@ -90,8 +96,6 @@ pub const COLD_ACCOUNT_ACCESS_COST: u64 = 2600; /// Additional gas cost for accessing a cold account. pub const COLD_ACCOUNT_ACCESS_COST_ADDITIONAL: u64 = COLD_ACCOUNT_ACCESS_COST - WARM_STORAGE_READ_COST; -/// Additional gas cost for accessing a cold storage -pub const COLD_SLOAD_COST_ADDITIONAL: u64 = COLD_SLOAD_COST - WARM_STORAGE_READ_COST; /// Gas cost for reading from a warm storage slot (EIP-2929). pub const WARM_STORAGE_READ_COST: u64 = 100; /// Gas cost for SSTORE reset operation on a warm storage slot. @@ -102,5 +106,98 @@ pub const INITCODE_WORD_COST: u64 = 2; /// Gas stipend provided to the recipient of a CALL with value transfer. pub const CALL_STIPEND: u64 = 2300; -/// Minimum gas that must be provided to a callee. -pub const MIN_CALLEE_GAS: u64 = CALL_STIPEND; + +/// Init and floor gas from transaction +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct InitialAndFloorGas { + /// Initial gas for transaction. + pub initial_gas: u64, + /// If transaction is a Call and Prague is enabled + /// floor_gas is at least amount of gas that is going to be spent. + pub floor_gas: u64, +} + +impl InitialAndFloorGas { + /// Create a new InitialAndFloorGas instance. + #[inline] + pub const fn new(initial_gas: u64, floor_gas: u64) -> Self { + Self { + initial_gas, + floor_gas, + } + } +} + +/// Initial gas that is deducted for transaction to be included. +/// Initial gas contains initial stipend gas, gas for access list and input data. +/// +/// # Returns +/// +/// - Intrinsic gas +/// - Number of tokens in calldata +pub fn calculate_initial_tx_gas( + spec_id: SpecId, + input: &[u8], + is_create: bool, + access_list_accounts: u64, + access_list_storages: u64, + authorization_list_num: u64, +) -> InitialAndFloorGas { + GasParams::new_spec(spec_id).initial_tx_gas( + input, + is_create, + access_list_accounts, + access_list_storages, + authorization_list_num, + ) +} + +/// Initial gas that is deducted for transaction to be included. +/// Initial gas contains initial stipend gas, gas for access list and input data. +/// +/// # Returns +/// +/// - Intrinsic gas +/// - Number of tokens in calldata +pub fn calculate_initial_tx_gas_for_tx(tx: impl Transaction, spec: SpecId) -> InitialAndFloorGas { + let mut accounts = 0; + let mut storages = 0; + // legacy is only tx type that does not have access list. + if tx.tx_type() != TransactionType::Legacy { + (accounts, storages) = tx + .access_list() + .map(|al| { + al.fold((0, 0), |(mut num_accounts, mut num_storage_slots), item| { + num_accounts += 1; + num_storage_slots += item.storage_slots().count(); + + (num_accounts, num_storage_slots) + }) + }) + .unwrap_or_default(); + } + + calculate_initial_tx_gas( + spec, + tx.input(), + tx.kind().is_create(), + accounts as u64, + storages as u64, + tx.authorization_list_len() as u64, + ) +} + +/// Retrieve the total number of tokens in calldata. +#[inline] +pub fn get_tokens_in_calldata_istanbul(input: &[u8]) -> u64 { + get_tokens_in_calldata(input, NON_ZERO_BYTE_MULTIPLIER_ISTANBUL) +} + +/// Retrieve the total number of tokens in calldata. +#[inline] +pub fn get_tokens_in_calldata(input: &[u8], non_zero_data_multiplier: u64) -> u64 { + let zero_data_len = input.iter().filter(|v| **v == 0).count() as u64; + let non_zero_data_len = input.len() as u64 - zero_data_len; + zero_data_len + non_zero_data_len * non_zero_data_multiplier +} diff --git a/crates/context/interface/src/cfg/gas_params.rs b/crates/context/interface/src/cfg/gas_params.rs new file mode 100644 index 0000000000..a646fc97d5 --- /dev/null +++ b/crates/context/interface/src/cfg/gas_params.rs @@ -0,0 +1,1163 @@ +//! Gas table for dynamic gas constants. + +use crate::{ + cfg::gas::{self, get_tokens_in_calldata, InitialAndFloorGas}, + context::SStoreResult, +}; +use core::hash::{Hash, Hasher}; +use primitives::{ + eip7702, + hardfork::SpecId::{self}, + OnceLock, U256, +}; +use std::sync::Arc; + +/// Gas table for dynamic gas constants. +#[derive(Clone)] +pub struct GasParams { + /// Table of gas costs for operations + table: Arc<[u64; 256]>, + /// Pointer to the table. + ptr: *const u64, +} + +impl PartialEq for GasParams { + fn eq(&self, other: &GasParams) -> bool { + self.table == other.table + } +} + +impl Hash for GasParams { + fn hash(&self, hasher: &mut H) { + self.table.hash(hasher); + } +} + +/// Pointer points to Arc so it is safe to send across threads +unsafe impl Send for GasParams {} +/// Pointer points to Arc so it is safe to access +unsafe impl Sync for GasParams {} + +impl core::fmt::Debug for GasParams { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "GasParams {{ table: {:?} }}", self.table) + } +} + +/// Returns number of words what would fit to provided number of bytes, +/// i.e. it rounds up the number bytes to number of words. +#[inline] +pub const fn num_words(len: usize) -> usize { + len.div_ceil(32) +} + +impl Eq for GasParams {} +#[cfg(feature = "serde")] +mod serde { + use super::{Arc, GasParams}; + use std::vec::Vec; + + #[derive(serde::Serialize, serde::Deserialize)] + struct GasParamsSerde { + table: Vec, + } + + #[cfg(feature = "serde")] + impl serde::Serialize for GasParams { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + GasParamsSerde { + table: self.table.to_vec(), + } + .serialize(serializer) + } + } + + impl<'de> serde::Deserialize<'de> for GasParams { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let table = GasParamsSerde::deserialize(deserializer)?; + if table.table.len() != 256 { + return Err(serde::de::Error::custom("Invalid gas params length")); + } + Ok(Self::new(Arc::new(table.table.try_into().unwrap()))) + } + } +} + +impl Default for GasParams { + fn default() -> Self { + Self::new_spec(SpecId::default()) + } +} + +impl GasParams { + /// Creates a new `GasParams` with the given table. + #[inline] + pub fn new(table: Arc<[u64; 256]>) -> Self { + Self { + ptr: table.as_ptr(), + table, + } + } + + /// Overrides the gas cost for the given gas id. + /// + /// It will clone underlying table and override the values. + /// + /// Use to override default gas cost + /// + /// ```rust + /// use revm_context_interface::cfg::gas_params::{GasParams, GasId}; + /// use primitives::hardfork::SpecId; + /// + /// let mut gas_table = GasParams::new_spec(SpecId::default()); + /// gas_table.override_gas([(GasId::memory_linear_cost(), 2), (GasId::memory_quadratic_reduction(), 512)].into_iter()); + /// assert_eq!(gas_table.get(GasId::memory_linear_cost()), 2); + /// assert_eq!(gas_table.get(GasId::memory_quadratic_reduction()), 512); + /// ``` + pub fn override_gas(&mut self, values: impl IntoIterator) { + let mut table = *self.table.clone(); + for (id, value) in values.into_iter() { + table[id.as_usize()] = value; + } + *self = Self::new(Arc::new(table)); + } + + /// Returns the table. + #[inline] + pub fn table(&self) -> &[u64; 256] { + &self.table + } + + /// Creates a new `GasParams` for the given spec. + #[inline(never)] + pub fn new_spec(spec: SpecId) -> Self { + use SpecId::*; + let gas_params = match spec { + FRONTIER | FRONTIER_THAWING => { + static TABLE: OnceLock = OnceLock::new(); + TABLE.get_or_init(|| Self::new_spec_inner(spec)) + } + // Transaction creation cost was added in homestead fork. + HOMESTEAD | DAO_FORK => { + static TABLE: OnceLock = OnceLock::new(); + TABLE.get_or_init(|| Self::new_spec_inner(spec)) + } + // New account cost for selfdestruct was added in tangerine fork. + TANGERINE => { + static TABLE: OnceLock = OnceLock::new(); + TABLE.get_or_init(|| Self::new_spec_inner(spec)) + } + // EXP cost was increased in spurious dragon fork. + SPURIOUS_DRAGON | BYZANTIUM | CONSTANTINOPLE | PETERSBURG => { + static TABLE: OnceLock = OnceLock::new(); + TABLE.get_or_init(|| Self::new_spec_inner(spec)) + } + // SSTORE gas calculation changed in istanbul fork. + ISTANBUL | MUIR_GLACIER => { + static TABLE: OnceLock = OnceLock::new(); + TABLE.get_or_init(|| Self::new_spec_inner(spec)) + } + // Warm/cold state access + BERLIN => { + static TABLE: OnceLock = OnceLock::new(); + TABLE.get_or_init(|| Self::new_spec_inner(spec)) + } + // Refund reduction in london fork. + LONDON | ARROW_GLACIER | GRAY_GLACIER | MERGE => { + static TABLE: OnceLock = OnceLock::new(); + TABLE.get_or_init(|| Self::new_spec_inner(spec)) + } + // Transaction initcode cost was introduced in shanghai fork. + SHANGHAI | CANCUN => { + static TABLE: OnceLock = OnceLock::new(); + TABLE.get_or_init(|| Self::new_spec_inner(spec)) + } + // EIP-7702 was introduced in prague fork. + PRAGUE | OSAKA => { + static TABLE: OnceLock = OnceLock::new(); + TABLE.get_or_init(|| Self::new_spec_inner(spec)) + } + // New fork. + SpecId::AMSTERDAM => { + static TABLE: OnceLock = OnceLock::new(); + TABLE.get_or_init(|| Self::new_spec_inner(spec)) + } + }; + gas_params.clone() + } + + /// Creates a new `GasParams` for the given spec. + #[inline] + fn new_spec_inner(spec: SpecId) -> Self { + let mut table = [0; 256]; + + table[GasId::exp_byte_gas().as_usize()] = 10; + table[GasId::logdata().as_usize()] = gas::LOGDATA; + table[GasId::logtopic().as_usize()] = gas::LOGTOPIC; + table[GasId::copy_per_word().as_usize()] = gas::COPY; + table[GasId::extcodecopy_per_word().as_usize()] = gas::COPY; + table[GasId::mcopy_per_word().as_usize()] = gas::COPY; + table[GasId::keccak256_per_word().as_usize()] = gas::KECCAK256WORD; + table[GasId::memory_linear_cost().as_usize()] = gas::MEMORY; + table[GasId::memory_quadratic_reduction().as_usize()] = 512; + table[GasId::initcode_per_word().as_usize()] = gas::INITCODE_WORD_COST; + table[GasId::create().as_usize()] = gas::CREATE; + table[GasId::call_stipend_reduction().as_usize()] = 64; + table[GasId::transfer_value_cost().as_usize()] = gas::CALLVALUE; + table[GasId::cold_account_additional_cost().as_usize()] = 0; + table[GasId::new_account_cost().as_usize()] = gas::NEWACCOUNT; + table[GasId::warm_storage_read_cost().as_usize()] = 0; + // Frontiers had fixed 5k cost. + table[GasId::sstore_static().as_usize()] = gas::SSTORE_RESET; + // SSTORE SET + table[GasId::sstore_set_without_load_cost().as_usize()] = + gas::SSTORE_SET - gas::SSTORE_RESET; + // SSTORE RESET Is covered in SSTORE_STATIC. + table[GasId::sstore_reset_without_cold_load_cost().as_usize()] = 0; + // SSTORE SET REFUND (same as sstore_set_without_load_cost but used only in sstore_refund) + table[GasId::sstore_set_refund().as_usize()] = + table[GasId::sstore_set_without_load_cost().as_usize()]; + // SSTORE RESET REFUND (same as sstore_reset_without_cold_load_cost but used only in sstore_refund) + table[GasId::sstore_reset_refund().as_usize()] = + table[GasId::sstore_reset_without_cold_load_cost().as_usize()]; + // SSTORE CLEARING SLOT REFUND + table[GasId::sstore_clearing_slot_refund().as_usize()] = 15000; + table[GasId::selfdestruct_refund().as_usize()] = 24000; + table[GasId::call_stipend().as_usize()] = gas::CALL_STIPEND; + table[GasId::cold_storage_additional_cost().as_usize()] = 0; + table[GasId::cold_storage_cost().as_usize()] = 0; + table[GasId::new_account_cost_for_selfdestruct().as_usize()] = 0; + table[GasId::code_deposit_cost().as_usize()] = gas::CODEDEPOSIT; + table[GasId::tx_token_non_zero_byte_multiplier().as_usize()] = + gas::NON_ZERO_BYTE_MULTIPLIER; + table[GasId::tx_token_cost().as_usize()] = gas::STANDARD_TOKEN_COST; + table[GasId::tx_base_stipend().as_usize()] = 21000; + + if spec.is_enabled_in(SpecId::HOMESTEAD) { + table[GasId::tx_create_cost().as_usize()] = gas::CREATE; + } + + if spec.is_enabled_in(SpecId::TANGERINE) { + table[GasId::new_account_cost_for_selfdestruct().as_usize()] = gas::NEWACCOUNT; + } + + if spec.is_enabled_in(SpecId::SPURIOUS_DRAGON) { + table[GasId::exp_byte_gas().as_usize()] = 50; + } + + if spec.is_enabled_in(SpecId::ISTANBUL) { + table[GasId::sstore_static().as_usize()] = gas::ISTANBUL_SLOAD_GAS; + table[GasId::sstore_set_without_load_cost().as_usize()] = + gas::SSTORE_SET - gas::ISTANBUL_SLOAD_GAS; + table[GasId::sstore_reset_without_cold_load_cost().as_usize()] = + gas::SSTORE_RESET - gas::ISTANBUL_SLOAD_GAS; + table[GasId::sstore_set_refund().as_usize()] = + table[GasId::sstore_set_without_load_cost().as_usize()]; + table[GasId::sstore_reset_refund().as_usize()] = + table[GasId::sstore_reset_without_cold_load_cost().as_usize()]; + table[GasId::tx_token_non_zero_byte_multiplier().as_usize()] = + gas::NON_ZERO_BYTE_MULTIPLIER_ISTANBUL; + } + + if spec.is_enabled_in(SpecId::BERLIN) { + table[GasId::sstore_static().as_usize()] = gas::WARM_STORAGE_READ_COST; + table[GasId::cold_account_additional_cost().as_usize()] = + gas::COLD_ACCOUNT_ACCESS_COST_ADDITIONAL; + table[GasId::cold_storage_additional_cost().as_usize()] = + gas::COLD_SLOAD_COST - gas::WARM_STORAGE_READ_COST; + table[GasId::cold_storage_cost().as_usize()] = gas::COLD_SLOAD_COST; + table[GasId::warm_storage_read_cost().as_usize()] = gas::WARM_STORAGE_READ_COST; + + table[GasId::sstore_reset_without_cold_load_cost().as_usize()] = + gas::WARM_SSTORE_RESET - gas::WARM_STORAGE_READ_COST; + table[GasId::sstore_set_without_load_cost().as_usize()] = + gas::SSTORE_SET - gas::WARM_STORAGE_READ_COST; + table[GasId::sstore_set_refund().as_usize()] = + table[GasId::sstore_set_without_load_cost().as_usize()]; + table[GasId::sstore_reset_refund().as_usize()] = + table[GasId::sstore_reset_without_cold_load_cost().as_usize()]; + + table[GasId::tx_access_list_address_cost().as_usize()] = gas::ACCESS_LIST_ADDRESS; + table[GasId::tx_access_list_storage_key_cost().as_usize()] = + gas::ACCESS_LIST_STORAGE_KEY; + } + + if spec.is_enabled_in(SpecId::LONDON) { + // EIP-3529: Reduction in refunds + + // Replace SSTORE_CLEARS_SCHEDULE (as defined in EIP-2200) with + // SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST (4,800 gas as of EIP-2929 + EIP-2930) + table[GasId::sstore_clearing_slot_refund().as_usize()] = + gas::WARM_SSTORE_RESET + gas::ACCESS_LIST_STORAGE_KEY; + + table[GasId::selfdestruct_refund().as_usize()] = 0; + } + + if spec.is_enabled_in(SpecId::SHANGHAI) { + table[GasId::tx_initcode_cost().as_usize()] = gas::INITCODE_WORD_COST; + } + + if spec.is_enabled_in(SpecId::PRAGUE) { + table[GasId::tx_eip7702_per_empty_account_cost().as_usize()] = + eip7702::PER_EMPTY_ACCOUNT_COST; + + table[GasId::tx_floor_cost_per_token().as_usize()] = gas::TOTAL_COST_FLOOR_PER_TOKEN; + table[GasId::tx_floor_cost_base_gas().as_usize()] = 21000; + } + + Self::new(Arc::new(table)) + } + + /// Gets the gas cost for the given gas id. + #[inline] + pub const fn get(&self, id: GasId) -> u64 { + unsafe { *self.ptr.add(id.as_usize()) } + } + + /// `EXP` opcode cost calculation. + #[inline] + pub fn exp_cost(&self, power: U256) -> u64 { + if power.is_zero() { + return 0; + } + // EIP-160: EXP cost increase + self.get(GasId::exp_byte_gas()) + .saturating_mul(log2floor(power) / 8 + 1) + } + + /// Selfdestruct refund. + #[inline] + pub fn selfdestruct_refund(&self) -> i64 { + self.get(GasId::selfdestruct_refund()) as i64 + } + + /// Selfdestruct cold cost is calculated differently from other cold costs. + /// and it contains both cold and warm costs. + #[inline] + pub fn selfdestruct_cold_cost(&self) -> u64 { + self.cold_account_additional_cost() + self.warm_storage_read_cost() + } + + /// Selfdestruct cost. + #[inline] + pub fn selfdestruct_cost(&self, should_charge_topup: bool, is_cold: bool) -> u64 { + let mut gas = 0; + + // EIP-150: Gas cost changes for IO-heavy operations + if should_charge_topup { + gas += self.new_account_cost_for_selfdestruct(); + } + + if is_cold { + // Note: SELFDESTRUCT does not charge a WARM_STORAGE_READ_COST in case the recipient is already warm, + // which differs from how the other call-variants work. The reasoning behind this is to keep + // the changes small, a SELFDESTRUCT already costs 5K and is a no-op if invoked more than once. + // + // For GasParams both values are zero before BERLIN fork. + gas += self.selfdestruct_cold_cost(); + } + gas + } + + /// EXTCODECOPY gas cost + #[inline] + pub fn extcodecopy(&self, len: usize) -> u64 { + self.get(GasId::extcodecopy_per_word()) + .saturating_mul(num_words(len) as u64) + } + + /// MCOPY gas cost + #[inline] + pub fn mcopy_cost(&self, len: usize) -> u64 { + self.get(GasId::mcopy_per_word()) + .saturating_mul(num_words(len) as u64) + } + + /// Static gas cost for SSTORE opcode + #[inline] + pub fn sstore_static_gas(&self) -> u64 { + self.get(GasId::sstore_static()) + } + + /// SSTORE set cost + #[inline] + pub fn sstore_set_without_load_cost(&self) -> u64 { + self.get(GasId::sstore_set_without_load_cost()) + } + + /// SSTORE reset cost + #[inline] + pub fn sstore_reset_without_cold_load_cost(&self) -> u64 { + self.get(GasId::sstore_reset_without_cold_load_cost()) + } + + /// SSTORE clearing slot refund + #[inline] + pub fn sstore_clearing_slot_refund(&self) -> u64 { + self.get(GasId::sstore_clearing_slot_refund()) + } + + /// SSTORE set refund. Used in sstore_refund for SSTORE_SET_GAS - SLOAD_GAS. + #[inline] + pub fn sstore_set_refund(&self) -> u64 { + self.get(GasId::sstore_set_refund()) + } + + /// SSTORE reset refund. Used in sstore_refund for SSTORE_RESET_GAS - SLOAD_GAS. + #[inline] + pub fn sstore_reset_refund(&self) -> u64 { + self.get(GasId::sstore_reset_refund()) + } + + /// Dynamic gas cost for SSTORE opcode. + /// + /// Dynamic gas cost is gas that needs input from SSTORE operation to be calculated. + #[inline] + pub fn sstore_dynamic_gas(&self, is_istanbul: bool, vals: &SStoreResult, is_cold: bool) -> u64 { + // frontier logic gets charged for every SSTORE operation if original value is zero. + // this behaviour is fixed in istanbul fork. + if !is_istanbul { + if vals.is_present_zero() && !vals.is_new_zero() { + return self.sstore_set_without_load_cost(); + } else { + return self.sstore_reset_without_cold_load_cost(); + } + } + + let mut gas = 0; + + // this will be zero before berlin fork. + if is_cold { + gas += self.cold_storage_cost(); + } + + // if new values changed present value and present value is unchanged from original. + if vals.new_values_changes_present() && vals.is_original_eq_present() { + gas += if vals.is_original_zero() { + // set cost for creating storage slot (Zero slot means it is not existing). + // and previous condition says present is same as original. + self.sstore_set_without_load_cost() + } else { + // if new value is not zero, this means we are setting some value to it. + self.sstore_reset_without_cold_load_cost() + }; + } + gas + } + + /// SSTORE refund calculation. + #[inline] + pub fn sstore_refund(&self, is_istanbul: bool, vals: &SStoreResult) -> i64 { + // EIP-3529: Reduction in refunds + let sstore_clearing_slot_refund = self.sstore_clearing_slot_refund() as i64; + + if !is_istanbul { + // // before istanbul fork, refund was always awarded without checking original state. + if !vals.is_present_zero() && vals.is_new_zero() { + return sstore_clearing_slot_refund; + } + return 0; + } + + // If current value equals new value (this is a no-op) + if vals.is_new_eq_present() { + return 0; + } + + // refund for the clearing of storage slot. + // As new is not equal to present, new values zero means that original and present values are not zero + if vals.is_original_eq_present() && vals.is_new_zero() { + return sstore_clearing_slot_refund; + } + + let mut refund = 0; + // If original value is not 0 + if !vals.is_original_zero() { + // If current value is 0 (also means that new value is not 0), + if vals.is_present_zero() { + // remove SSTORE_CLEARS_SCHEDULE gas from refund counter. + refund -= sstore_clearing_slot_refund; + // If new value is 0 (also means that current value is not 0), + } else if vals.is_new_zero() { + // add SSTORE_CLEARS_SCHEDULE gas to refund counter. + refund += sstore_clearing_slot_refund; + } + } + + // If original value equals new value (this storage slot is reset) + if vals.is_original_eq_new() { + // If original value is 0 + if vals.is_original_zero() { + // add SSTORE_SET_GAS - SLOAD_GAS to refund counter. + refund += self.sstore_set_refund() as i64; + // Otherwise + } else { + // add SSTORE_RESET_GAS - SLOAD_GAS gas to refund counter. + refund += self.sstore_reset_refund() as i64; + } + } + refund + } + + /// `LOG` opcode cost calculation. + #[inline] + pub const fn log_cost(&self, n: u8, len: u64) -> u64 { + self.get(GasId::logdata()) + .saturating_mul(len) + .saturating_add(self.get(GasId::logtopic()) * n as u64) + } + + /// KECCAK256 gas cost per word + #[inline] + pub fn keccak256_cost(&self, len: usize) -> u64 { + self.get(GasId::keccak256_per_word()) + .saturating_mul(num_words(len) as u64) + } + + /// Memory gas cost + #[inline] + pub fn memory_cost(&self, len: usize) -> u64 { + let len = len as u64; + self.get(GasId::memory_linear_cost()) + .saturating_mul(len) + .saturating_add( + (len.saturating_mul(len)) + .saturating_div(self.get(GasId::memory_quadratic_reduction())), + ) + } + + /// Initcode word cost + #[inline] + pub fn initcode_cost(&self, len: usize) -> u64 { + self.get(GasId::initcode_per_word()) + .saturating_mul(num_words(len) as u64) + } + + /// Create gas cost + #[inline] + pub fn create_cost(&self) -> u64 { + self.get(GasId::create()) + } + + /// Create2 gas cost. + #[inline] + pub fn create2_cost(&self, len: usize) -> u64 { + self.get(GasId::create()).saturating_add( + self.get(GasId::keccak256_per_word()) + .saturating_mul(num_words(len) as u64), + ) + } + + /// Call stipend. + #[inline] + pub fn call_stipend(&self) -> u64 { + self.get(GasId::call_stipend()) + } + + /// Call stipend reduction. Call stipend is reduced by 1/64 of the gas limit. + #[inline] + pub fn call_stipend_reduction(&self, gas_limit: u64) -> u64 { + gas_limit - gas_limit / self.get(GasId::call_stipend_reduction()) + } + + /// Transfer value cost + #[inline] + pub fn transfer_value_cost(&self) -> u64 { + self.get(GasId::transfer_value_cost()) + } + + /// Additional cold cost. Additional cold cost is added to the gas cost if the account is cold loaded. + #[inline] + pub fn cold_account_additional_cost(&self) -> u64 { + self.get(GasId::cold_account_additional_cost()) + } + + /// Cold storage additional cost. + #[inline] + pub fn cold_storage_additional_cost(&self) -> u64 { + self.get(GasId::cold_storage_additional_cost()) + } + + /// Cold storage cost. + #[inline] + pub fn cold_storage_cost(&self) -> u64 { + self.get(GasId::cold_storage_cost()) + } + + /// New account cost. New account cost is added to the gas cost if the account is empty. + #[inline] + pub fn new_account_cost(&self, is_spurious_dragon: bool, transfers_value: bool) -> u64 { + // EIP-161: State trie clearing (invariant-preserving alternative) + // Pre-Spurious Dragon: always charge for new account + // Post-Spurious Dragon: only charge if value is transferred + if !is_spurious_dragon || transfers_value { + return self.get(GasId::new_account_cost()); + } + 0 + } + + /// New account cost for selfdestruct. + #[inline] + pub fn new_account_cost_for_selfdestruct(&self) -> u64 { + self.get(GasId::new_account_cost_for_selfdestruct()) + } + + /// Warm storage read cost. Warm storage read cost is added to the gas cost if the account is warm loaded. + #[inline] + pub fn warm_storage_read_cost(&self) -> u64 { + self.get(GasId::warm_storage_read_cost()) + } + + /// Copy cost + #[inline] + pub fn copy_cost(&self, len: usize) -> u64 { + self.copy_per_word_cost(num_words(len)) + } + + /// Copy per word cost + #[inline] + pub fn copy_per_word_cost(&self, word_num: usize) -> u64 { + self.get(GasId::copy_per_word()) + .saturating_mul(word_num as u64) + } + + /// Code deposit cost, calculated per byte as len * code_deposit_cost. + #[inline] + pub fn code_deposit_cost(&self, len: usize) -> u64 { + self.get(GasId::code_deposit_cost()) + .saturating_mul(len as u64) + } + + /// Used in [GasParams::initial_tx_gas] to calculate the eip7702 per empty account cost. + #[inline] + pub fn tx_eip7702_per_empty_account_cost(&self) -> u64 { + self.get(GasId::tx_eip7702_per_empty_account_cost()) + } + + /// Used in [GasParams::initial_tx_gas] to calculate the token non zero byte multiplier. + #[inline] + pub fn tx_token_non_zero_byte_multiplier(&self) -> u64 { + self.get(GasId::tx_token_non_zero_byte_multiplier()) + } + + /// Used in [GasParams::initial_tx_gas] to calculate the token cost for input data. + #[inline] + pub fn tx_token_cost(&self) -> u64 { + self.get(GasId::tx_token_cost()) + } + + /// Used in [GasParams::initial_tx_gas] to calculate the floor gas per token. + pub fn tx_floor_cost_per_token(&self) -> u64 { + self.get(GasId::tx_floor_cost_per_token()) + } + + /// Used [GasParams::initial_tx_gas] to calculate the floor gas. + /// + /// Floor gas is introduced in EIP-7623. + #[inline] + pub fn tx_floor_cost(&self, tokens_in_calldata: u64) -> u64 { + self.tx_floor_cost_per_token() * tokens_in_calldata + self.tx_floor_cost_base_gas() + } + + /// Used in [GasParams::initial_tx_gas] to calculate the floor gas base gas. + pub fn tx_floor_cost_base_gas(&self) -> u64 { + self.get(GasId::tx_floor_cost_base_gas()) + } + + /// Used in [GasParams::initial_tx_gas] to calculate the access list address cost. + pub fn tx_access_list_address_cost(&self) -> u64 { + self.get(GasId::tx_access_list_address_cost()) + } + + /// Used in [GasParams::initial_tx_gas] to calculate the access list storage key cost. + pub fn tx_access_list_storage_key_cost(&self) -> u64 { + self.get(GasId::tx_access_list_storage_key_cost()) + } + + /// Used in [GasParams::initial_tx_gas] to calculate the base transaction stipend. + pub fn tx_base_stipend(&self) -> u64 { + self.get(GasId::tx_base_stipend()) + } + + /// Used in [GasParams::initial_tx_gas] to calculate the create cost. + /// + /// Similar to the [`Self::create_cost`] method but it got activated in different fork, + #[inline] + pub fn tx_create_cost(&self) -> u64 { + self.get(GasId::tx_create_cost()) + } + + /// Used in [GasParams::initial_tx_gas] to calculate the initcode cost per word of len. + #[inline] + pub fn tx_initcode_cost(&self, len: usize) -> u64 { + self.get(GasId::tx_initcode_cost()) + .saturating_mul(num_words(len) as u64) + } + + /// Initial gas that is deducted for transaction to be included. + /// Initial gas contains initial stipend gas, gas for access list and input data. + /// + /// # Returns + /// + /// - Intrinsic gas + /// - Number of tokens in calldata + pub fn initial_tx_gas( + &self, + input: &[u8], + is_create: bool, + access_list_accounts: u64, + access_list_storages: u64, + authorization_list_num: u64, + ) -> InitialAndFloorGas { + let mut gas = InitialAndFloorGas::default(); + + // Initdate stipend + let tokens_in_calldata = + get_tokens_in_calldata(input, self.tx_token_non_zero_byte_multiplier()); + + gas.initial_gas += tokens_in_calldata * self.tx_token_cost() + // before berlin tx_access_list_address_cost will be zero + + access_list_accounts * self.tx_access_list_address_cost() + // before berlin tx_access_list_storage_key_cost will be zero + + access_list_storages * self.tx_access_list_storage_key_cost() + + self.tx_base_stipend() + // EIP-7702: Authorization list + + authorization_list_num * self.tx_eip7702_per_empty_account_cost(); + + if is_create { + // EIP-2: Homestead Hard-fork Changes + gas.initial_gas += self.tx_create_cost(); + + // EIP-3860: Limit and meter initcode + gas.initial_gas += self.tx_initcode_cost(input.len()); + } + + // Calculate gas floor for EIP-7623 + gas.floor_gas = self.tx_floor_cost(tokens_in_calldata); + + gas + } +} + +#[inline] +pub(crate) const fn log2floor(value: U256) -> u64 { + let mut l: u64 = 256; + let mut i = 3; + loop { + if value.as_limbs()[i] == 0u64 { + l -= 64; + } else { + l -= value.as_limbs()[i].leading_zeros() as u64; + if l == 0 { + return l; + } else { + return l - 1; + } + } + if i == 0 { + break; + } + i -= 1; + } + l +} + +/// Gas identifier that maps onto index in gas table. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct GasId(u8); + +impl GasId { + /// Creates a new `GasId` with the given id. + pub const fn new(id: u8) -> Self { + Self(id) + } + + /// Returns the id of the gas. + pub const fn as_u8(&self) -> u8 { + self.0 + } + + /// Returns the id of the gas as a usize. + pub const fn as_usize(&self) -> usize { + self.0 as usize + } + + /// Returns the name of the gas identifier as a string. + /// + /// # Examples + /// + /// ``` + /// use revm_context_interface::cfg::gas_params::GasId; + /// + /// assert_eq!(GasId::exp_byte_gas().name(), "exp_byte_gas"); + /// assert_eq!(GasId::memory_linear_cost().name(), "memory_linear_cost"); + /// assert_eq!(GasId::sstore_static().name(), "sstore_static"); + /// ``` + pub const fn name(&self) -> &'static str { + match self.0 { + x if x == Self::exp_byte_gas().as_u8() => "exp_byte_gas", + x if x == Self::extcodecopy_per_word().as_u8() => "extcodecopy_per_word", + x if x == Self::copy_per_word().as_u8() => "copy_per_word", + x if x == Self::logdata().as_u8() => "logdata", + x if x == Self::logtopic().as_u8() => "logtopic", + x if x == Self::mcopy_per_word().as_u8() => "mcopy_per_word", + x if x == Self::keccak256_per_word().as_u8() => "keccak256_per_word", + x if x == Self::memory_linear_cost().as_u8() => "memory_linear_cost", + x if x == Self::memory_quadratic_reduction().as_u8() => "memory_quadratic_reduction", + x if x == Self::initcode_per_word().as_u8() => "initcode_per_word", + x if x == Self::create().as_u8() => "create", + x if x == Self::call_stipend_reduction().as_u8() => "call_stipend_reduction", + x if x == Self::transfer_value_cost().as_u8() => "transfer_value_cost", + x if x == Self::cold_account_additional_cost().as_u8() => { + "cold_account_additional_cost" + } + x if x == Self::new_account_cost().as_u8() => "new_account_cost", + x if x == Self::warm_storage_read_cost().as_u8() => "warm_storage_read_cost", + x if x == Self::sstore_static().as_u8() => "sstore_static", + x if x == Self::sstore_set_without_load_cost().as_u8() => { + "sstore_set_without_load_cost" + } + x if x == Self::sstore_reset_without_cold_load_cost().as_u8() => { + "sstore_reset_without_cold_load_cost" + } + x if x == Self::sstore_clearing_slot_refund().as_u8() => "sstore_clearing_slot_refund", + x if x == Self::selfdestruct_refund().as_u8() => "selfdestruct_refund", + x if x == Self::call_stipend().as_u8() => "call_stipend", + x if x == Self::cold_storage_additional_cost().as_u8() => { + "cold_storage_additional_cost" + } + x if x == Self::cold_storage_cost().as_u8() => "cold_storage_cost", + x if x == Self::new_account_cost_for_selfdestruct().as_u8() => { + "new_account_cost_for_selfdestruct" + } + x if x == Self::code_deposit_cost().as_u8() => "code_deposit_cost", + x if x == Self::tx_eip7702_per_empty_account_cost().as_u8() => { + "tx_eip7702_per_empty_account_cost" + } + x if x == Self::tx_token_non_zero_byte_multiplier().as_u8() => { + "tx_token_non_zero_byte_multiplier" + } + x if x == Self::tx_token_cost().as_u8() => "tx_token_cost", + x if x == Self::tx_floor_cost_per_token().as_u8() => "tx_floor_cost_per_token", + x if x == Self::tx_floor_cost_base_gas().as_u8() => "tx_floor_cost_base_gas", + x if x == Self::tx_access_list_address_cost().as_u8() => "tx_access_list_address_cost", + x if x == Self::tx_access_list_storage_key_cost().as_u8() => { + "tx_access_list_storage_key_cost" + } + x if x == Self::tx_base_stipend().as_u8() => "tx_base_stipend", + x if x == Self::tx_create_cost().as_u8() => "tx_create_cost", + x if x == Self::tx_initcode_cost().as_u8() => "tx_initcode_cost", + x if x == Self::sstore_set_refund().as_u8() => "sstore_set_refund", + x if x == Self::sstore_reset_refund().as_u8() => "sstore_reset_refund", + _ => "unknown", + } + } + + /// Converts a string to a `GasId`. + /// + /// Returns `None` if the string does not match any known gas identifier. + /// + /// # Examples + /// + /// ``` + /// use revm_context_interface::cfg::gas_params::GasId; + /// + /// assert_eq!(GasId::from_name("exp_byte_gas"), Some(GasId::exp_byte_gas())); + /// assert_eq!(GasId::from_name("memory_linear_cost"), Some(GasId::memory_linear_cost())); + /// assert_eq!(GasId::from_name("invalid_name"), None); + /// ``` + pub fn from_name(s: &str) -> Option { + match s { + "exp_byte_gas" => Some(Self::exp_byte_gas()), + "extcodecopy_per_word" => Some(Self::extcodecopy_per_word()), + "copy_per_word" => Some(Self::copy_per_word()), + "logdata" => Some(Self::logdata()), + "logtopic" => Some(Self::logtopic()), + "mcopy_per_word" => Some(Self::mcopy_per_word()), + "keccak256_per_word" => Some(Self::keccak256_per_word()), + "memory_linear_cost" => Some(Self::memory_linear_cost()), + "memory_quadratic_reduction" => Some(Self::memory_quadratic_reduction()), + "initcode_per_word" => Some(Self::initcode_per_word()), + "create" => Some(Self::create()), + "call_stipend_reduction" => Some(Self::call_stipend_reduction()), + "transfer_value_cost" => Some(Self::transfer_value_cost()), + "cold_account_additional_cost" => Some(Self::cold_account_additional_cost()), + "new_account_cost" => Some(Self::new_account_cost()), + "warm_storage_read_cost" => Some(Self::warm_storage_read_cost()), + "sstore_static" => Some(Self::sstore_static()), + "sstore_set_without_load_cost" => Some(Self::sstore_set_without_load_cost()), + "sstore_reset_without_cold_load_cost" => { + Some(Self::sstore_reset_without_cold_load_cost()) + } + "sstore_clearing_slot_refund" => Some(Self::sstore_clearing_slot_refund()), + "selfdestruct_refund" => Some(Self::selfdestruct_refund()), + "call_stipend" => Some(Self::call_stipend()), + "cold_storage_additional_cost" => Some(Self::cold_storage_additional_cost()), + "cold_storage_cost" => Some(Self::cold_storage_cost()), + "new_account_cost_for_selfdestruct" => Some(Self::new_account_cost_for_selfdestruct()), + "code_deposit_cost" => Some(Self::code_deposit_cost()), + "tx_eip7702_per_empty_account_cost" => Some(Self::tx_eip7702_per_empty_account_cost()), + "tx_token_non_zero_byte_multiplier" => Some(Self::tx_token_non_zero_byte_multiplier()), + "tx_token_cost" => Some(Self::tx_token_cost()), + "tx_floor_cost_per_token" => Some(Self::tx_floor_cost_per_token()), + "tx_floor_cost_base_gas" => Some(Self::tx_floor_cost_base_gas()), + "tx_access_list_address_cost" => Some(Self::tx_access_list_address_cost()), + "tx_access_list_storage_key_cost" => Some(Self::tx_access_list_storage_key_cost()), + "tx_base_stipend" => Some(Self::tx_base_stipend()), + "tx_create_cost" => Some(Self::tx_create_cost()), + "tx_initcode_cost" => Some(Self::tx_initcode_cost()), + "sstore_set_refund" => Some(Self::sstore_set_refund()), + "sstore_reset_refund" => Some(Self::sstore_reset_refund()), + _ => None, + } + } + + /// EXP gas cost per byte + pub const fn exp_byte_gas() -> GasId { + Self::new(1) + } + + /// EXTCODECOPY gas cost per word + pub const fn extcodecopy_per_word() -> GasId { + Self::new(2) + } + + /// Copy copy per word + pub const fn copy_per_word() -> GasId { + Self::new(3) + } + + /// Log data gas cost per byte + pub const fn logdata() -> GasId { + Self::new(4) + } + + /// Log topic gas cost per topic + pub const fn logtopic() -> GasId { + Self::new(5) + } + + /// MCOPY gas cost per word + pub const fn mcopy_per_word() -> GasId { + Self::new(6) + } + + /// KECCAK256 gas cost per word + pub const fn keccak256_per_word() -> GasId { + Self::new(7) + } + + /// Memory linear cost. Memory is additionally added as n*linear_cost. + pub const fn memory_linear_cost() -> GasId { + Self::new(8) + } + + /// Memory quadratic reduction. Memory is additionally added as n*n/quadratic_reduction. + pub const fn memory_quadratic_reduction() -> GasId { + Self::new(9) + } + + /// Initcode word cost + pub const fn initcode_per_word() -> GasId { + Self::new(10) + } + + /// Create gas cost + pub const fn create() -> GasId { + Self::new(11) + } + + /// Call stipend reduction. Call stipend is reduced by 1/64 of the gas limit. + pub const fn call_stipend_reduction() -> GasId { + Self::new(12) + } + + /// Transfer value cost + pub const fn transfer_value_cost() -> GasId { + Self::new(13) + } + + /// Additional cold cost. Additional cold cost is added to the gas cost if the account is cold loaded. + pub const fn cold_account_additional_cost() -> GasId { + Self::new(14) + } + + /// New account cost. New account cost is added to the gas cost if the account is empty. + pub const fn new_account_cost() -> GasId { + Self::new(15) + } + + /// Warm storage read cost. Warm storage read cost is added to the gas cost if the account is warm loaded. + /// + /// Used in delegated account access to specify delegated account warm gas cost. + pub const fn warm_storage_read_cost() -> GasId { + Self::new(16) + } + + /// Static gas cost for SSTORE opcode. This gas in comparison with other gas const needs + /// to be deducted after check for minimal stipend gas cost. This is a reason why it is here. + pub const fn sstore_static() -> GasId { + Self::new(17) + } + + /// SSTORE set cost additional amount after SSTORE_RESET is added. + pub const fn sstore_set_without_load_cost() -> GasId { + Self::new(18) + } + + /// SSTORE reset cost + pub const fn sstore_reset_without_cold_load_cost() -> GasId { + Self::new(19) + } + + /// SSTORE clearing slot refund + pub const fn sstore_clearing_slot_refund() -> GasId { + Self::new(20) + } + + /// Selfdestruct refund. + pub const fn selfdestruct_refund() -> GasId { + Self::new(21) + } + + /// Call stipend checked in sstore. + pub const fn call_stipend() -> GasId { + Self::new(22) + } + + /// Cold storage additional cost. + pub const fn cold_storage_additional_cost() -> GasId { + Self::new(23) + } + + /// Cold storage cost + pub const fn cold_storage_cost() -> GasId { + Self::new(24) + } + + /// New account cost for selfdestruct. + pub const fn new_account_cost_for_selfdestruct() -> GasId { + Self::new(25) + } + + /// Code deposit cost. Calculated as len * code_deposit_cost. + pub const fn code_deposit_cost() -> GasId { + Self::new(26) + } + + /// EIP-7702 PER_EMPTY_ACCOUNT_COST gas + pub const fn tx_eip7702_per_empty_account_cost() -> GasId { + Self::new(27) + } + + /// Initial tx gas token non zero byte multiplier. + pub const fn tx_token_non_zero_byte_multiplier() -> GasId { + Self::new(28) + } + + /// Initial tx gas token cost. + pub const fn tx_token_cost() -> GasId { + Self::new(29) + } + + /// Initial tx gas floor cost per token. + pub const fn tx_floor_cost_per_token() -> GasId { + Self::new(30) + } + + /// Initial tx gas floor cost base gas. + pub const fn tx_floor_cost_base_gas() -> GasId { + Self::new(31) + } + + /// Initial tx gas access list address cost. + pub const fn tx_access_list_address_cost() -> GasId { + Self::new(32) + } + + /// Initial tx gas access list storage key cost. + pub const fn tx_access_list_storage_key_cost() -> GasId { + Self::new(33) + } + + /// Initial tx gas base stipend. + pub const fn tx_base_stipend() -> GasId { + Self::new(34) + } + + /// Initial tx gas create cost. + pub const fn tx_create_cost() -> GasId { + Self::new(35) + } + + /// Initial tx gas initcode cost per word. + pub const fn tx_initcode_cost() -> GasId { + Self::new(36) + } + + /// SSTORE set refund. Used in sstore_refund for SSTORE_SET_GAS - SLOAD_GAS refund calculation. + pub const fn sstore_set_refund() -> GasId { + Self::new(37) + } + + /// SSTORE reset refund. Used in sstore_refund for SSTORE_RESET_GAS - SLOAD_GAS refund calculation. + pub const fn sstore_reset_refund() -> GasId { + Self::new(38) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::collections::HashSet; + + #[test] + fn test_gas_id_name_and_from_str_coverage() { + let mut unique_names = HashSet::new(); + let mut known_gas_ids = 0; + + // Iterate over all possible GasId values (0..256) + for i in 0..=255 { + let gas_id = GasId::new(i); + let name = gas_id.name(); + + // Count unique names (excluding "unknown") + if name != "unknown" { + unique_names.insert(name); + } + } + + // Now test from_str for each unique name + for name in &unique_names { + if let Some(gas_id) = GasId::from_name(name) { + known_gas_ids += 1; + // Verify round-trip: name -> GasId -> name should be consistent + assert_eq!(gas_id.name(), *name, "Round-trip failed for {}", name); + } + } + + println!("Total unique named GasIds: {}", unique_names.len()); + println!("GasIds resolvable via from_str: {}", known_gas_ids); + + // All unique names should be resolvable via from_str + assert_eq!( + unique_names.len(), + known_gas_ids, + "Not all unique names are resolvable via from_str" + ); + + // We should have exactly 38 known GasIds (based on the indices 1-38 used) + assert_eq!( + unique_names.len(), + 38, + "Expected 38 unique GasIds, found {}", + unique_names.len() + ); + } +} diff --git a/crates/context/interface/src/context.rs b/crates/context/interface/src/context.rs index 114235cb6e..d544e1cff4 100644 --- a/crates/context/interface/src/context.rs +++ b/crates/context/interface/src/context.rs @@ -152,6 +152,13 @@ pub trait ContextTr: Host { let (_, tx, _, _, _, local) = self.all_mut(); (tx, local) } + + /// Get the configuration and journal mutably + #[inline] + fn cfg_journal_mut(&mut self) -> (&Self::Cfg, &mut Self::Journal) { + let (_, _, cfg, journal, _, _) = self.all_mut(); + (cfg, journal) + } } /// Inner Context error used for Interpreter to set error without returning it from instruction @@ -195,6 +202,18 @@ impl SStoreResult { self.new_value.const_eq(&self.present_value) } + /// Returns `true` if the new values changes the present value. + #[inline] + pub const fn new_values_changes_present(&self) -> bool { + !self.is_new_eq_present() + } + + /// Returns `true` if the original value is zero and the new value is not zero. + #[inline] + pub const fn have_changed_from_zero(&self) -> bool { + self.is_original_zero() && !self.is_new_zero() + } + /// Returns `true` if the original value is equal to the present value. #[inline] pub const fn is_original_eq_present(&self) -> bool { diff --git a/crates/context/interface/src/host.rs b/crates/context/interface/src/host.rs index 608edb53da..e4efb22081 100644 --- a/crates/context/interface/src/host.rs +++ b/crates/context/interface/src/host.rs @@ -1,11 +1,12 @@ //! Host interface for external blockchain state access. use crate::{ + cfg::GasParams, context::{SStoreResult, SelfDestructResult, StateLoad}, journaled_state::{AccountInfoLoad, AccountLoad}, }; use auto_impl::auto_impl; -use primitives::{Address, Bytes, Log, StorageKey, StorageValue, B256, U256}; +use primitives::{hardfork::SpecId, Address, Bytes, Log, StorageKey, StorageValue, B256, U256}; use state::Bytecode; /// Error that can happen when loading account info. @@ -60,6 +61,9 @@ pub trait Host { /// Max initcode size, calls `ContextTr::cfg().max_code_size().saturating_mul(2)` fn max_initcode_size(&self) -> usize; + /// Gas params contains the dynamic gas constants for the EVM. + fn gas_params(&self) -> &GasParams; + /* Database */ /// Block hash, calls `ContextTr::journal_mut().db().block_hash(number)` @@ -72,7 +76,8 @@ pub trait Host { &mut self, address: Address, target: Address, - ) -> Option>; + skip_cold_load: bool, + ) -> Result, LoadError>; /// Log, calls `ContextTr::journal_mut().log(log)` fn log(&mut self, log: Log); @@ -199,8 +204,19 @@ pub trait Host { } /// Dummy host that implements [`Host`] trait and returns all default values. -#[derive(Debug)] -pub struct DummyHost; +#[derive(Default, Debug)] +pub struct DummyHost { + gas_params: GasParams, +} + +impl DummyHost { + /// Create a new dummy host with the given spec. + pub fn new(spec: SpecId) -> Self { + Self { + gas_params: GasParams::new_spec(spec), + } + } +} impl Host for DummyHost { fn basefee(&self) -> U256 { @@ -215,6 +231,10 @@ impl Host for DummyHost { U256::ZERO } + fn gas_params(&self) -> &GasParams { + &self.gas_params + } + fn difficulty(&self) -> U256 { U256::ZERO } @@ -263,8 +283,9 @@ impl Host for DummyHost { &mut self, _address: Address, _target: Address, - ) -> Option> { - None + _skip_cold_load: bool, + ) -> Result, LoadError> { + Err(LoadError::ColdLoadSkipped) } fn log(&mut self, _log: Log) {} diff --git a/crates/context/interface/src/journaled_state.rs b/crates/context/interface/src/journaled_state.rs index 1b8f66f678..8caf3a59de 100644 --- a/crates/context/interface/src/journaled_state.rs +++ b/crates/context/interface/src/journaled_state.rs @@ -1,22 +1,31 @@ //! Journaled state trait [`JournalTr`] and related types. + +pub mod account; +pub mod entry; + use crate::{ context::{SStoreResult, SelfDestructResult}, host::LoadError, + journaled_state::account::JournaledAccountTr, + ErasedError, }; use core::ops::{Deref, DerefMut}; use database_interface::Database; use primitives::{ - hardfork::SpecId, Address, Bytes, HashSet, Log, StorageKey, StorageValue, B256, U256, + hardfork::SpecId, Address, Bytes, HashMap, HashSet, Log, StorageKey, StorageValue, B256, U256, }; use state::{Account, AccountInfo, Bytecode}; use std::{borrow::Cow, vec::Vec}; - /// Trait that contains database and journal of all changes that were made to the state. pub trait JournalTr { /// Database type that is used in the journal. type Database: Database; /// State type that is returned by the journal after finalization. type State; + /// Journal account allows modification of account with all needed changes. + type JournaledAccount<'a>: JournaledAccountTr + where + Self: 'a; /// Creates new Journaled state. /// @@ -80,27 +89,22 @@ pub trait JournalTr { /// Logs the log in Journal state. fn log(&mut self, log: Log); + /// Take logs from journal. + fn take_logs(&mut self) -> Vec; + + /// Returns the logs from journal. + fn logs(&self) -> &[Log]; + /// Marks the account for selfdestruction and transfers all the balance to the target. fn selfdestruct( &mut self, address: Address, target: Address, - ) -> Result, ::Error>; - - /// Warms the account and storage. - fn warm_account_and_storage( - &mut self, - address: Address, - storage_keys: impl IntoIterator, - ) -> Result<(), ::Error>; + skip_cold_load: bool, + ) -> Result, JournalLoadError<::Error>>; - /// Warms the account. Internally calls [`JournalTr::warm_account_and_storage`] with empty storage keys. - fn warm_account( - &mut self, - address: Address, - ) -> Result<(), ::Error> { - self.warm_account_and_storage(address, []) - } + /// Sets access list inside journal. + fn warm_access_list(&mut self, access_list: HashMap>); /// Warms the coinbase account. fn warm_coinbase_account(&mut self, address: Address); @@ -155,13 +159,23 @@ pub trait JournalTr { fn load_account( &mut self, address: Address, - ) -> Result, ::Error>; + ) -> Result, ::Error>; - /// Loads the account code. + /// Loads the account code, use `load_account_with_code` instead. + #[inline] + #[deprecated(note = "Use `load_account_with_code` instead")] fn load_account_code( &mut self, address: Address, - ) -> Result, ::Error>; + ) -> Result, ::Error> { + self.load_account_with_code(address) + } + + /// Loads the account with code. + fn load_account_with_code( + &mut self, + address: Address, + ) -> Result, ::Error>; /// Loads the account delegated. fn load_account_delegated( @@ -170,6 +184,38 @@ pub trait JournalTr { address: Address, ) -> Result, ::Error>; + /// Loads the journaled account. + #[inline] + fn load_account_mut( + &mut self, + address: Address, + ) -> Result>, ::Error> { + self.load_account_mut_skip_cold_load(address, false) + } + + /// Loads the journaled account. + fn load_account_mut_skip_cold_load( + &mut self, + address: Address, + skip_cold_load: bool, + ) -> Result>, ::Error>; + + /// Loads the journaled account. + #[inline] + fn load_account_with_code_mut( + &mut self, + address: Address, + ) -> Result>, ::Error> { + self.load_account_mut_optional_code(address, true) + } + + /// Loads the journaled account. + fn load_account_mut_optional_code( + &mut self, + address: Address, + load_code: bool, + ) -> Result>, ::Error>; + /// Sets bytecode with hash. Assume that account is warm. fn set_code_with_hash(&mut self, address: Address, code: Bytecode, hash: B256); @@ -188,7 +234,7 @@ pub trait JournalTr { &mut self, address: Address, ) -> Result, ::Error> { - let a = self.load_account_code(address)?; + let a = self.load_account_with_code(address)?; // SAFETY: Safe to unwrap as load_code will insert code if it is empty. let code = a.info.code.as_ref().unwrap().original_bytes(); @@ -200,7 +246,7 @@ pub trait JournalTr { &mut self, address: Address, ) -> Result, ::Error> { - let acc = self.load_account_code(address)?; + let acc = self.load_account_with_code(address)?; if acc.is_empty() { return Ok(StateLoad::new(B256::ZERO, acc.is_cold)); } @@ -235,9 +281,6 @@ pub trait JournalTr { /// Returns the depth of the journal. fn depth(&self) -> usize; - /// Take logs from journal. - fn take_logs(&mut self) -> Vec; - /// Commit current transaction journal and returns transaction logs. fn commit_tx(&mut self); @@ -269,6 +312,9 @@ pub enum JournalLoadError { ColdLoadSkipped, } +/// Journal error on loading of storage or account with Boxed Database error. +pub type JournalLoadErasedError = JournalLoadError; + impl JournalLoadError { /// Returns true if the error is a database error. #[inline] @@ -310,6 +356,18 @@ impl JournalLoadError { JournalLoadError::ColdLoadSkipped => (LoadError::ColdLoadSkipped, None), } } + + /// Maps the database error to a new error. + #[inline] + pub fn map(self, f: F) -> JournalLoadError + where + F: FnOnce(E) -> B, + { + match self { + JournalLoadError::DBError(e) => JournalLoadError::DBError(f(e)), + JournalLoadError::ColdLoadSkipped => JournalLoadError::ColdLoadSkipped, + } + } } impl From for JournalLoadError { @@ -374,6 +432,7 @@ impl DerefMut for StateLoad { impl StateLoad { /// Returns a new [`StateLoad`] with the given data and cold load status. + #[inline] pub fn new(data: T, is_cold: bool) -> Self { Self { data, is_cold } } @@ -381,6 +440,7 @@ impl StateLoad { /// Maps the data of the [`StateLoad`] to a new value. /// /// Useful for transforming the data of the [`StateLoad`] without changing the cold load status. + #[inline] pub fn map(self, f: F) -> StateLoad where F: FnOnce(T) -> B, @@ -413,6 +473,7 @@ pub struct AccountInfoLoad<'a> { impl<'a> AccountInfoLoad<'a> { /// Creates new [`AccountInfoLoad`] with the given account info, cold load status and empty status. + #[inline] pub fn new(account: &'a AccountInfo, is_cold: bool, is_empty: bool) -> Self { Self { account: Cow::Borrowed(account), @@ -424,6 +485,7 @@ impl<'a> AccountInfoLoad<'a> { /// Maps the account info of the [`AccountInfoLoad`] to a new [`StateLoad`]. /// /// Useful for transforming the account info of the [`AccountInfoLoad`] and preserving the cold load status. + #[inline] pub fn into_state_load(self, f: F) -> StateLoad where F: FnOnce(Cow<'a, AccountInfo>) -> O, diff --git a/crates/context/interface/src/journaled_state/account.rs b/crates/context/interface/src/journaled_state/account.rs new file mode 100644 index 0000000000..ecba313060 --- /dev/null +++ b/crates/context/interface/src/journaled_state/account.rs @@ -0,0 +1,490 @@ +//! This module contains [`JournaledAccount`] struct a wrapper around account and journal entries that +//! allow updates to the account and journal entries. +//! +//! Useful to encapsulate account and journal entries together. So when account gets changed, we can add a journal entry for it. + +use crate::{ + context::{SStoreResult, StateLoad}, + journaled_state::{entry::JournalEntry, JournalLoadErasedError, JournalLoadError}, + ErasedError, +}; + +use super::entry::JournalEntryTr; +use auto_impl::auto_impl; +use database_interface::Database; +use primitives::{ + hash_map::Entry, Address, HashMap, HashSet, StorageKey, StorageValue, B256, KECCAK_EMPTY, U256, +}; +use state::{Account, Bytecode, EvmStorageSlot}; +use std::vec::Vec; + +/// Trait that contains database and journal of all changes that were made to the account. +#[auto_impl(&mut, Box)] +pub trait JournaledAccountTr { + /// Returns the account. + fn account(&self) -> &Account; + + /// Sloads the storage slot and returns its mutable reference + fn sload( + &mut self, + key: StorageKey, + skip_cold_load: bool, + ) -> Result, JournalLoadErasedError>; + + /// Loads the storage slot and stores the new value + fn sstore( + &mut self, + key: StorageKey, + new: StorageValue, + skip_cold_load: bool, + ) -> Result, JournalLoadErasedError>; + + /// Loads the code of the account. and returns it as reference. + fn load_code(&mut self) -> Result<&Bytecode, JournalLoadErasedError>; + + /// Returns the balance of the account. + fn balance(&self) -> &U256; + + /// Returns the nonce of the account. + fn nonce(&self) -> u64; + + /// Returns the code hash of the account. + fn code_hash(&self) -> &B256; + + /// Returns the code of the account. + fn code(&self) -> Option<&Bytecode>; + + /// Touches the account. + fn touch(&mut self); + + /// Marks the account as cold without making a journal entry. + /// + /// Changing account without journal entry can be a footgun as reverting of the state change + /// would not happen without entry. It is the reason why this function has an `unsafe` prefix. + /// + /// If account is in access list, it would still be marked as warm if account get accessed again. + fn unsafe_mark_cold(&mut self); + + /// Sets the balance of the account. + /// + /// If balance is the same, we don't add a journal entry. + /// + /// Touches the account in all cases. + fn set_balance(&mut self, balance: U256); + + /// Increments the balance of the account. + /// + /// Touches the account in all cases. + fn incr_balance(&mut self, balance: U256) -> bool; + + /// Decrements the balance of the account. + /// + /// Touches the account in all cases. + fn decr_balance(&mut self, balance: U256) -> bool; + + /// Bumps the nonce of the account. + /// + /// Touches the account in all cases. + /// + /// Returns true if nonce was bumped, false if nonce is at the max value. + fn bump_nonce(&mut self) -> bool; + + /// Set the nonce of the account and create a journal entry. + /// + /// Touches the account in all cases. + fn set_nonce(&mut self, nonce: u64); + + /// Set the nonce of the account without creating a journal entry. + /// + /// Changing account without journal entry can be a footgun as reverting of the state change + /// would not happen without entry. It is the reason why this function has an `unsafe` prefix. + fn unsafe_set_nonce(&mut self, nonce: u64); + + /// Sets the code of the account. + /// + /// Touches the account in all cases. + fn set_code(&mut self, code_hash: B256, code: Bytecode); + + /// Sets the code of the account. Calculates hash of the code. + /// + /// Touches the account in all cases. + fn set_code_and_hash_slow(&mut self, code: Bytecode); + + /// Delegates the account to another address (EIP-7702). + /// + /// This touches the account, sets the code to the delegation designation, + /// and bumps the nonce. + fn delegate(&mut self, address: Address); +} + +/// Journaled account contains both mutable account and journal entries. +/// +/// Useful to encapsulate account and journal entries together. So when account gets changed, we can add a journal entry for it. +#[derive(Debug, PartialEq, Eq)] +pub struct JournaledAccount<'a, DB, ENTRY: JournalEntryTr = JournalEntry> { + /// Address of the account. + address: Address, + /// Mutable account. + account: &'a mut Account, + /// Journal entries. + journal_entries: &'a mut Vec, + /// Access list. + access_list: &'a HashMap>, + /// Transaction ID. + transaction_id: usize, + /// Database used to load storage. + db: &'a mut DB, +} + +impl<'a, DB: Database, ENTRY: JournalEntryTr> JournaledAccount<'a, DB, ENTRY> { + /// Creates new JournaledAccount + #[inline] + pub fn new( + address: Address, + account: &'a mut Account, + journal_entries: &'a mut Vec, + db: &'a mut DB, + access_list: &'a HashMap>, + transaction_id: usize, + ) -> Self { + Self { + address, + account, + journal_entries, + access_list, + transaction_id, + db, + } + } + + /// Loads the storage slot. + /// + /// If storage is cold and skip_cold_load is true, it will return [`JournalLoadError::ColdLoadSkipped`] error. + /// + /// Does not erase the db error. + #[inline(never)] + pub fn sload_concrete_error( + &mut self, + key: StorageKey, + skip_cold_load: bool, + ) -> Result, JournalLoadError> { + let is_newly_created = self.account.is_created(); + let (slot, is_cold) = match self.account.storage.entry(key) { + Entry::Occupied(occ) => { + let slot = occ.into_mut(); + // skip load if account is cold. + let mut is_cold = false; + if slot.is_cold_transaction_id(self.transaction_id) { + // is storage cold + is_cold = self + .access_list + .get(&self.address) + .and_then(|v| v.get(&key)) + .is_none(); + + if is_cold && skip_cold_load { + return Err(JournalLoadError::ColdLoadSkipped); + } + } + slot.mark_warm_with_transaction_id(self.transaction_id); + (slot, is_cold) + } + Entry::Vacant(vac) => { + // is storage cold + let is_cold = self + .access_list + .get(&self.address) + .and_then(|v| v.get(&key)) + .is_none(); + + if is_cold && skip_cold_load { + return Err(JournalLoadError::ColdLoadSkipped); + } + // if storage was cleared, we don't need to ping db. + let value = if is_newly_created { + StorageValue::ZERO + } else { + self.db.storage(self.address, key)? + }; + + let slot = vac.insert(EvmStorageSlot::new(value, self.transaction_id)); + (slot, is_cold) + } + }; + + if is_cold { + // add it to journal as cold loaded. + self.journal_entries + .push(ENTRY::storage_warmed(self.address, key)); + } + + Ok(StateLoad::new(slot, is_cold)) + } + + /// Stores the storage slot. + /// + /// If storage is cold and skip_cold_load is true, it will return [`JournalLoadError::ColdLoadSkipped`] error. + /// + /// Does not erase the db error. + #[inline] + pub fn sstore_concrete_error( + &mut self, + key: StorageKey, + new: StorageValue, + skip_cold_load: bool, + ) -> Result, JournalLoadError> { + // touch the account so changes are tracked. + self.touch(); + + // assume that acc exists and load the slot. + let slot = self.sload_concrete_error(key, skip_cold_load)?; + + let ret = Ok(StateLoad::new( + SStoreResult { + original_value: slot.original_value(), + present_value: slot.present_value(), + new_value: new, + }, + slot.is_cold, + )); + + // when new value is different from present, we need to add a journal entry and make a change. + if slot.present_value != new { + let previous_value = slot.present_value; + // insert value into present state. + slot.data.present_value = new; + + // add journal entry. + self.journal_entries + .push(ENTRY::storage_changed(self.address, key, previous_value)); + } + + ret + } + + /// Loads the code of the account. and returns it as reference. + /// + /// Does not erase the db error. + #[inline] + pub fn load_code_preserve_error(&mut self) -> Result<&Bytecode, JournalLoadError> { + if self.account.info.code.is_none() { + let hash = *self.code_hash(); + let code = if hash == KECCAK_EMPTY { + Bytecode::default() + } else { + self.db.code_by_hash(hash)? + }; + self.account.info.code = Some(code); + } + + Ok(self.account.info.code.as_ref().unwrap()) + } + + /// Consumes the journaled account and returns the account. + #[inline] + pub fn into_account(self) -> &'a Account { + self.account + } +} + +impl<'a, DB: Database, ENTRY: JournalEntryTr> JournaledAccountTr + for JournaledAccount<'a, DB, ENTRY> +{ + /// Returns the account. + fn account(&self) -> &Account { + self.account + } + + /// Returns the balance of the account. + #[inline] + fn balance(&self) -> &U256 { + &self.account.info.balance + } + + /// Returns the nonce of the account. + #[inline] + fn nonce(&self) -> u64 { + self.account.info.nonce + } + + /// Returns the code hash of the account. + #[inline] + fn code_hash(&self) -> &B256 { + &self.account.info.code_hash + } + + /// Returns the code of the account. + #[inline] + fn code(&self) -> Option<&Bytecode> { + self.account.info.code.as_ref() + } + + /// Touches the account. + #[inline] + fn touch(&mut self) { + if !self.account.status.is_touched() { + self.account.mark_touch(); + self.journal_entries + .push(ENTRY::account_touched(self.address)); + } + } + + /// Marks the account as cold without making a journal entry. + /// + /// Changing account without journal entry can be a footgun as reverting of the state change + /// would not happen without entry. It is the reason why this function has an `unsafe` prefix. + /// + /// If account is in access list, it would still be marked as warm if account get accessed again. + #[inline] + fn unsafe_mark_cold(&mut self) { + self.account.mark_cold(); + } + + /// Sets the balance of the account. + /// + /// If balance is the same, we don't add a journal entry. + /// + /// Touches the account in all cases. + #[inline] + fn set_balance(&mut self, balance: U256) { + self.touch(); + if self.account.info.balance != balance { + self.journal_entries.push(ENTRY::balance_changed( + self.address, + self.account.info.balance, + )); + self.account.info.set_balance(balance); + } + } + + /// Increments the balance of the account. + /// + /// Touches the account in all cases. + #[inline] + fn incr_balance(&mut self, balance: U256) -> bool { + self.touch(); + let Some(balance) = self.account.info.balance.checked_add(balance) else { + return false; + }; + self.set_balance(balance); + true + } + + /// Decrements the balance of the account. + /// + /// Touches the account in all cases. + #[inline] + fn decr_balance(&mut self, balance: U256) -> bool { + self.touch(); + let Some(balance) = self.account.info.balance.checked_sub(balance) else { + return false; + }; + self.set_balance(balance); + true + } + + /// Bumps the nonce of the account. + /// + /// Touches the account in all cases. + /// + /// Returns true if nonce was bumped, false if nonce is at the max value. + #[inline] + fn bump_nonce(&mut self) -> bool { + self.touch(); + let Some(nonce) = self.account.info.nonce.checked_add(1) else { + return false; + }; + self.account.info.set_nonce(nonce); + self.journal_entries.push(ENTRY::nonce_bumped(self.address)); + true + } + + /// Set the nonce of the account and create a journal entry. + /// + /// Touches the account in all cases. + #[inline] + fn set_nonce(&mut self, nonce: u64) { + self.touch(); + let previous_nonce = self.account.info.nonce; + self.account.info.set_nonce(nonce); + self.journal_entries + .push(ENTRY::nonce_changed(self.address, previous_nonce)); + } + + /// Set the nonce of the account without creating a journal entry. + /// + /// Changing account without journal entry can be a footgun as reverting of the state change + /// would not happen without entry. It is the reason why this function has an `unsafe` prefix. + #[inline] + fn unsafe_set_nonce(&mut self, nonce: u64) { + self.account.info.set_nonce(nonce); + } + + /// Sets the code of the account. + /// + /// Touches the account in all cases. + #[inline] + fn set_code(&mut self, code_hash: B256, code: Bytecode) { + self.touch(); + self.account.info.set_code_hash(code_hash); + self.account.info.set_code(code); + self.journal_entries.push(ENTRY::code_changed(self.address)); + } + + /// Sets the code of the account. Calculates hash of the code. + /// + /// Touches the account in all cases. + #[inline] + fn set_code_and_hash_slow(&mut self, code: Bytecode) { + let code_hash = code.hash_slow(); + self.set_code(code_hash, code); + } + + /// Delegates the account to another address (EIP-7702). + /// + /// This touches the account, sets the code to the delegation designation, + /// and bumps the nonce. + #[inline] + fn delegate(&mut self, address: Address) { + let (bytecode, hash) = if address.is_zero() { + (Bytecode::default(), KECCAK_EMPTY) + } else { + let bytecode = Bytecode::new_eip7702(address); + let hash = bytecode.hash_slow(); + (bytecode, hash) + }; + self.touch(); + self.set_code(hash, bytecode); + self.bump_nonce(); + } + + /// Loads the storage slot. + #[inline] + fn sload( + &mut self, + key: StorageKey, + skip_cold_load: bool, + ) -> Result, JournalLoadErasedError> { + self.sload_concrete_error(key, skip_cold_load) + .map_err(|i| i.map(ErasedError::new)) + } + + /// Stores the storage slot. + #[inline] + fn sstore( + &mut self, + key: StorageKey, + new: StorageValue, + skip_cold_load: bool, + ) -> Result, JournalLoadErasedError> { + self.sstore_concrete_error(key, new, skip_cold_load) + .map_err(|i| i.map(ErasedError::new)) + } + + /// Loads the code of the account. and returns it as reference. + #[inline] + fn load_code(&mut self) -> Result<&Bytecode, JournalLoadErasedError> { + self.load_code_preserve_error() + .map_err(|i| i.map(ErasedError::new)) + } +} diff --git a/crates/context/src/journal/entry.rs b/crates/context/interface/src/journaled_state/entry.rs similarity index 92% rename from crates/context/src/journal/entry.rs rename to crates/context/interface/src/journaled_state/entry.rs index 00b5f17219..b6807fe07c 100644 --- a/crates/context/src/journal/entry.rs +++ b/crates/context/interface/src/journaled_state/entry.rs @@ -35,8 +35,11 @@ pub trait JournalEntryTr { /// Creates a journal entry for when an account's balance is changed. fn balance_changed(address: Address, old_balance: U256) -> Self; - /// Creates a journal entry for when an account's nonce is incremented. - fn nonce_changed(address: Address) -> Self; + /// Creates a journal entry for when an account's nonce is changed. + fn nonce_changed(address: Address, previous_nonce: u64) -> Self; + + /// Creates a journal entry for when an account's nonce is bumped. + fn nonce_bumped(address: Address) -> Self; /// Creates a journal entry for when a new account is created fn account_created(address: Address, is_created_globally: bool) -> Self; @@ -158,9 +161,19 @@ pub enum JournalEntry { to: Address, }, /// Increment nonce + /// Action: Set nonce + /// Revert: Revert to previous nonce + NonceChange { + /// Address of account that had its nonce changed. + /// Nonce is incremented by one. + address: Address, + /// Previous nonce of account. + previous_nonce: u64, + }, + /// Increment nonce /// Action: Increment nonce by one /// Revert: Decrement nonce by one - NonceChange { + NonceBump { /// Address of account that had its nonce changed. /// Nonce is incremented by one. address: Address, @@ -263,8 +276,15 @@ impl JournalEntryTr for JournalEntry { } } - fn nonce_changed(address: Address) -> Self { - JournalEntry::NonceChange { address } + fn nonce_changed(address: Address, previous_nonce: u64) -> Self { + JournalEntry::NonceChange { + address, + previous_nonce, + } + } + + fn nonce_bumped(address: Address) -> Self { + JournalEntry::NonceBump { address } } fn storage_warmed(address: Address, key: StorageKey) -> Self { @@ -346,8 +366,15 @@ impl JournalEntryTr for JournalEntry { let to = state.get_mut(&to).unwrap(); to.info.balance -= balance; } - JournalEntry::NonceChange { address } => { - state.get_mut(&address).unwrap().info.nonce -= 1; + JournalEntry::NonceChange { + address, + previous_nonce, + } => { + state.get_mut(&address).unwrap().info.nonce = previous_nonce; + } + JournalEntry::NonceBump { address } => { + let nonce = &mut state.get_mut(&address).unwrap().info.nonce; + *nonce = nonce.saturating_sub(1); } JournalEntry::AccountCreated { address, diff --git a/crates/context/interface/src/lib.rs b/crates/context/interface/src/lib.rs index 0e66071368..cdf8a79c38 100644 --- a/crates/context/interface/src/lib.rs +++ b/crates/context/interface/src/lib.rs @@ -17,7 +17,7 @@ pub mod transaction; pub use block::Block; pub use cfg::{Cfg, CreateScheme, TransactTo}; pub use context::{ContextError, ContextSetters, ContextTr}; -pub use database_interface::{DBErrorMarker, Database}; +pub use database_interface::{erased_error::ErasedError, DBErrorMarker, Database}; pub use either; pub use host::{DummyHost, Host}; pub use journaled_state::JournalTr; diff --git a/crates/context/interface/src/result.rs b/crates/context/interface/src/result.rs index 2bbb99583c..282495ce1a 100644 --- a/crates/context/interface/src/result.rs +++ b/crates/context/interface/src/result.rs @@ -176,6 +176,45 @@ impl ExecutionResult { } } +impl fmt::Display for ExecutionResult { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Success { + reason, + gas_used, + gas_refunded, + logs, + output, + } => { + write!( + f, + "Success ({}): {} gas used, {} refunded", + reason, gas_used, gas_refunded + )?; + if !logs.is_empty() { + write!( + f, + ", {} log{}", + logs.len(), + if logs.len() == 1 { "" } else { "s" } + )?; + } + write!(f, ", {}", output) + } + Self::Revert { gas_used, output } => { + write!(f, "Revert: {} gas used", gas_used)?; + if !output.is_empty() { + write!(f, ", {} bytes output", output.len())?; + } + Ok(()) + } + Self::Halt { reason, gas_used } => { + write!(f, "Halted: {} ({} gas used)", reason, gas_used) + } + } + } +} + /// Output of a transaction execution #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -212,6 +251,34 @@ impl Output { } } +impl fmt::Display for Output { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Output::Call(data) => { + if data.is_empty() { + write!(f, "no output") + } else { + write!(f, "{} bytes output", data.len()) + } + } + Output::Create(data, Some(addr)) => { + if data.is_empty() { + write!(f, "contract created at {}", addr) + } else { + write!(f, "contract created at {} ({} bytes)", addr, data.len()) + } + } + Output::Create(data, None) => { + if data.is_empty() { + write!(f, "contract creation (no address)") + } else { + write!(f, "contract creation (no address, {} bytes)", data.len()) + } + } + } + } +} + /// Main EVM error #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -579,6 +646,16 @@ pub enum SuccessReason { SelfDestruct, } +impl fmt::Display for SuccessReason { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Stop => write!(f, "Stop"), + Self::Return => write!(f, "Return"), + Self::SelfDestruct => write!(f, "SelfDestruct"), + } + } +} + /// Indicates that the EVM has experienced an exceptional halt. /// /// This causes execution to immediately end with all gas being consumed. @@ -629,6 +706,37 @@ pub enum HaltReason { CallTooDeep, } +impl core::error::Error for HaltReason {} + +impl fmt::Display for HaltReason { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::OutOfGas(err) => write!(f, "{err}"), + Self::OpcodeNotFound => write!(f, "opcode not found"), + Self::InvalidFEOpcode => write!(f, "invalid 0xFE opcode"), + Self::InvalidJump => write!(f, "invalid jump destination"), + Self::NotActivated => write!(f, "feature or opcode not activated"), + Self::StackUnderflow => write!(f, "stack underflow"), + Self::StackOverflow => write!(f, "stack overflow"), + Self::OutOfOffset => write!(f, "out of offset"), + Self::CreateCollision => write!(f, "create collision"), + Self::PrecompileError => write!(f, "precompile error"), + Self::PrecompileErrorWithContext(msg) => write!(f, "precompile error: {msg}"), + Self::NonceOverflow => write!(f, "nonce overflow"), + Self::CreateContractSizeLimit => write!(f, "create contract size limit"), + Self::CreateContractStartingWithEF => { + write!(f, "create contract starting with 0xEF") + } + Self::CreateInitCodeSizeLimit => write!(f, "create initcode size limit"), + Self::OverflowPayment => write!(f, "overflow payment"), + Self::StateChangeDuringStaticCall => write!(f, "state change during static call"), + Self::CallNotAllowedInsideStatic => write!(f, "call not allowed inside static call"), + Self::OutOfFunds => write!(f, "out of funds"), + Self::CallTooDeep => write!(f, "call too deep"), + } + } +} + /// Out of gas errors. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -648,6 +756,21 @@ pub enum OutOfGasError { ReentrancySentry, } +impl core::error::Error for OutOfGasError {} + +impl fmt::Display for OutOfGasError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Basic => write!(f, "out of gas"), + Self::MemoryLimit => write!(f, "out of gas: memory limit exceeded"), + Self::Memory => write!(f, "out of gas: memory expansion"), + Self::Precompile => write!(f, "out of gas: precompile"), + Self::InvalidOperand => write!(f, "out of gas: invalid operand"), + Self::ReentrancySentry => write!(f, "out of gas: reentrancy sentry"), + } + } +} + /// Error that includes transaction index for batch transaction processing. #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -707,3 +830,38 @@ impl From for InvalidTransaction { Self::Str(Cow::Owned(s)) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_execution_result_display() { + let result: ExecutionResult = ExecutionResult::Success { + reason: SuccessReason::Return, + gas_used: 21000, + gas_refunded: 5000, + logs: vec![Log::default(), Log::default()], + output: Output::Call(Bytes::from(vec![1, 2, 3])), + }; + assert_eq!( + result.to_string(), + "Success (Return): 21000 gas used, 5000 refunded, 2 logs, 3 bytes output" + ); + + let result: ExecutionResult = ExecutionResult::Revert { + gas_used: 100000, + output: Bytes::from(vec![1, 2, 3, 4]), + }; + assert_eq!( + result.to_string(), + "Revert: 100000 gas used, 4 bytes output" + ); + + let result: ExecutionResult = ExecutionResult::Halt { + reason: HaltReason::OutOfGas(OutOfGasError::Basic), + gas_used: 1000000, + }; + assert_eq!(result.to_string(), "Halted: out of gas (1000000 gas used)"); + } +} diff --git a/crates/context/interface/src/transaction.rs b/crates/context/interface/src/transaction.rs index a1cd0a3097..6fd8dc5b7f 100644 --- a/crates/context/interface/src/transaction.rs +++ b/crates/context/interface/src/transaction.rs @@ -15,8 +15,7 @@ pub use transaction_type::TransactionType; use crate::result::InvalidTransaction; use auto_impl::auto_impl; -use core::cmp::min; -use core::fmt::Debug; +use core::{cmp::min, fmt::Debug}; use primitives::{eip4844::GAS_PER_BLOB, Address, Bytes, TxKind, B256, U256}; use std::boxed::Box; diff --git a/crates/context/src/cfg.rs b/crates/context/src/cfg.rs index 05656b0ca2..682d7e9437 100644 --- a/crates/context/src/cfg.rs +++ b/crates/context/src/cfg.rs @@ -1,12 +1,26 @@ //! This module contains [`CfgEnv`] and implements [`Cfg`] trait for it. pub use context_interface::Cfg; +use context_interface::cfg::GasParams; use primitives::{eip170, eip3860, eip7825, hardfork::SpecId}; + /// EVM configuration #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Clone, Debug, Eq, PartialEq)] #[non_exhaustive] pub struct CfgEnv { + /// Specification for EVM represent the hardfork + /// + /// [`CfgEnv::new_with_spec`] is going to set both gas params and spec. + /// + /// As GasParams is spec dependent, it is recommended to use one of following function to set both of them. + /// [`CfgEnv::set_spec_and_mainnet_gas_params`], [`CfgEnv::with_mainnet_gas_params`], [`CfgEnv::with_mainnet_gas_params`] + pub spec: SPEC, + + /// Gas params for the EVM. Use [`CfgEnv::set_gas_params`] to set the gas params. + /// If gas_params was not set it will be set to the default gas params for the spec. + pub gas_params: GasParams, + /// Chain ID of the EVM. Used in CHAINID opcode and transaction's chain ID check. /// /// Chain ID is introduced EIP-155. @@ -17,8 +31,6 @@ pub struct CfgEnv { /// If set to `false`, the transaction's chain ID check will be skipped. pub tx_chain_id_check: bool, - /// Specification for EVM represent the hardfork - pub spec: SPEC, /// Contract code size limit override. /// /// If None, the limit will be determined by the SpecId (EIP-170 or EIP-7907) at runtime. @@ -142,28 +154,9 @@ impl CfgEnv { } } -impl + Copy> CfgEnv { - /// Returns the blob base fee update fraction from [CfgEnv::blob_base_fee_update_fraction]. - /// - /// If this field is not set, return the default value for the spec. - /// - /// Default values for Cancun is [`primitives::eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN`] - /// and for Prague is [`primitives::eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE`]. - pub fn blob_base_fee_update_fraction(&mut self) -> u64 { - self.blob_base_fee_update_fraction.unwrap_or_else(|| { - let spec: SpecId = self.spec.into(); - if spec.is_enabled_in(SpecId::PRAGUE) { - primitives::eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE - } else { - primitives::eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN - } - }) - } -} - impl CfgEnv { /// Create new `CfgEnv` with default values and specified spec. - pub fn new_with_spec(spec: SPEC) -> Self { + pub fn new_with_spec_and_gas_params(spec: SPEC, gas_params: GasParams) -> Self { Self { chain_id: 1, tx_chain_id_check: true, @@ -174,6 +167,7 @@ impl CfgEnv { max_blobs_per_tx: None, tx_gas_limit_cap: None, blob_base_fee_update_fraction: None, + gas_params, #[cfg(feature = "memory_limit")] memory_limit: (1 << 32) - 1, #[cfg(feature = "optional_balance_check")] @@ -201,12 +195,37 @@ impl CfgEnv { } } + /// Returns the spec for the `CfgEnv`. + #[inline] + pub fn spec(&self) -> &SPEC { + &self.spec + } + /// Consumes `self` and returns a new `CfgEnv` with the specified chain ID. pub fn with_chain_id(mut self, chain_id: u64) -> Self { self.chain_id = chain_id; self } + /// Sets the gas params for the `CfgEnv`. + #[inline] + pub fn with_gas_params(mut self, gas_params: GasParams) -> Self { + self.set_gas_params(gas_params); + self + } + + /// Sets the spec for the `CfgEnv`. + #[inline] + pub fn set_spec(&mut self, spec: SPEC) { + self.spec = spec; + } + + /// Sets the gas params for the `CfgEnv`. + #[inline] + pub fn set_gas_params(&mut self, gas_params: GasParams) { + self.gas_params = gas_params; + } + /// Enables the transaction's chain ID check. pub fn enable_tx_chain_id_check(mut self) -> Self { self.tx_chain_id_check = true; @@ -219,8 +238,33 @@ impl CfgEnv { self } + /// Sets the spec for the `CfgEnv`. + #[inline] + #[deprecated( + since = "0.1.0", + note = "Use [`CfgEnv::with_spec_and_mainnet_gas_params`] instead" + )] + pub fn with_spec(mut self, spec: SPEC) -> Self { + self.spec = spec; + self + } + + /// Sets the spec for the `CfgEnv` and the gas params to the mainnet gas params. + pub fn with_spec_and_mainnet_gas_params + Clone>( + self, + spec: OSPEC, + ) -> CfgEnv { + self.with_spec_and_gas_params(spec.clone(), GasParams::new_spec(spec.into())) + } + /// Consumes `self` and returns a new `CfgEnv` with the specified spec. - pub fn with_spec>(self, spec: OSPEC) -> CfgEnv { + /// + /// Resets the gas params override function as it is generic over SPEC. + pub fn with_spec_and_gas_params + Clone>( + self, + spec: OSPEC, + gas_params: GasParams, + ) -> CfgEnv { CfgEnv { chain_id: self.chain_id, tx_chain_id_check: self.tx_chain_id_check, @@ -231,6 +275,7 @@ impl CfgEnv { tx_gas_limit_cap: self.tx_gas_limit_cap, max_blobs_per_tx: self.max_blobs_per_tx, blob_base_fee_update_fraction: self.blob_base_fee_update_fraction, + gas_params, #[cfg(feature = "memory_limit")] memory_limit: self.memory_limit, #[cfg(feature = "optional_balance_check")] @@ -299,6 +344,12 @@ impl CfgEnv { #[cfg(feature = "enable_eip7702")] pub fn enable_eip_7702(mut self) -> CfgEnv { self.enable_eip7702 = true; + use context_interface::cfg::gas_params::GasId; + use primitives::eip7702; + self.gas_params.override_gas([( + GasId::tx_eip7702_per_empty_account_cost(), + eip7702::PER_EMPTY_ACCOUNT_COST, + )]); self } @@ -306,6 +357,14 @@ impl CfgEnv { #[cfg(feature = "enable_eip7623")] pub fn enable_eip_7623(mut self) -> CfgEnv { self.enable_eip7623 = true; + use context_interface::cfg::{gas, gas_params::GasId}; + self.gas_params.override_gas([ + ( + GasId::tx_floor_cost_per_token(), + gas::TOTAL_COST_FLOOR_PER_TOKEN, + ), + (GasId::tx_floor_cost_base_gas(), 21000), + ]); self } @@ -317,7 +376,49 @@ impl CfgEnv { } } -impl + Copy> Cfg for CfgEnv { +impl + Clone> CfgEnv { + /// Returns the blob base fee update fraction from [CfgEnv::blob_base_fee_update_fraction]. + /// + /// If this field is not set, return the default value for the spec. + /// + /// Default values for Cancun is [`primitives::eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN`] + /// and for Prague is [`primitives::eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE`]. + pub fn blob_base_fee_update_fraction(&mut self) -> u64 { + self.blob_base_fee_update_fraction.unwrap_or_else(|| { + let spec: SpecId = self.spec.clone().into(); + if spec.is_enabled_in(SpecId::PRAGUE) { + primitives::eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE + } else { + primitives::eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN + } + }) + } + + /// Create new `CfgEnv` with default values and specified spec. + /// It will create a new gas params based on mainnet spec. + /// + /// Internally it will call [`CfgEnv::new_with_spec_and_gas_params`] with the mainnet gas params. + pub fn new_with_spec(spec: SPEC) -> Self { + Self::new_with_spec_and_gas_params(spec.clone(), GasParams::new_spec(spec.into())) + } + + /// Sets the gas params for the `CfgEnv` to the mainnet gas params. + /// + /// If spec gets changed, calling this function would use this spec to set the mainnetF gas params. + pub fn with_mainnet_gas_params(mut self) -> Self { + self.set_gas_params(GasParams::new_spec(self.spec.clone().into())); + self + } + + /// Sets the spec for the `CfgEnv` and the gas params to the mainnet gas params. + #[inline] + pub fn set_spec_and_mainnet_gas_params(&mut self, spec: SPEC) { + self.set_spec(spec.clone()); + self.set_gas_params(GasParams::new_spec(spec.into())); + } +} + +impl + Clone> Cfg for CfgEnv { type Spec = SPEC; #[inline] @@ -327,7 +428,7 @@ impl + Copy> Cfg for CfgEnv { #[inline] fn spec(&self) -> Self::Spec { - self.spec + self.spec.clone() } #[inline] @@ -338,7 +439,7 @@ impl + Copy> Cfg for CfgEnv { #[inline] fn tx_gas_limit_cap(&self) -> u64 { self.tx_gas_limit_cap - .unwrap_or(if self.spec.into().is_enabled_in(SpecId::OSAKA) { + .unwrap_or(if self.spec.clone().into().is_enabled_in(SpecId::OSAKA) { eip7825::TX_GAS_LIMIT_CAP } else { u64::MAX @@ -452,37 +553,45 @@ impl + Copy> Cfg for CfgEnv { fn is_eip7702_enabled(&self) -> bool { cfg_if::cfg_if! { if #[cfg(feature = "enable_eip7702")] { - self.enable_eip7702 || (self.spec.into() >= SpecId::PRAGUE) + self.enable_eip7702 || (self.spec.clone().into() >= SpecId::PRAGUE) } else { - self.spec.into() >= SpecId::PRAGUE + self.spec.clone().into() >= SpecId::PRAGUE } } } - fn is_eip7623_enabled(&self) -> bool { + fn is_l1_data_fee_buffer_required(&self) -> bool { cfg_if::cfg_if! { - if #[cfg(feature = "enable_eip7623")] { - self.enable_eip7623 || (self.spec.into() >= SpecId::PRAGUE) + if #[cfg(feature = "require_l1_data_fee_buffer")] { + self.require_l1_data_fee_buffer } else { - self.spec.into() >= SpecId::PRAGUE + false } } } - fn is_l1_data_fee_buffer_required(&self) -> bool { + fn memory_limit(&self) -> u64 { cfg_if::cfg_if! { - if #[cfg(feature = "require_l1_data_fee_buffer")] { - self.require_l1_data_fee_buffer + if #[cfg(feature = "memory_limit")] { + self.memory_limit } else { - false + u64::MAX } } } + + #[inline] + fn gas_params(&self) -> &GasParams { + &self.gas_params + } } -impl Default for CfgEnv { +impl> Default for CfgEnv { fn default() -> Self { - Self::new_with_spec(SPEC::default()) + Self::new_with_spec_and_gas_params( + SPEC::default(), + GasParams::new_spec(SPEC::default().into()), + ) } } diff --git a/crates/context/src/context.rs b/crates/context/src/context.rs index 66bef8f89f..de226f3342 100644 --- a/crates/context/src/context.rs +++ b/crates/context/src/context.rs @@ -1,6 +1,7 @@ //! This module contains [`Context`] struct and implements [`ContextTr`] trait for it. use crate::{block::BlockEnv, cfg::CfgEnv, journal::Journal, tx::TxEnv, LocalContext}; use context_interface::{ + cfg::GasParams, context::{ContextError, ContextSetters, SStoreResult, SelfDestructResult, StateLoad}, host::LoadError, journaled_state::AccountInfoLoad, @@ -8,7 +9,9 @@ use context_interface::{ }; use database_interface::{Database, DatabaseRef, EmptyDB, WrapDatabaseRef}; use derive_where::derive_where; -use primitives::{hardfork::SpecId, Address, Log, StorageKey, StorageValue, B256, U256}; +use primitives::{ + hardfork::SpecId, hints_util::cold_path, Address, Log, StorageKey, StorageValue, B256, U256, +}; /// EVM context contains data that EVM needs for execution. #[derive_where(Clone, Debug; BLOCK, CFG, CHAIN, TX, DB, JOURNAL, ::Error, LOCAL)] @@ -70,7 +73,7 @@ impl< let block = &self.block; let tx = &self.tx; let cfg = &self.cfg; - let db = &self.journaled_state.db(); + let db = self.journaled_state.db(); let journal = &self.journaled_state; let chain = &self.chain; let local = &self.local; @@ -131,21 +134,19 @@ impl< JOURNAL: JournalTr, CHAIN: Default, LOCAL: LocalContextTr + Default, - > Context + SPEC: Default + Into + Clone, + > Context, DB, JOURNAL, CHAIN, LOCAL> { /// Creates a new context with a new database type. /// /// This will create a new [`Journal`] object. - pub fn new(db: DB, spec: SpecId) -> Self { + pub fn new(db: DB, spec: SPEC) -> Self { let mut journaled_state = JOURNAL::new(db); - journaled_state.set_spec_id(spec); + journaled_state.set_spec_id(spec.clone().into()); Self { tx: TX::default(), block: BLOCK::default(), - cfg: CfgEnv { - spec, - ..Default::default() - }, + cfg: CfgEnv::new_with_spec(spec), local: LOCAL::default(), journaled_state, chain: Default::default(), @@ -449,6 +450,11 @@ impl< self.block().prevrandao().map(|r| r.into()) } + #[inline] + fn gas_params(&self) -> &GasParams { + self.cfg().gas_params() + } + fn block_number(&self) -> U256 { self.block().number() } @@ -498,6 +504,7 @@ impl< self.db_mut() .block_hash(requested_number) .map_err(|e| { + cold_path(); *self.error() = Err(e.into()); }) .ok() @@ -521,19 +528,26 @@ impl< } /// Marks `address` to be deleted, with funds transferred to `target`. + #[inline] fn selfdestruct( &mut self, address: Address, target: Address, - ) -> Option> { + skip_cold_load: bool, + ) -> Result, LoadError> { self.journal_mut() - .selfdestruct(address, target) + .selfdestruct(address, target, skip_cold_load) .map_err(|e| { - *self.error() = Err(e.into()); + cold_path(); + let (ret, err) = e.into_parts(); + if let Some(err) = err { + *self.error() = Err(err.into()); + } + ret }) - .ok() } + #[inline] fn sstore_skip_cold_load( &mut self, address: Address, @@ -544,6 +558,7 @@ impl< self.journal_mut() .sstore_skip_cold_load(address, key, value, skip_cold_load) .map_err(|e| { + cold_path(); let (ret, err) = e.into_parts(); if let Some(err) = err { *self.error() = Err(err.into()); @@ -552,6 +567,7 @@ impl< }) } + #[inline] fn sload_skip_cold_load( &mut self, address: Address, @@ -561,6 +577,7 @@ impl< self.journal_mut() .sload_skip_cold_load(address, key, skip_cold_load) .map_err(|e| { + cold_path(); let (ret, err) = e.into_parts(); if let Some(err) = err { *self.error() = Err(err.into()); @@ -569,20 +586,24 @@ impl< }) } + #[inline] fn load_account_info_skip_cold_load( &mut self, address: Address, load_code: bool, skip_cold_load: bool, ) -> Result, LoadError> { - let error = &mut self.error; - let journal = &mut self.journaled_state; - match journal.load_account_info_skip_cold_load(address, load_code, skip_cold_load) { + match self.journaled_state.load_account_info_skip_cold_load( + address, + load_code, + skip_cold_load, + ) { Ok(a) => Ok(a), Err(e) => { + cold_path(); let (ret, err) = e.into_parts(); if let Some(err) = err { - *error = Err(err.into()); + self.error = Err(err.into()); } Err(ret) } diff --git a/crates/context/src/evm.rs b/crates/context/src/evm.rs index 786505fdb7..283fa917b2 100644 --- a/crates/context/src/evm.rs +++ b/crates/context/src/evm.rs @@ -1,6 +1,8 @@ //! This module contains [`Evm`] struct. -use core::fmt::Debug; -use core::ops::{Deref, DerefMut}; +use core::{ + fmt::Debug, + ops::{Deref, DerefMut}, +}; use context_interface::FrameStack; diff --git a/crates/context/src/journal.rs b/crates/context/src/journal.rs index 4f5110e0ef..dd96b61f9c 100644 --- a/crates/context/src/journal.rs +++ b/crates/context/src/journal.rs @@ -2,23 +2,25 @@ //! //! Entry submodule contains [`JournalEntry`] and [`JournalEntryTr`] traits. //! and inner submodule contains [`JournalInner`] struct that contains state. -pub mod entry; pub mod inner; pub mod warm_addresses; -pub use entry::{JournalEntry, JournalEntryTr}; +pub use context_interface::journaled_state::entry::{JournalEntry, JournalEntryTr}; pub use inner::JournalInner; use bytecode::Bytecode; use context_interface::{ context::{SStoreResult, SelfDestructResult, StateLoad}, journaled_state::{ - AccountInfoLoad, AccountLoad, JournalCheckpoint, JournalLoadError, JournalTr, TransferError, + account::JournaledAccount, AccountInfoLoad, AccountLoad, JournalCheckpoint, + JournalLoadError, JournalTr, TransferError, }, }; use core::ops::{Deref, DerefMut}; use database_interface::Database; -use primitives::{hardfork::SpecId, Address, HashSet, Log, StorageKey, StorageValue, B256, U256}; +use primitives::{ + hardfork::SpecId, Address, HashMap, HashSet, Log, StorageKey, StorageValue, B256, U256, +}; use state::{Account, EvmState}; use std::vec::Vec; @@ -90,6 +92,11 @@ impl Journal { impl JournalTr for Journal { type Database = DB; type State = EvmState; + type JournaledAccount<'a> + = JournaledAccount<'a, DB, ENTRY> + where + ENTRY: 'a, + DB: 'a; fn new(database: DB) -> Journal { Self { @@ -112,7 +119,7 @@ impl JournalTr for Journal { key: StorageKey, ) -> Result, ::Error> { self.inner - .sload(&mut self.database, address, key, false) + .sload_assume_account_present(&mut self.database, address, key, false) .map_err(JournalLoadError::unwrap_db_error) } @@ -123,7 +130,7 @@ impl JournalTr for Journal { value: StorageValue, ) -> Result, ::Error> { self.inner - .sstore(&mut self.database, address, key, value, false) + .sstore_assume_account_present(&mut self.database, address, key, value, false) .map_err(JournalLoadError::unwrap_db_error) } @@ -139,12 +146,30 @@ impl JournalTr for Journal { self.inner.log(log) } + #[inline] + fn logs(&self) -> &[Log] { + &self.inner.logs + } + + #[inline] + fn take_logs(&mut self) -> Vec { + self.inner.take_logs() + } + fn selfdestruct( &mut self, address: Address, target: Address, - ) -> Result, DB::Error> { - self.inner.selfdestruct(&mut self.database, address, target) + skip_cold_load: bool, + ) -> Result, JournalLoadError<::Error>> + { + self.inner + .selfdestruct(&mut self.database, address, target, skip_cold_load) + } + + #[inline] + fn warm_access_list(&mut self, access_list: HashMap>) { + self.inner.warm_addresses.set_access_list(access_list); } fn warm_coinbase_account(&mut self, address: Address) { @@ -168,18 +193,6 @@ impl JournalTr for Journal { self.inner.depth } - #[inline] - fn warm_account_and_storage( - &mut self, - address: Address, - storage_keys: impl IntoIterator, - ) -> Result<(), ::Error> { - self.inner - .load_account_optional(&mut self.database, address, false, storage_keys, false) - .map_err(JournalLoadError::unwrap_db_error)?; - Ok(()) - } - #[inline] fn set_spec_id(&mut self, spec_id: SpecId) { self.inner.spec = spec_id; @@ -239,15 +252,37 @@ impl JournalTr for Journal { } #[inline] - fn load_account(&mut self, address: Address) -> Result, DB::Error> { + fn load_account(&mut self, address: Address) -> Result, DB::Error> { self.inner.load_account(&mut self.database, address) } #[inline] - fn load_account_code( + fn load_account_mut_skip_cold_load( &mut self, address: Address, - ) -> Result, DB::Error> { + skip_cold_load: bool, + ) -> Result>, DB::Error> { + self.inner + .load_account_mut_optional(&mut self.database, address, skip_cold_load) + .map_err(JournalLoadError::unwrap_db_error) + } + + #[inline] + fn load_account_mut_optional_code( + &mut self, + address: Address, + load_code: bool, + ) -> Result>, DB::Error> { + self.inner + .load_account_mut_optional_code(&mut self.database, address, load_code, false) + .map_err(JournalLoadError::unwrap_db_error) + } + + #[inline] + fn load_account_with_code( + &mut self, + address: Address, + ) -> Result, DB::Error> { self.inner.load_code(&mut self.database, address) } @@ -294,11 +329,6 @@ impl JournalTr for Journal { .create_account_checkpoint(caller, address, balance, spec_id) } - #[inline] - fn take_logs(&mut self) -> Vec { - self.inner.take_logs() - } - #[inline] fn commit_tx(&mut self) { self.inner.commit_tx() @@ -324,7 +354,7 @@ impl JournalTr for Journal { ) -> Result, JournalLoadError<::Error>> { self.inner - .sload(&mut self.database, address, key, skip_cold_load) + .sload_assume_account_present(&mut self.database, address, key, skip_cold_load) } #[inline] @@ -336,10 +366,16 @@ impl JournalTr for Journal { skip_cold_load: bool, ) -> Result, JournalLoadError<::Error>> { - self.inner - .sstore(&mut self.database, address, key, value, skip_cold_load) + self.inner.sstore_assume_account_present( + &mut self.database, + address, + key, + value, + skip_cold_load, + ) } + #[inline] fn load_account_info_skip_cold_load( &mut self, address: Address, @@ -348,7 +384,7 @@ impl JournalTr for Journal { ) -> Result, JournalLoadError<::Error>> { let spec = self.inner.spec; self.inner - .load_account_optional(&mut self.database, address, load_code, [], skip_cold_load) + .load_account_optional(&mut self.database, address, load_code, skip_cold_load) .map(|a| { AccountInfoLoad::new(&a.data.info, a.is_cold, a.state_clear_aware_is_empty(spec)) }) diff --git a/crates/context/src/journal/inner.rs b/crates/context/src/journal/inner.rs index 57bd5f1f96..4752b3f601 100644 --- a/crates/context/src/journal/inner.rs +++ b/crates/context/src/journal/inner.rs @@ -1,20 +1,23 @@ //! Module containing the [`JournalInner`] that is part of [`crate::Journal`]. -use crate::{entry::SelfdestructionRevertStatus, warm_addresses::WarmAddresses}; - -use super::JournalEntryTr; +use super::warm_addresses::WarmAddresses; use bytecode::Bytecode; use context_interface::{ context::{SStoreResult, SelfDestructResult, StateLoad}, - journaled_state::{AccountLoad, JournalCheckpoint, JournalLoadError, TransferError}, + journaled_state::{ + account::{JournaledAccount, JournaledAccountTr}, + entry::{JournalEntryTr, SelfdestructionRevertStatus}, + AccountLoad, JournalCheckpoint, JournalLoadError, TransferError, + }, }; use core::mem; use database_interface::Database; use primitives::{ hardfork::SpecId::{self, *}, hash_map::Entry, + hints_util::unlikely, Address, HashMap, Log, StorageKey, StorageValue, B256, KECCAK_EMPTY, U256, }; -use state::{Account, EvmState, EvmStorageSlot, TransientStorage}; +use state::{Account, EvmState, TransientStorage}; use std::vec::Vec; /// Inner journal state that contains journal and state changes. /// @@ -107,7 +110,7 @@ impl JournalInner { spec, warm_addresses, } = self; - // Spec precompiles and state are not changed. It is always set again execution. + // Spec, precompiles, BAL and state are not changed. It is always set again execution. let _ = spec; let _ = state; transient_storage.clear(); @@ -117,9 +120,10 @@ impl JournalInner { journal.clear(); // Clear coinbase address warming for next tx - warm_addresses.clear_coinbase(); + warm_addresses.clear_coinbase_and_access_list(); // increment transaction id. *transaction_id += 1; + logs.clear(); } @@ -147,7 +151,7 @@ impl JournalInner { *transaction_id += 1; // Clear coinbase address warming for next tx - warm_addresses.clear_coinbase(); + warm_addresses.clear_coinbase_and_access_list(); } /// Take the [`EvmState`] and clears the journal by resetting it to initial state. @@ -171,7 +175,7 @@ impl JournalInner { // Spec is not changed. And it is always set again in execution. let _ = spec; // Clear coinbase address warming for next tx - warm_addresses.clear_coinbase(); + warm_addresses.clear_coinbase_and_access_list(); let state = mem::take(state); logs.clear(); @@ -279,7 +283,7 @@ impl JournalInner { if bump_nonce { // nonce changed. - self.journal.push(ENTRY::nonce_changed(address)); + self.journal.push(ENTRY::nonce_bumped(address)); } } @@ -293,26 +297,15 @@ impl JournalInner { address: Address, balance: U256, ) -> Result<(), DB::Error> { - let account = self.load_account(db, address)?.data; - let old_balance = account.info.balance; - account.info.balance = account.info.balance.saturating_add(balance); - - // march account as touched. - if !account.is_touched() { - account.mark_touch(); - self.journal.push(ENTRY::account_touched(address)); - } - - // add journal entry for balance increment. - self.journal - .push(ENTRY::balance_changed(address, old_balance)); + let mut account = self.load_account_mut(db, address)?.data; + account.incr_balance(balance); Ok(()) } /// Increments the nonce of the account. #[inline] pub fn nonce_bump_journal_entry(&mut self, address: Address) { - self.journal.push(ENTRY::nonce_changed(address)); + self.journal.push(ENTRY::nonce_bumped(address)); } /// Transfers balance from two accounts. Returns error if sender balance is not enough. @@ -343,8 +336,7 @@ impl JournalInner { // sub balance from let from_account = self.state.get_mut(&from).unwrap(); - // no need to touch caller account. - // Self::touch_account(&mut self.journal, from, from_account); + Self::touch_account(&mut self.journal, from, from_account); let from_balance = &mut from_account.info.balance; let Some(from_balance_decr) = from_balance.checked_sub(balance) else { return Some(TransferError::OutOfFunds); @@ -377,40 +369,9 @@ impl JournalInner { to: Address, balance: U256, ) -> Result, DB::Error> { - if balance.is_zero() { - self.load_account(db, to)?; - let to_account = self.state.get_mut(&to).unwrap(); - Self::touch_account(&mut self.journal, to, to_account); - return Ok(None); - } - // load accounts self.load_account(db, from)?; self.load_account(db, to)?; - - // sub balance from - let from_account = self.state.get_mut(&from).unwrap(); - Self::touch_account(&mut self.journal, from, from_account); - let from_balance = &mut from_account.info.balance; - - let Some(from_balance_decr) = from_balance.checked_sub(balance) else { - return Ok(Some(TransferError::OutOfFunds)); - }; - *from_balance = from_balance_decr; - - // add balance to - let to_account = &mut self.state.get_mut(&to).unwrap(); - Self::touch_account(&mut self.journal, to, to_account); - let to_balance = &mut to_account.info.balance; - let Some(to_balance_incr) = to_balance.checked_add(balance) else { - // Overflow of U256 balance is not possible to happen on mainnet. We don't bother to return funds from from_acc. - return Ok(Some(TransferError::OverflowPayment)); - }; - *to_balance = to_balance_incr; - - self.journal - .push(ENTRY::balance_transfer(from, to, balance)); - - Ok(None) + Ok(self.transfer_loaded(from, to, balance)) } /// Creates account or returns false if collision is detected. @@ -439,14 +400,6 @@ impl JournalInner { // Enter subroutine let checkpoint = self.checkpoint(); - // Fetch balance of caller. - let caller_balance = self.state.get(&caller).unwrap().info.balance; - // Check if caller has enough balance to send to the created contract. - if caller_balance < balance { - self.checkpoint_revert(checkpoint); - return Err(TransferError::OutOfFunds); - } - // Newly created account is present, as we just loaded it. let target_acc = self.state.get_mut(&target_address).unwrap(); let last_journal = &mut self.journal; @@ -484,7 +437,8 @@ impl JournalInner { target_acc.info.balance = new_balance; // safe to decrement for the caller as balance check is already done. - self.state.get_mut(&caller).unwrap().info.balance -= balance; + let caller_account = self.state.get_mut(&caller).unwrap(); + caller_account.info.balance -= balance; // add journal entry of transferred balance last_journal.push(ENTRY::balance_transfer(caller, target_address, balance)); @@ -506,7 +460,7 @@ impl JournalInner { /// Commits the checkpoint. #[inline] pub fn checkpoint_commit(&mut self) { - self.depth -= 1; + self.depth = self.depth.saturating_sub(1); } /// Reverts all changes to state until given checkpoint. @@ -515,7 +469,7 @@ impl JournalInner { let is_spurious_dragon_enabled = self.spec.is_enabled_in(SPURIOUS_DRAGON); let state = &mut self.state; let transient_storage = &mut self.transient_storage; - self.depth -= 1; + self.depth = self.depth.saturating_sub(1); self.logs.truncate(checkpoint.log_i); // iterate over last N journals sets and revert our global state @@ -546,9 +500,10 @@ impl JournalInner { db: &mut DB, address: Address, target: Address, - ) -> Result, DB::Error> { + skip_cold_load: bool, + ) -> Result, JournalLoadError> { let spec = self.spec; - let account_load = self.load_account(db, target)?; + let account_load = self.load_account_optional(db, target, false, skip_cold_load)?; let is_cold = account_load.is_cold; let is_empty = account_load.state_clear_aware_is_empty(spec); @@ -613,12 +568,15 @@ impl JournalInner { /// Loads account into memory. return if it is cold or warm accessed #[inline] - pub fn load_account( - &mut self, - db: &mut DB, + pub fn load_account<'a, 'db, DB: Database>( + &'a mut self, + db: &'db mut DB, address: Address, - ) -> Result, DB::Error> { - self.load_account_optional(db, address, false, [], false) + ) -> Result, DB::Error> + where + 'db: 'a, + { + self.load_account_optional(db, address, false, false) .map_err(JournalLoadError::unwrap_db_error) } @@ -639,7 +597,7 @@ impl JournalInner { let spec = self.spec; let is_eip7702_enabled = spec.is_enabled_in(SpecId::PRAGUE) | is_eip7702_enabled; let account = self - .load_account_optional(db, address, is_eip7702_enabled, [], false) + .load_account_optional(db, address, is_eip7702_enabled, false) .map_err(JournalLoadError::unwrap_db_error)?; let is_empty = account.state_clear_aware_is_empty(spec); @@ -655,7 +613,7 @@ impl JournalInner { if let Some(Bytecode::Eip7702(code)) = &account.info.code { let address = code.address(); let delegate_account = self - .load_account_optional(db, address, true, [], false) + .load_account_optional(db, address, true, false) .map_err(JournalLoadError::unwrap_db_error)?; account_load.data.is_delegate_account_cold = Some(delegate_account.is_cold); } @@ -670,40 +628,121 @@ impl JournalInner { /// In case of EIP-7702 delegated account will not be loaded, /// [`Self::load_account_delegated`] should be used instead. #[inline] - pub fn load_code( - &mut self, - db: &mut DB, + pub fn load_code<'a, 'db, DB: Database>( + &'a mut self, + db: &'db mut DB, + address: Address, + ) -> Result, DB::Error> + where + 'db: 'a, + { + self.load_account_optional(db, address, true, false) + .map_err(JournalLoadError::unwrap_db_error) + } + + /// Loads account into memory. If account is already loaded it will be marked as warm. + #[inline] + pub fn load_account_optional<'a, 'db, DB: Database>( + &'a mut self, + db: &'db mut DB, + address: Address, + load_code: bool, + skip_cold_load: bool, + ) -> Result, JournalLoadError> + where + 'db: 'a, + { + let mut load = self.load_account_mut_optional(db, address, skip_cold_load)?; + if load_code { + load.data.load_code_preserve_error()?; + } + Ok(load.map(|i| i.into_account())) + } + + /// Loads account into memory. If account is already loaded it will be marked as warm. + #[inline] + pub fn load_account_mut<'a, 'db, DB: Database>( + &'a mut self, + db: &'db mut DB, address: Address, - ) -> Result, DB::Error> { - self.load_account_optional(db, address, true, [], false) + ) -> Result>, DB::Error> + where + 'db: 'a, + { + self.load_account_mut_optional(db, address, false) .map_err(JournalLoadError::unwrap_db_error) } /// Loads account. If account is already loaded it will be marked as warm. - #[inline(never)] - pub fn load_account_optional( - &mut self, - db: &mut DB, + #[inline] + pub fn load_account_mut_optional_code<'a, 'db, DB: Database>( + &'a mut self, + db: &'db mut DB, address: Address, load_code: bool, - storage_keys: impl IntoIterator, skip_cold_load: bool, - ) -> Result, JournalLoadError> { - let load = match self.state.entry(address) { + ) -> Result>, JournalLoadError> + where + 'db: 'a, + { + let mut load = self.load_account_mut_optional(db, address, skip_cold_load)?; + if load_code { + load.data.load_code_preserve_error()?; + } + Ok(load) + } + + /// Gets the account mut reference. + /// + /// # Load Unsafe + /// + /// Use this function only if you know what you are doing. It will not mark the account as warm or cold. + /// It will not bump transition_id or return if it is cold or warm loaded. This function is useful + /// when we know account is warm, touched and already loaded. + /// + /// It is useful when we want to access storage from account that is currently being executed. + #[inline] + pub fn get_account_mut<'a, 'db, DB: Database>( + &'a mut self, + db: &'db mut DB, + address: Address, + ) -> Option> + where + 'db: 'a, + { + let account = self.state.get_mut(&address)?; + Some(JournaledAccount::new( + address, + account, + &mut self.journal, + db, + self.warm_addresses.access_list(), + self.transaction_id, + )) + } + + /// Loads account. If account is already loaded it will be marked as warm. + #[inline(never)] + pub fn load_account_mut_optional<'a, 'db, DB: Database>( + &'a mut self, + db: &'db mut DB, + address: Address, + skip_cold_load: bool, + ) -> Result>, JournalLoadError> + where + 'db: 'a, + { + let (account, is_cold) = match self.state.entry(address) { Entry::Occupied(entry) => { let account = entry.into_mut(); // skip load if account is cold. let mut is_cold = account.is_cold_transaction_id(self.transaction_id); - if is_cold { - // account can be loaded by we still need to check warm_addresses to see if it is cold. - let should_be_cold = self.warm_addresses.is_cold(&address); - // dont load it cold if skipping cold load is true. - if should_be_cold && skip_cold_load { - return Err(JournalLoadError::ColdLoadSkipped); - } - is_cold = should_be_cold; + if unlikely(is_cold) { + is_cold = self + .warm_addresses + .check_is_cold(&address, skip_cold_load)?; // mark it warm. account.mark_warm_with_transaction_id(self.transaction_id); @@ -714,70 +753,55 @@ impl JournalInner { account.selfdestruct(); account.unmark_selfdestructed_locally(); } + // set original info to current info. + *account.original_info = account.info.clone(); + // unmark locally created account.unmark_created_locally(); + + // journal loading of cold account. + self.journal.push(ENTRY::account_warmed(address)); } - StateLoad { - data: account, - is_cold, - } + (account, is_cold) } Entry::Vacant(vac) => { - // Precompiles among some other account(coinbase included) are warm loaded so we need to take that into account - let is_cold = self.warm_addresses.is_cold(&address); + // Precompiles, among some other account(access list and coinbase included) + // are warm loaded so we need to take that into account + let is_cold = self + .warm_addresses + .check_is_cold(&address, skip_cold_load)?; - // dont load cold account if skip_cold_load is true - if is_cold && skip_cold_load { - return Err(JournalLoadError::ColdLoadSkipped); - } let account = if let Some(account) = db.basic(address)? { - account.into() + let mut account: Account = account.into(); + account.transaction_id = self.transaction_id; + account } else { Account::new_not_existing(self.transaction_id) }; - StateLoad { - data: vac.insert(account), - is_cold, + // journal loading of cold account. + if is_cold { + self.journal.push(ENTRY::account_warmed(address)); } - } - }; - // journal loading of cold account. - if load.is_cold { - self.journal.push(ENTRY::account_warmed(address)); - } - if load_code { - let info = &mut load.data.info; - if info.code.is_none() { - let code = if info.code_hash == KECCAK_EMPTY { - Bytecode::default() - } else { - db.code_by_hash(info.code_hash)? - }; - info.code = Some(code); + (vac.insert(account), is_cold) } - } + }; - for storage_key in storage_keys.into_iter() { - sload_with_account( - load.data, - db, + Ok(StateLoad::new( + JournaledAccount::new( + address, + account, &mut self.journal, + db, + self.warm_addresses.access_list(), self.transaction_id, - address, - storage_key, - false, - )?; - } - Ok(load) + ), + is_cold, + )) } /// Loads storage slot. - /// - /// # Panics - /// - /// Panics if the account is not present in the state. #[inline] pub fn sload( &mut self, @@ -786,18 +810,45 @@ impl JournalInner { key: StorageKey, skip_cold_load: bool, ) -> Result, JournalLoadError> { - // assume acc is warm - let account = self.state.get_mut(&address).unwrap(); - // only if account is created in this tx we can assume that storage is empty. - sload_with_account( - account, - db, - &mut self.journal, - self.transaction_id, - address, - key, - skip_cold_load, - ) + self.load_account_mut(db, address)? + .sload_concrete_error(key, skip_cold_load) + .map(|s| s.map(|s| s.present_value)) + } + + /// Loads storage slot. + /// + /// If account is not present it will return [`JournalLoadError::ColdLoadSkipped`] error. + #[inline] + pub fn sload_assume_account_present( + &mut self, + db: &mut DB, + address: Address, + key: StorageKey, + skip_cold_load: bool, + ) -> Result, JournalLoadError> { + let Some(mut account) = self.get_account_mut(db, address) else { + return Err(JournalLoadError::ColdLoadSkipped); + }; + + account + .sload_concrete_error(key, skip_cold_load) + .map(|s| s.map(|s| s.present_value)) + } + + /// Stores storage slot. + /// + /// If account is not present it will load from database + #[inline] + pub fn sstore( + &mut self, + db: &mut DB, + address: Address, + key: StorageKey, + new: StorageValue, + skip_cold_load: bool, + ) -> Result, JournalLoadError> { + self.load_account_mut(db, address)? + .sstore_concrete_error(key, new, skip_cold_load) } /// Stores storage slot. @@ -806,7 +857,7 @@ impl JournalInner { /// /// **Note**: Account should already be present in our state. #[inline] - pub fn sstore( + pub fn sstore_assume_account_present( &mut self, db: &mut DB, address: Address, @@ -814,37 +865,11 @@ impl JournalInner { new: StorageValue, skip_cold_load: bool, ) -> Result, JournalLoadError> { - // assume that acc exists and load the slot. - let present = self.sload(db, address, key, skip_cold_load)?; - let acc = self.state.get_mut(&address).unwrap(); - - // if there is no original value in dirty return present value, that is our original. - let slot = acc.storage.get_mut(&key).unwrap(); - - // new value is same as present, we don't need to do anything - if present.data == new { - return Ok(StateLoad::new( - SStoreResult { - original_value: slot.original_value(), - present_value: present.data, - new_value: new, - }, - present.is_cold, - )); - } + let Some(mut account) = self.get_account_mut(db, address) else { + return Err(JournalLoadError::ColdLoadSkipped); + }; - self.journal - .push(ENTRY::storage_changed(address, key, present.data)); - // insert value into present state. - slot.present_value = new; - Ok(StateLoad::new( - SStoreResult { - original_value: slot.original_value(), - present_value: present.data, - new_value: new, - }, - present.is_cold, - )) + account.sstore_concrete_error(key, new, skip_cold_load) } /// Read transient storage tied to the account. @@ -901,49 +926,47 @@ impl JournalInner { } } -/// Loads storage slot with account. -#[inline] -pub fn sload_with_account( - account: &mut Account, - db: &mut DB, - journal: &mut Vec, - transaction_id: usize, - address: Address, - key: StorageKey, - skip_cold_load: bool, -) -> Result, JournalLoadError> { - let is_newly_created = account.is_created(); - let (value, is_cold) = match account.storage.entry(key) { - Entry::Occupied(occ) => { - let slot = occ.into_mut(); - // skip load if account is cold. - let is_cold = slot.is_cold_transaction_id(transaction_id); - if skip_cold_load && is_cold { - return Err(JournalLoadError::ColdLoadSkipped); - } - slot.mark_warm_with_transaction_id(transaction_id); - (slot.present_value, is_cold) - } - Entry::Vacant(vac) => { - if skip_cold_load { - return Err(JournalLoadError::ColdLoadSkipped); - } - // if storage was cleared, we don't need to ping db. - let value = if is_newly_created { - StorageValue::ZERO - } else { - db.storage(address, key)? - }; - vac.insert(EvmStorageSlot::new(value, transaction_id)); - - (value, true) - } - }; - - if is_cold { - // add it to journal as cold loaded. - journal.push(ENTRY::storage_warmed(address, key)); +#[cfg(test)] +mod tests { + use super::*; + use context_interface::journaled_state::entry::JournalEntry; + use database_interface::EmptyDB; + use primitives::{address, HashSet, U256}; + use state::AccountInfo; + + #[test] + fn test_sload_skip_cold_load() { + let mut journal = JournalInner::::new(); + let test_address = address!("1000000000000000000000000000000000000000"); + let test_key = U256::from(1); + + // Insert account into state + let account_info = AccountInfo { + balance: U256::from(1000), + nonce: 1, + code_hash: KECCAK_EMPTY, + code: Some(Bytecode::default()), + account_id: None, + }; + journal + .state + .insert(test_address, Account::from(account_info)); + + // Add storage slot to access list (make it warm) + let mut access_list = HashMap::default(); + let mut storage_keys = HashSet::default(); + storage_keys.insert(test_key); + access_list.insert(test_address, storage_keys); + journal.warm_addresses.set_access_list(access_list); + + // Try to sload with skip_cold_load=true - should succeed because slot is in access list + let mut db = EmptyDB::new(); + let result = journal.sload_assume_account_present(&mut db, test_address, test_key, true); + + // Should succeed and return as warm + assert!(result.is_ok()); + let state_load = result.unwrap(); + assert!(!state_load.is_cold); // Should be warm + assert_eq!(state_load.data, U256::ZERO); // Empty slot } - - Ok(StateLoad::new(value, is_cold)) } diff --git a/crates/context/src/journal/warm_addresses.rs b/crates/context/src/journal/warm_addresses.rs index 8335563ee4..27e5bce936 100644 --- a/crates/context/src/journal/warm_addresses.rs +++ b/crates/context/src/journal/warm_addresses.rs @@ -3,9 +3,18 @@ //! It is used to optimize access to precompile addresses. use bitvec::{bitvec, vec::BitVec}; -use primitives::{short_address, Address, HashSet, SHORT_ADDRESS_CAP}; +use context_interface::journaled_state::JournalLoadError; +use primitives::{short_address, Address, HashMap, HashSet, StorageKey, SHORT_ADDRESS_CAP}; /// Stores addresses that are warm loaded. Contains precompiles and coinbase address. +/// +/// It contains precompiles addresses that are not changed frequently and AccessList that +/// is changed per transaction. +/// +/// [WarmAddresses::precompiles] will always contain all precompile addresses. +/// +/// As precompiles addresses are usually very small, precompile_short_addresses will +/// contain bitset of shrunk precompile address. #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct WarmAddresses { @@ -15,9 +24,11 @@ pub struct WarmAddresses { /// will be stored in this bit vector for faster access. precompile_short_addresses: BitVec, /// `true` if all precompiles are short addresses. - all_short_addresses: bool, + precompile_all_short_addresses: bool, /// Coinbase address. coinbase: Option
, + /// Access list + access_list: HashMap>, } impl Default for WarmAddresses { @@ -33,8 +44,9 @@ impl WarmAddresses { Self { precompile_set: HashSet::default(), precompile_short_addresses: bitvec![0; SHORT_ADDRESS_CAP], - all_short_addresses: true, + precompile_all_short_addresses: true, coinbase: None, + access_list: HashMap::default(), } } @@ -64,7 +76,7 @@ impl WarmAddresses { } } - self.all_short_addresses = all_short_addresses; + self.precompile_all_short_addresses = all_short_addresses; self.precompile_set = addresses; } @@ -74,12 +86,31 @@ impl WarmAddresses { self.coinbase = Some(address); } + /// Set the access list. + #[inline] + pub fn set_access_list(&mut self, access_list: HashMap>) { + self.access_list = access_list; + } + + /// Returns the access list. + #[inline] + pub fn access_list(&self) -> &HashMap> { + &self.access_list + } + /// Clear the coinbase address. #[inline] pub fn clear_coinbase(&mut self) { self.coinbase = None; } + /// Clear the coinbase and access list. + #[inline] + pub fn clear_coinbase_and_access_list(&mut self) { + self.coinbase = None; + self.access_list.clear(); + } + /// Returns true if the address is warm loaded. #[inline] pub fn is_warm(&self, address: &Address) -> bool { @@ -88,6 +119,11 @@ impl WarmAddresses { return true; } + // if it is part of access list. + if self.access_list.contains_key(address) { + return true; + } + // if there are no precompiles, it is cold loaded and bitvec is not set. if self.precompile_set.is_empty() { return false; @@ -98,13 +134,22 @@ impl WarmAddresses { return self.precompile_short_addresses[short_address]; } - // if all precompiles are short addresses, it is cold loaded. - if self.all_short_addresses { - return false; + if !self.precompile_all_short_addresses { + // in the end check if it is inside precompile set + return self.precompile_set.contains(address); } - // in the end check if it is inside precompile set - self.precompile_set.contains(address) + false + } + + /// Returns true if the storage is warm loaded. + #[inline] + pub fn is_storage_warm(&self, address: &Address, key: &StorageKey) -> bool { + if let Some(access_list) = self.access_list.get(address) { + return access_list.contains(key); + } + + false } /// Returns true if the address is cold loaded. @@ -112,6 +157,22 @@ impl WarmAddresses { pub fn is_cold(&self, address: &Address) -> bool { !self.is_warm(address) } + + /// Checks if the address is cold loaded and returns an error if it is and skip_cold_load is true. + #[inline(never)] + pub fn check_is_cold( + &self, + address: &Address, + skip_cold_load: bool, + ) -> Result> { + let is_cold = self.is_cold(address); + + if is_cold && skip_cold_load { + return Err(JournalLoadError::ColdLoadSkipped); + } + + Ok(is_cold) + } } #[cfg(test)] @@ -146,7 +207,7 @@ mod tests { assert!(warm_addresses.is_warm(&coinbase_addr)); // Test clearing coinbase - warm_addresses.clear_coinbase(); + warm_addresses.clear_coinbase_and_access_list(); assert!(warm_addresses.coinbase.is_none()); assert!(!warm_addresses.is_warm(&coinbase_addr)); } diff --git a/crates/context/src/local.rs b/crates/context/src/local.rs index 55b321ff88..1026721b73 100644 --- a/crates/context/src/local.rs +++ b/crates/context/src/local.rs @@ -42,7 +42,9 @@ impl LocalContextTr for LocalContext { } impl LocalContext { - /// Creates a new local context, initcodes are hashes and added to the mapping. + /// Creates a new local context with default values. + /// + /// Initializes a shared memory buffer with 4KB capacity and no precompile error message. pub fn new() -> Self { Self::default() } diff --git a/crates/context/src/setters.rs b/crates/context/src/setters.rs deleted file mode 100644 index 278fd40d74..0000000000 --- a/crates/context/src/setters.rs +++ /dev/null @@ -1,39 +0,0 @@ -use crate::Context; -use auto_impl::auto_impl; -use context_interface::{Block, Cfg, Database, JournalTr, Transaction}; - -/// Setters for the context. -#[auto_impl(&mut, Box)] -pub trait ContextSetters { - /// Transaction type. - type Tx: Transaction; - /// Block type. - type Block: Block; - - /// Set the transaction. - fn set_tx(&mut self, tx: Self::Tx); - - /// Set the block. - fn set_block(&mut self, block: Self::Block); -} - -impl ContextSetters - for Context -where - BLOCK: Block, - TX: Transaction, - CFG: Cfg, - DB: Database, - JOURNAL: JournalTr, -{ - type Tx = TX; - type Block = BLOCK; - - fn set_tx(&mut self, tx: Self::Tx) { - self.tx = tx; - } - - fn set_block(&mut self, block: Self::Block) { - self.block = block; - } -} diff --git a/crates/database/CHANGELOG.md b/crates/database/CHANGELOG.md index 78c4b2e43a..c54c25d78a 100644 --- a/crates/database/CHANGELOG.md +++ b/crates/database/CHANGELOG.md @@ -7,6 +7,65 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [10.0.0](https://github.com/bluealloy/revm/compare/revm-database-v9.0.6...revm-database-v10.0.0) - 2026-01-15 + +### Added + +- *(cache-db)* Added pritty_print for CacheDB ([#3296](https://github.com/bluealloy/revm/pull/3296)) +- Propagate `map-foldhash` Feature Through Dependency Chain ([#3252](https://github.com/bluealloy/revm/pull/3252)) +- BAL EIP-7928 ([#3070](https://github.com/bluealloy/revm/pull/3070)) +- DatabaseCommitExt::drain_balances ([#3205](https://github.com/bluealloy/revm/pull/3205)) +- DatabaseCommitExt + increment_balances ([#3195](https://github.com/bluealloy/revm/pull/3195)) +- DatabaseCommit::commit_iter ([#3197](https://github.com/bluealloy/revm/pull/3197)) +- Restrict Database::Error. JournaledAccountTr ([#3199](https://github.com/bluealloy/revm/pull/3199)) + +### Fixed + +- *(database)* return error instead of panic when block not found in AlloyDB ([#3284](https://github.com/bluealloy/revm/pull/3284)) +- *(database)* make DatabaseCommit dyn-compatible ([#3264](https://github.com/bluealloy/revm/pull/3264)) +- *(database)* prevent deadlock in ([#3251](https://github.com/bluealloy/revm/pull/3251)) +- *(database)* verify handle belongs to current runtime before block_in_place ([#3212](https://github.com/bluealloy/revm/pull/3212)) + +### Other + +- fix typos, grammar errors, and improve documentation consistency ([#3294](https://github.com/bluealloy/revm/pull/3294)) +- fix typos and grammar in database crate ([#3279](https://github.com/bluealloy/revm/pull/3279)) +- happy new year, 2026 licence ([#3272](https://github.com/bluealloy/revm/pull/3272)) +- avoid collect in CacheState commit ([#3242](https://github.com/bluealloy/revm/pull/3242)) +- *(database)* use fixed hashmaps in cache db ([#3231](https://github.com/bluealloy/revm/pull/3231)) +- *(database)* avoid triple cache lookup ([#3232](https://github.com/bluealloy/revm/pull/3232)) +- optimize vector initialization with size hints in state and precompile modules ([#3191](https://github.com/bluealloy/revm/pull/3191)) + +## [9.0.6](https://github.com/bluealloy/revm/compare/revm-database-v9.0.5...revm-database-v9.0.6) - 2025-11-14 + +### Fixed + +- *(database)* return correct bytecode in BenchmarkDB::code_by_hash ([#3170](https://github.com/bluealloy/revm/pull/3170)) + +## [9.0.5](https://github.com/bluealloy/revm/compare/revm-database-v9.0.4...revm-database-v9.0.5) - 2025-11-10 + +### Fixed + +- *(op)* Ensure L1Block account is always loaded ([#3150](https://github.com/bluealloy/revm/pull/3150)) + +## [9.0.4](https://github.com/bluealloy/revm/compare/revm-database-v9.0.3...revm-database-v9.0.4) - 2025-11-07 + +### Other + +- updated the following local packages: revm-primitives, revm-bytecode, revm-state, revm-database-interface + +## [9.0.3](https://github.com/bluealloy/revm/compare/revm-database-v9.0.2...revm-database-v9.0.3) - 2025-10-30 + +### Other + +- updated the following local packages: revm-bytecode, revm-state, revm-database-interface + +## [9.0.2](https://github.com/bluealloy/revm/compare/revm-database-v9.0.1...revm-database-v9.0.2) - 2025-10-15 + +### Other + +- updated the following local packages: revm-bytecode, revm-state, revm-database-interface + ## [9.0.1](https://github.com/bluealloy/revm/compare/revm-database-v9.0.0...revm-database-v9.0.1) - 2025-10-15 ### Other diff --git a/crates/database/Cargo.toml b/crates/database/Cargo.toml index d9fd4fe017..f7a0df71f8 100644 --- a/crates/database/Cargo.toml +++ b/crates/database/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "revm-database" description = "Revm Database implementations" -version = "9.0.1" +version = "10.0.0" authors.workspace = true edition.workspace = true keywords.workspace = true @@ -65,3 +65,4 @@ alloydb = [ "dep:alloy-eips", "dep:alloy-transport", ] +map-foldhash = ["primitives/map-foldhash", "state/map-foldhash"] diff --git a/crates/database/LICENSE b/crates/database/LICENSE index be6d350ebe..7edb0405ff 100644 --- a/crates/database/LICENSE +++ b/crates/database/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021-2025 draganrakita +Copyright (c) 2021-2026 draganrakita Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/crates/database/interface/CHANGELOG.md b/crates/database/interface/CHANGELOG.md index 4717e67481..ff79d15a5d 100644 --- a/crates/database/interface/CHANGELOG.md +++ b/crates/database/interface/CHANGELOG.md @@ -7,6 +7,47 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [9.0.0](https://github.com/bluealloy/revm/compare/revm-database-interface-v8.0.5...revm-database-interface-v9.0.0) - 2026-01-15 + +### Added + +- BAL EIP-7928 ([#3070](https://github.com/bluealloy/revm/pull/3070)) +- DatabaseCommitExt::drain_balances ([#3205](https://github.com/bluealloy/revm/pull/3205)) +- DatabaseCommitExt + increment_balances ([#3195](https://github.com/bluealloy/revm/pull/3195)) +- DatabaseCommit::commit_iter ([#3197](https://github.com/bluealloy/revm/pull/3197)) +- Restrict Database::Error. JournaledAccountTr ([#3199](https://github.com/bluealloy/revm/pull/3199)) + +### Fixed + +- *(database)* make DatabaseCommit dyn-compatible ([#3264](https://github.com/bluealloy/revm/pull/3264)) +- *(database)* prevent deadlock in ([#3251](https://github.com/bluealloy/revm/pull/3251)) +- *(database)* verify handle belongs to current runtime before block_in_place ([#3212](https://github.com/bluealloy/revm/pull/3212)) + +### Other + +- fix typos, grammar errors, and improve documentation consistency ([#3294](https://github.com/bluealloy/revm/pull/3294)) +- fix typos and grammar in database crate ([#3279](https://github.com/bluealloy/revm/pull/3279)) +- happy new year, 2026 licence ([#3272](https://github.com/bluealloy/revm/pull/3272)) +- *(fmt)* merge all imports ([#3184](https://github.com/bluealloy/revm/pull/3184)) + +## [8.0.5](https://github.com/bluealloy/revm/compare/revm-database-interface-v8.0.4...revm-database-interface-v8.0.5) - 2025-11-07 + +### Other + +- updated the following local packages: revm-primitives, revm-state + +## [8.0.4](https://github.com/bluealloy/revm/compare/revm-database-interface-v8.0.3...revm-database-interface-v8.0.4) - 2025-10-30 + +### Other + +- updated the following local packages: revm-state + +## [8.0.3](https://github.com/bluealloy/revm/compare/revm-database-interface-v8.0.2...revm-database-interface-v8.0.3) - 2025-10-15 + +### Other + +- updated the following local packages: revm-state + ## [8.0.2](https://github.com/bluealloy/revm/compare/revm-database-interface-v8.0.1...revm-database-interface-v8.0.2) - 2025-10-15 ### Other diff --git a/crates/database/interface/Cargo.toml b/crates/database/interface/Cargo.toml index b86656476e..133e05c119 100644 --- a/crates/database/interface/Cargo.toml +++ b/crates/database/interface/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "revm-database-interface" description = "Revm Database interface" -version = "8.0.2" +version = "9.0.0" authors.workspace = true edition.workspace = true keywords.workspace = true @@ -25,6 +25,7 @@ primitives.workspace = true # misc auto_impl.workspace = true either.workspace = true +thiserror.workspace = true # Optional serde = { workspace = true, features = ["derive", "rc"], optional = true } @@ -36,6 +37,12 @@ tokio = { workspace = true, optional = true } [features] default = ["std"] -std = ["serde?/std", "primitives/std", "state/std", "either/std"] +std = [ + "serde?/std", + "primitives/std", + "state/std", + "either/std", + "thiserror/std", +] serde = ["dep:serde", "primitives/serde", "state/serde", "either/serde"] asyncdb = ["dep:tokio", "tokio/rt-multi-thread"] diff --git a/crates/database/interface/LICENSE b/crates/database/interface/LICENSE index be6d350ebe..7edb0405ff 100644 --- a/crates/database/interface/LICENSE +++ b/crates/database/interface/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021-2025 draganrakita +Copyright (c) 2021-2026 draganrakita Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/crates/database/interface/src/async_db.rs b/crates/database/interface/src/async_db.rs index 5e42d0c9c1..2561e7c642 100644 --- a/crates/database/interface/src/async_db.rs +++ b/crates/database/interface/src/async_db.rs @@ -1,6 +1,6 @@ //! Async database interface. use crate::{DBErrorMarker, Database, DatabaseRef}; -use core::{error::Error, future::Future}; +use core::future::Future; use primitives::{Address, StorageKey, StorageValue, B256}; use state::{AccountInfo, Bytecode}; use tokio::runtime::{Handle, Runtime}; @@ -12,7 +12,7 @@ use tokio::runtime::{Handle, Runtime}; /// Use [WrapDatabaseAsync] to provide [Database] implementation for a type that only implements this trait. pub trait DatabaseAsync { /// The database error type - type Error: Send + DBErrorMarker + Error; + type Error: DBErrorMarker; /// Gets basic account information. fn basic_async( @@ -33,6 +33,20 @@ pub trait DatabaseAsync { index: StorageKey, ) -> impl Future> + Send; + /// Gets storage value of account by its id. + /// + /// Default implementation is to call [`DatabaseAsync::storage_async`] method. + #[inline] + fn storage_by_account_id_async( + &mut self, + address: Address, + account_id: usize, + storage_key: StorageKey, + ) -> impl Future> + Send { + let _ = account_id; + self.storage_async(address, storage_key) + } + /// Gets block hash by block number. fn block_hash_async( &mut self, @@ -47,7 +61,7 @@ pub trait DatabaseAsync { /// Use [WrapDatabaseAsync] to provide [DatabaseRef] implementation for a type that only implements this trait. pub trait DatabaseAsyncRef { /// The database error type - type Error: Send + DBErrorMarker + Error; + type Error: DBErrorMarker; /// Gets basic account information. fn basic_async_ref( @@ -68,6 +82,20 @@ pub trait DatabaseAsyncRef { index: StorageKey, ) -> impl Future> + Send; + /// Gets storage value of account by its id. + /// + /// Default implementation is to call [`DatabaseAsyncRef::storage_async_ref`] method. + #[inline] + fn storage_by_account_id_async_ref( + &self, + address: Address, + account_id: usize, + storage_key: StorageKey, + ) -> impl Future> + Send { + let _ = account_id; + self.storage_async_ref(address, storage_key) + } + /// Gets block hash by block number. fn block_hash_async_ref( &self, @@ -141,6 +169,22 @@ impl Database for WrapDatabaseAsync { self.rt.block_on(self.db.storage_async(address, index)) } + /// Gets storage value of account by its id. + /// + /// Wraps [`DatabaseAsync::storage_by_account_id_async`] in a blocking call. + #[inline] + fn storage_by_account_id( + &mut self, + address: Address, + account_id: usize, + storage_key: StorageKey, + ) -> Result { + self.rt.block_on( + self.db + .storage_by_account_id_async(address, account_id, storage_key), + ) + } + #[inline] fn block_hash(&mut self, number: u64) -> Result { self.rt.block_on(self.db.block_hash_async(number)) @@ -169,6 +213,19 @@ impl DatabaseRef for WrapDatabaseAsync { self.rt.block_on(self.db.storage_async_ref(address, index)) } + #[inline] + fn storage_by_account_id_ref( + &self, + address: Address, + account_id: usize, + storage_key: StorageKey, + ) -> Result { + self.rt.block_on( + self.db + .storage_by_account_id_async_ref(address, account_id, storage_key), + ) + } + #[inline] fn block_hash_ref(&self, number: u64) -> Result { self.rt.block_on(self.db.block_hash_async_ref(number)) @@ -191,19 +248,37 @@ impl HandleOrRuntime { { match self { Self::Handle(handle) => { - // Use block_in_place only when we're currently inside a multi-threaded Tokio runtime. - // Otherwise, call handle.block_on directly to avoid panicking outside of a runtime. - let can_block_in_place = match Handle::try_current() { - Ok(current) => !matches!( - current.runtime_flavor(), - tokio::runtime::RuntimeFlavor::CurrentThread - ), - Err(_) => false, - }; - - if can_block_in_place { + // We use a conservative approach: if we're currently in a multi-threaded + // tokio runtime context, we use block_in_place. This works because: + // 1. If we're in the SAME runtime, block_in_place prevents deadlock + // 2. If we're in a DIFFERENT runtime, block_in_place still works safely + // (it just moves the work off the current worker thread before blocking) + // + // This approach is compatible with all tokio versions and doesn't require + // runtime identity comparison (Handle::id() is unstable feature). + let should_use_block_in_place = Handle::try_current() + .ok() + .map(|current| { + // Only use block_in_place for multi-threaded runtimes + // (block_in_place panics on current-thread runtime) + !matches!( + current.runtime_flavor(), + tokio::runtime::RuntimeFlavor::CurrentThread + ) + }) + .unwrap_or(false); + + if should_use_block_in_place { + // We're in a multi-threaded runtime context. + // Use block_in_place to: + // 1. Move the blocking operation off the async worker thread + // 2. Prevent potential deadlock if this is the same runtime + // 3. Allow other tasks to continue executing tokio::task::block_in_place(move || handle.block_on(f)) } else { + // Safe to call block_on directly in these cases: + // - We're outside any runtime context + // - We're in a current-thread runtime (where block_in_place doesn't work) handle.block_on(f) } } diff --git a/crates/database/interface/src/bal.rs b/crates/database/interface/src/bal.rs new file mode 100644 index 0000000000..e97768934a --- /dev/null +++ b/crates/database/interface/src/bal.rs @@ -0,0 +1,391 @@ +//! Database implementation for BAL. +use core::{ + error::Error, + fmt::Display, + ops::{Deref, DerefMut}, +}; +use primitives::{Address, StorageKey, StorageValue, B256}; +use state::{ + bal::{alloy::AlloyBal, Bal, BalError}, + Account, AccountInfo, Bytecode, EvmState, +}; +use std::sync::Arc; + +use crate::{DBErrorMarker, Database, DatabaseCommit}; + +/// Contains both the BAL for reads and BAL builders. +#[derive(Clone, Default, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct BalState { + /// BAL used to execute transactions. + pub bal: Option>, + /// BAL builder that is used to build BAL. + /// It is create from State output of transaction execution. + pub bal_builder: Option, + /// BAL index, used by bal to fetch appropriate values and used by bal_builder on commit + /// to submit changes. + pub bal_index: u64, +} + +impl BalState { + /// Create a new BAL manager. + #[inline] + pub fn new() -> Self { + Self::default() + } + + /// Reset BAL index. + #[inline] + pub fn reset_bal_index(&mut self) { + self.bal_index = 0; + } + + /// Bump BAL index. + #[inline] + pub fn bump_bal_index(&mut self) { + self.bal_index += 1; + } + + /// Get BAL index. + #[inline] + pub fn bal_index(&self) -> u64 { + self.bal_index + } + + /// Get BAL. + #[inline] + pub fn bal(&self) -> Option> { + self.bal.clone() + } + + /// Get BAL builder. + #[inline] + pub fn bal_builder(&self) -> Option { + self.bal_builder.clone() + } + + /// Set BAL. + #[inline] + pub fn with_bal(mut self, bal: Arc) -> Self { + self.bal = Some(bal); + self + } + + /// Set BAL builder. + #[inline] + pub fn with_bal_builder(mut self) -> Self { + self.bal_builder = Some(Bal::new()); + self + } + + /// Take BAL builder. + #[inline] + pub fn take_built_bal(&mut self) -> Option { + self.reset_bal_index(); + self.bal_builder.take() + } + + /// Take built BAL as AlloyBAL. + #[inline] + pub fn take_built_alloy_bal(&mut self) -> Option { + self.take_built_bal().map(|bal| bal.into_alloy_bal()) + } + + /// Get account id from BAL. + /// + /// Return Error if BAL is not found and Account is not + #[inline] + pub fn get_account_id(&self, address: &Address) -> Result, BalError> { + self.bal + .as_ref() + .map(|bal| { + bal.accounts + .get_full(address) + .map(|i| i.0) + .ok_or(BalError::AccountNotFound) + }) + .transpose() + } + + /// Fetch account from database and apply bal changes to it. + /// + /// Return Some if BAL is existing, None if not. + /// Return Err if Accounts is not found inside BAL. + /// And return true + #[inline] + pub fn basic( + &self, + address: Address, + basic: &mut Option, + ) -> Result { + let Some(account_id) = self.get_account_id(&address)? else { + return Ok(false); + }; + Ok(self.basic_by_account_id(account_id, basic)) + } + + /// Fetch account from database and apply bal changes to it by account id. + /// + /// Panics if account_id is invalid + #[inline] + pub fn basic_by_account_id(&self, account_id: usize, basic: &mut Option) -> bool { + if let Some(bal) = &self.bal { + let is_none = basic.is_none(); + let mut bal_basic = core::mem::take(basic).unwrap_or_default(); + bal.populate_account_info(account_id, self.bal_index, &mut bal_basic) + .expect("Invalid account id"); + + // if it is not changed, check if it is none and return it. + if is_none { + return true; + } + + *basic = Some(bal_basic); + return true; + } + false + } + + /// Get storage value from BAL. + /// + /// Return Err if bal is present but account or storage is not found inside BAL. + #[inline] + pub fn storage( + &self, + account: &Address, + storage_key: StorageKey, + ) -> Result, BalError> { + let Some(bal) = &self.bal else { + return Ok(None); + }; + + let Some(bal_account) = bal.accounts.get(account) else { + return Err(BalError::AccountNotFound); + }; + + Ok(bal_account + .storage + .get_bal_writes(storage_key)? + .get(self.bal_index)) + } + + /// Get the storage value by account id. + /// + /// Return Err if bal is present but account or storage is not found inside BAL. + /// + /// + #[inline] + pub fn storage_by_account_id( + &self, + account_id: usize, + storage_key: StorageKey, + ) -> Result, BalError> { + let Some(bal) = &self.bal else { + return Ok(None); + }; + + let Some((_, bal_account)) = bal.accounts.get_index(account_id) else { + return Err(BalError::AccountNotFound); + }; + + Ok(bal_account + .storage + .get_bal_writes(storage_key)? + .get(self.bal_index)) + } + + /// Apply changed from EvmState to the bal_builder + #[inline] + pub fn commit(&mut self, changes: &EvmState) { + if let Some(bal_builder) = &mut self.bal_builder { + for (address, account) in changes.iter() { + bal_builder.update_account(self.bal_index, *address, account); + } + } + } + + /// Commit one account to the BAL builder. + #[inline] + pub fn commit_one(&mut self, address: Address, account: &Account) { + if let Some(bal_builder) = &mut self.bal_builder { + bal_builder.update_account(self.bal_index, address, account); + } + } +} + +/// Database implementation for BAL. +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct BalDatabase { + /// BAL manager. + pub bal_state: BalState, + /// Database. + pub db: DB, +} + +impl Deref for BalDatabase { + type Target = DB; + + fn deref(&self) -> &Self::Target { + &self.db + } +} + +impl DerefMut for BalDatabase { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.db + } +} + +impl BalDatabase { + /// Create a new BAL database. + #[inline] + pub fn new(db: DB) -> Self { + Self { + bal_state: BalState::default(), + db, + } + } + + /// With BAL. + #[inline] + pub fn with_bal_option(self, bal: Option>) -> Self { + Self { + bal_state: BalState { + bal, + ..self.bal_state + }, + ..self + } + } + + /// With BAL builder. + #[inline] + pub fn with_bal_builder(self) -> Self { + Self { + bal_state: self.bal_state.with_bal_builder(), + ..self + } + } + + /// Reset BAL index. + #[inline] + pub fn reset_bal_index(mut self) -> Self { + self.bal_state.reset_bal_index(); + self + } + + /// Bump BAL index. + #[inline] + pub fn bump_bal_index(&mut self) { + self.bal_state.bump_bal_index(); + } +} + +/// Error type from database. +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum EvmDatabaseError { + /// BAL error. + Bal(BalError), + /// External database error. + Database(ERROR), +} + +impl From for EvmDatabaseError { + fn from(error: BalError) -> Self { + Self::Bal(error) + } +} + +impl DBErrorMarker for EvmDatabaseError {} + +impl Display for EvmDatabaseError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Bal(error) => write!(f, "Bal error: {error}"), + Self::Database(error) => write!(f, "Database error: {error}"), + } + } +} + +impl Error for EvmDatabaseError {} + +impl EvmDatabaseError { + /// Convert BAL database error to database error. + /// + /// Panics if BAL error is present. + pub fn into_external_error(self) -> ERROR { + match self { + Self::Bal(_) => panic!("Expected database error, got BAL error"), + Self::Database(error) => error, + } + } +} + +impl Database for BalDatabase { + type Error = EvmDatabaseError; + + #[inline] + fn basic(&mut self, address: Address) -> Result, Self::Error> { + let account_id = self.bal_state.get_account_id(&address)?; + + let mut account = self.db.basic(address).map_err(EvmDatabaseError::Database)?; + + if let Some(account_id) = account_id { + self.bal_state.basic_by_account_id(account_id, &mut account); + } + + Ok(account) + } + + #[inline] + fn code_by_hash(&mut self, code_hash: B256) -> Result { + self.db + .code_by_hash(code_hash) + .map_err(EvmDatabaseError::Database) + } + + #[inline] + fn storage(&mut self, address: Address, key: StorageKey) -> Result { + if let Some(storage) = self.bal_state.storage(&address, key)? { + return Ok(storage); + } + + self.db + .storage(address, key) + .map_err(EvmDatabaseError::Database) + } + + #[inline] + fn storage_by_account_id( + &mut self, + address: Address, + account_id: usize, + storage_key: StorageKey, + ) -> Result { + if let Some(value) = self + .bal_state + .storage_by_account_id(account_id, storage_key)? + { + return Ok(value); + } + + self.db + .storage(address, storage_key) + .map_err(EvmDatabaseError::Database) + } + + fn block_hash(&mut self, number: u64) -> Result { + self.db + .block_hash(number) + .map_err(EvmDatabaseError::Database) + } +} + +impl DatabaseCommit for BalDatabase { + fn commit(&mut self, changes: EvmState) { + self.bal_state.commit(&changes); + self.db.commit(changes); + } +} diff --git a/crates/database/interface/src/either.rs b/crates/database/interface/src/either.rs index a22210cb4b..ce1e7d1336 100644 --- a/crates/database/interface/src/either.rs +++ b/crates/database/interface/src/either.rs @@ -43,6 +43,18 @@ where Self::Right(db) => db.block_hash(number), } } + + fn storage_by_account_id( + &mut self, + address: Address, + account_id: usize, + storage_key: StorageKey, + ) -> Result { + match self { + Self::Left(db) => db.storage_by_account_id(address, account_id, storage_key), + Self::Right(db) => db.storage_by_account_id(address, account_id, storage_key), + } + } } impl DatabaseCommit for Either @@ -96,4 +108,16 @@ where Self::Right(db) => db.block_hash_ref(number), } } + + fn storage_by_account_id_ref( + &self, + address: Address, + account_id: usize, + storage_key: StorageKey, + ) -> Result { + match self { + Self::Left(db) => db.storage_by_account_id_ref(address, account_id, storage_key), + Self::Right(db) => db.storage_by_account_id_ref(address, account_id, storage_key), + } + } } diff --git a/crates/database/interface/src/empty_db.rs b/crates/database/interface/src/empty_db.rs index 455dcf35e6..8898e7a4ed 100644 --- a/crates/database/interface/src/empty_db.rs +++ b/crates/database/interface/src/empty_db.rs @@ -1,6 +1,5 @@ //! Empty database implementation. use crate::{DBErrorMarker, Database, DatabaseRef}; -use core::error::Error; use core::{convert::Infallible, fmt, marker::PhantomData}; use primitives::{keccak256, Address, StorageKey, StorageValue, B256}; use state::{AccountInfo, Bytecode}; @@ -55,7 +54,7 @@ impl EmptyDBTyped { } } -impl Database for EmptyDBTyped { +impl Database for EmptyDBTyped { type Error = E; #[inline] @@ -83,7 +82,9 @@ impl Database for EmptyDBTyped { } } -impl DatabaseRef for EmptyDBTyped { +impl DatabaseRef + for EmptyDBTyped +{ type Error = E; #[inline] diff --git a/crates/database/interface/src/erased_error.rs b/crates/database/interface/src/erased_error.rs new file mode 100644 index 0000000000..b5f6cf4cbd --- /dev/null +++ b/crates/database/interface/src/erased_error.rs @@ -0,0 +1,21 @@ +//! Erased error type. + +use std::boxed::Box; + +/// Erased error type. +#[derive(thiserror::Error, Debug)] +#[error(transparent)] +pub struct ErasedError(Box); + +impl ErasedError { + /// Creates a new erased error. + pub fn new(error: impl core::error::Error + Send + Sync + 'static) -> Self { + Self(Box::new(error)) + } + + /// Consumes the erased error and returns the inner error. + #[inline] + pub fn into_inner(self) -> Box { + self.0 + } +} diff --git a/crates/database/interface/src/lib.rs b/crates/database/interface/src/lib.rs index 76e816b3e2..140d0298ef 100644 --- a/crates/database/interface/src/lib.rs +++ b/crates/database/interface/src/lib.rs @@ -8,10 +8,9 @@ extern crate alloc as std; use core::convert::Infallible; use auto_impl::auto_impl; -use core::error::Error; use primitives::{address, Address, HashMap, StorageKey, StorageValue, B256, U256}; use state::{Account, AccountInfo, Bytecode}; -use std::string::String; +use std::vec::Vec; /// Address with all `0xff..ff` in it. Used for testing. pub const FFADDRESS: Address = address!("0xffffffffffffffffffffffffffffffffffffffff"); @@ -30,28 +29,30 @@ pub const BENCH_CALLER_BALANCE: U256 = TEST_BALANCE; #[cfg(feature = "asyncdb")] pub mod async_db; +pub mod bal; pub mod either; pub mod empty_db; +pub mod erased_error; pub mod try_commit; #[cfg(feature = "asyncdb")] pub use async_db::{DatabaseAsync, WrapDatabaseAsync}; pub use empty_db::{EmptyDB, EmptyDBTyped}; +pub use erased_error::ErasedError; pub use try_commit::{ArcUpgradeError, TryDatabaseCommit}; /// Database error marker is needed to implement From conversion for Error type. -pub trait DBErrorMarker {} +pub trait DBErrorMarker: core::error::Error + Send + Sync + 'static {} /// Implement marker for `()`. -impl DBErrorMarker for () {} impl DBErrorMarker for Infallible {} -impl DBErrorMarker for String {} +impl DBErrorMarker for ErasedError {} /// EVM database interface. #[auto_impl(&mut, Box)] pub trait Database { /// The database error type. - type Error: DBErrorMarker + Error; + type Error: DBErrorMarker; /// Gets basic account information. fn basic(&mut self, address: Address) -> Result, Self::Error>; @@ -63,15 +64,66 @@ pub trait Database { fn storage(&mut self, address: Address, index: StorageKey) -> Result; + /// Gets storage value of account by its id. By default call [`Database::storage`] method. + /// + /// If basic account sets account_id inside [`AccountInfo::account_id`], evm will call this + /// function with that given account_id. This can be useful if IndexMap is used to get faster access to the account. + #[inline] + fn storage_by_account_id( + &mut self, + address: Address, + account_id: usize, + storage_key: StorageKey, + ) -> Result { + let _ = account_id; + self.storage(address, storage_key) + } + /// Gets block hash by block number. fn block_hash(&mut self, number: u64) -> Result; } /// EVM database commit interface. +/// +/// # Dyn Compatibility +/// +/// This trait is dyn-compatible. The `commit_iter` method uses `&mut dyn Iterator` +/// which allows it to be called on trait objects while remaining in the vtable. #[auto_impl(&mut, Box)] pub trait DatabaseCommit { /// Commit changes to the database. fn commit(&mut self, changes: HashMap); + + /// Commit changes to the database with an iterator. + /// + /// Implementors of [`DatabaseCommit`] should override this method when possible for efficiency. + /// + /// Callers should prefer using [`DatabaseCommit::commit`] when they already have a [`HashMap`]. + /// + /// # Dyn Compatibility + /// + /// This method uses `&mut dyn Iterator` to remain object-safe and callable on trait objects. + /// For ergonomic usage with `impl IntoIterator`, use the inherent method + /// `commit_from_iter` on `dyn DatabaseCommit`. + fn commit_iter(&mut self, changes: &mut dyn Iterator) { + let changes: HashMap = changes.collect(); + self.commit(changes); + } +} + +/// Inherent implementation for `dyn DatabaseCommit` trait objects. +/// +/// This provides `commit_from_iter` as an ergonomic wrapper around the trait's +/// `commit_iter` method, accepting `impl IntoIterator` for convenience. +impl dyn DatabaseCommit { + /// Commit changes to the database with an iterator. + /// + /// This is an ergonomic wrapper that accepts `impl IntoIterator` and delegates + /// to the trait's [`commit_iter`](DatabaseCommit::commit_iter) method. + #[inline] + pub fn commit_from_iter(&mut self, changes: impl IntoIterator) { + self.commit_iter(&mut changes.into_iter()) + } } /// EVM database interface. @@ -83,7 +135,7 @@ pub trait DatabaseCommit { #[auto_impl(&, &mut, Box, Rc, Arc)] pub trait DatabaseRef { /// The database error type. - type Error: DBErrorMarker + Error; + type Error: DBErrorMarker; /// Gets basic account information. fn basic_ref(&self, address: Address) -> Result, Self::Error>; @@ -95,6 +147,20 @@ pub trait DatabaseRef { fn storage_ref(&self, address: Address, index: StorageKey) -> Result; + /// Gets storage value of account by its id. + /// + /// Default implementation is to call [`DatabaseRef::storage_ref`] method. + #[inline] + fn storage_by_account_id_ref( + &self, + address: Address, + account_id: usize, + storage_key: StorageKey, + ) -> Result { + let _ = account_id; + self.storage_ref(address, storage_key) + } + /// Gets block hash by block number. fn block_hash_ref(&self, number: u64) -> Result; } @@ -172,3 +238,122 @@ impl DatabaseRef for WrapDatabaseRef { self.0.block_hash_ref(number) } } + +impl DatabaseCommitExt for T { + // default implementation +} + +/// EVM database commit interface. +pub trait DatabaseCommitExt: Database + DatabaseCommit { + /// Iterates over received balances and increment all account balances. + /// + /// Update will create transitions for all accounts that are updated. + fn increment_balances( + &mut self, + balances: impl IntoIterator, + ) -> Result<(), Self::Error> { + // Make transition and update cache state + let transitions = balances + .into_iter() + .map(|(address, balance)| { + let mut original_account = match self.basic(address)? { + Some(acc_info) => Account::from(acc_info), + None => Account::new_not_existing(0), + }; + original_account.info.balance = original_account + .info + .balance + .saturating_add(U256::from(balance)); + original_account.mark_touch(); + Ok((address, original_account)) + }) + // Unfortunately must collect here to short circuit on error + .collect::, _>>()?; + + self.commit_iter(&mut transitions.into_iter()); + Ok(()) + } + + /// Drains balances from given account and return those values. + /// + /// It is used for DAO hardfork state change to move values from given accounts. + fn drain_balances( + &mut self, + addresses: impl IntoIterator, + ) -> Result, Self::Error> { + // Make transition and update cache state + let addresses_iter = addresses.into_iter(); + let (lower, _) = addresses_iter.size_hint(); + let mut transitions = Vec::with_capacity(lower); + let balances = addresses_iter + .map(|address| { + let mut original_account = match self.basic(address)? { + Some(acc_info) => Account::from(acc_info), + None => Account::new_not_existing(0), + }; + let balance = core::mem::take(&mut original_account.info.balance); + original_account.mark_touch(); + transitions.push((address, original_account)); + Ok(balance.try_into().unwrap()) + }) + .collect::, _>>()?; + + self.commit_iter(&mut transitions.into_iter()); + Ok(balances) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + /// Compile-time test that DatabaseCommit is dyn-compatible. + /// This mirrors Foundry's approach: `struct _ObjectSafe(dyn DatabaseExt);` + struct _DatabaseCommitObjectSafe(dyn DatabaseCommit); + + /// Test that dyn DatabaseCommit works correctly. + #[test] + fn test_dyn_database_commit() { + use std::collections::HashMap as StdHashMap; + + struct MockDb { + commits: Vec>, + } + + impl DatabaseCommit for MockDb { + fn commit(&mut self, changes: HashMap) { + let std_map: StdHashMap<_, _> = changes.into_iter().collect(); + self.commits.push(std_map); + } + } + + let mut db = MockDb { commits: vec![] }; + + // Test commit_iter on concrete types + let items: Vec<(Address, Account)> = vec![]; + db.commit_iter(&mut items.into_iter()); + assert_eq!(db.commits.len(), 1); + + // Test commit() on trait objects + { + let db_dyn: &mut dyn DatabaseCommit = &mut db; + db_dyn.commit(HashMap::default()); + } + assert_eq!(db.commits.len(), 2); + + // Test commit_iter on trait objects (now works directly!) + { + let db_dyn: &mut dyn DatabaseCommit = &mut db; + let items: Vec<(Address, Account)> = vec![]; + db_dyn.commit_iter(&mut items.into_iter()); + } + assert_eq!(db.commits.len(), 3); + + // Test ergonomic commit_from_iter on trait objects + { + let db_dyn: &mut dyn DatabaseCommit = &mut db; + db_dyn.commit_from_iter(vec![]); + } + assert_eq!(db.commits.len(), 4); + } +} diff --git a/crates/database/src/alloydb.rs b/crates/database/src/alloydb.rs index 5a0234ba72..06a75edf37 100644 --- a/crates/database/src/alloydb.rs +++ b/crates/database/src/alloydb.rs @@ -12,23 +12,42 @@ use primitives::{Address, StorageKey, StorageValue, B256}; use state::{AccountInfo, Bytecode}; use std::fmt::Display; -/// Error type for transport-related database operations. +/// Error type for AlloyDB database operations. #[derive(Debug)] -pub struct DBTransportError(pub TransportError); +pub enum AlloyDBError { + /// Transport error from the underlying provider. + Transport(TransportError), + /// Block not found for the given block number. + /// + /// This can occur when: + /// - The node has pruned the block data + /// - Using a light client that doesn't have the block + BlockNotFound(u64), +} -impl DBErrorMarker for DBTransportError {} +impl DBErrorMarker for AlloyDBError {} -impl Display for DBTransportError { +impl Display for AlloyDBError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "Transport error: {}", self.0) + match self { + Self::Transport(err) => write!(f, "Transport error: {err}"), + Self::BlockNotFound(number) => write!(f, "Block not found: {number}"), + } } } -impl Error for DBTransportError {} +impl Error for AlloyDBError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + Self::Transport(err) => Some(err), + Self::BlockNotFound(_) => None, + } + } +} -impl From for DBTransportError { +impl From for AlloyDBError { fn from(e: TransportError) -> Self { - Self(e) + Self::Transport(e) } } @@ -61,7 +80,7 @@ impl> AlloyDB { } impl> DatabaseAsyncRef for AlloyDB { - type Error = DBTransportError; + type Error = AlloyDBError; async fn basic_async_ref(&self, address: Address) -> Result, Self::Error> { let nonce = self @@ -93,8 +112,11 @@ impl> DatabaseAsyncRef for AlloyDB { // SAFETY: We know number <= u64::MAX, so we can safely convert it to u64 .get_block_by_number(number.into()) .await?; - // SAFETY: If the number is given, the block is supposed to be finalized, so unwrapping is safe. - Ok(B256::new(*block.unwrap().header().hash())) + + match block { + Some(block) => Ok(B256::new(*block.header().hash())), + None => Err(AlloyDBError::BlockNotFound(number)), + } } async fn code_by_hash_async_ref(&self, _code_hash: B256) -> Result { diff --git a/crates/database/src/in_memory_db.rs b/crates/database/src/in_memory_db.rs index 1437b8c024..16166853fe 100644 --- a/crates/database/src/in_memory_db.rs +++ b/crates/database/src/in_memory_db.rs @@ -4,7 +4,8 @@ use database_interface::{ BENCH_TARGET, BENCH_TARGET_BALANCE, }; use primitives::{ - hash_map::Entry, Address, HashMap, Log, StorageKey, StorageValue, B256, KECCAK_EMPTY, U256, + hash_map::Entry, Address, AddressMap, B256Map, HashMap, Log, StorageKey, StorageValue, B256, + KECCAK_EMPTY, U256, }; use state::{Account, AccountInfo, Bytecode}; use std::vec::Vec; @@ -22,9 +23,9 @@ pub type InMemoryDB = CacheDB; pub struct Cache { /// Account info where None means it is not existing. Not existing state is needed for Pre TANGERINE forks. /// `code` is always `None`, and bytecode can be found in `contracts`. - pub accounts: HashMap, + pub accounts: AddressMap, /// Tracks all contracts by their code hash. - pub contracts: HashMap, + pub contracts: B256Map, /// All logs that were committed via [DatabaseCommit::commit]. pub logs: Vec, /// All cached block hashes from the [DatabaseRef]. @@ -84,6 +85,7 @@ impl CacheDB> { block_hashes, }, db: mut inner, + .. } = self; inner.cache.accounts.extend(accounts); @@ -188,6 +190,93 @@ impl CacheDB { account.storage = storage.into_iter().collect(); Ok(()) } + + /// Pretty print the cache DB for debugging purposes. + #[cfg(feature = "std")] + pub fn pretty_print(&self) -> String { + let mut output = String::new(); + output.push_str("CacheDB:\n"); + output.push_str(&format!( + " accounts: {} total\n", + self.cache.accounts.len() + )); + + // Sort accounts by address for deterministic output + let mut accounts: Vec<_> = self.cache.accounts.iter().collect(); + accounts.sort_by_key(|(addr, _)| *addr); + + for (address, db_account) in accounts { + output.push_str(&format!(" [{address}]:\n")); + output.push_str(&format!(" state: {:?}\n", db_account.account_state)); + + if let Some(info) = db_account.info() { + output.push_str(&format!(" balance: {}\n", info.balance)); + output.push_str(&format!(" nonce: {}\n", info.nonce)); + output.push_str(&format!(" code_hash: {}\n", info.code_hash)); + + if let Some(code) = info.code { + if !code.is_empty() { + output.push_str(&format!(" code: {} bytes\n", code.len())); + } + } + } else { + output.push_str(" account: None (not existing)\n"); + } + + if !db_account.storage.is_empty() { + output.push_str(&format!( + " storage: {} slots\n", + db_account.storage.len() + )); + let mut storage: Vec<_> = db_account.storage.iter().collect(); + storage.sort_by_key(|(k, _)| *k); + for (key, value) in storage { + output.push_str(&format!(" [{key:#x}]: {value:#x}\n")); + } + } + } + + if !self.cache.contracts.is_empty() { + output.push_str(&format!( + " contracts: {} total\n", + self.cache.contracts.len() + )); + let mut contracts: Vec<_> = self.cache.contracts.iter().collect(); + contracts.sort_by_key(|(h, _)| *h); + for (hash, bytecode) in contracts { + output.push_str(&format!(" [{hash}]: {} bytes\n", bytecode.len())); + } + } + + // Print logs in detail: index, address and number of topics. + if !self.cache.logs.is_empty() { + output.push_str(&format!(" logs: {} total\n", self.cache.logs.len())); + for (i, log) in self.cache.logs.iter().enumerate() { + // Print address and topics count. We avoid printing raw data to keep output compact. + output.push_str(&format!( + " [{i}]: address: {:?}, topics: {}\n", + log.address, + log.data.topics().len() + )); + } + } + + // Print block_hashes entries (sorted by block number) with their hash. + if !self.cache.block_hashes.is_empty() { + output.push_str(&format!( + " block_hashes: {} total\n", + self.cache.block_hashes.len() + )); + let mut block_hashes: Vec<_> = self.cache.block_hashes.iter().collect(); + block_hashes.sort_by_key(|(num, _)| *num); + for (num, hash) in block_hashes { + output.push_str(&format!(" [{num}]: {hash}\n")); + } + } + + output.push_str("}\n"); + output + } } impl DatabaseCommit for CacheDB { @@ -468,6 +557,7 @@ impl Database for BenchmarkDB { balance: BENCH_TARGET_BALANCE, code: Some(self.0.clone()), code_hash: self.1, + ..Default::default() })); } if address == BENCH_CALLER { @@ -476,14 +566,19 @@ impl Database for BenchmarkDB { balance: BENCH_CALLER_BALANCE, code: None, code_hash: KECCAK_EMPTY, + ..Default::default() })); } Ok(None) } /// Get account code by its hash - fn code_by_hash(&mut self, _code_hash: B256) -> Result { - Ok(Bytecode::default()) + fn code_by_hash(&mut self, code_hash: B256) -> Result { + if code_hash == self.1 { + Ok(self.0.clone()) + } else { + Ok(Bytecode::default()) + } } /// Get storage value of address at index. @@ -560,6 +655,49 @@ mod tests { assert_eq!(new_state.storage(account, key1), Ok(value1)); } + #[cfg(feature = "std")] + #[test] + fn test_pretty_print_cachedb() { + use primitives::{Bytes, Log, LogData, B256, U256}; + + let account = Address::with_last_byte(55); + let mut cachedb = CacheDB::new(EmptyDB::default()); + cachedb.insert_account_info( + account, + AccountInfo { + nonce: 7, + ..Default::default() + }, + ); + let key = StorageKey::from(1); + let value = StorageValue::from(2); + cachedb.insert_account_storage(account, key, value).unwrap(); + + // Add a log entry + let log = Log { + address: account, + data: LogData::new(Vec::new(), Bytes::from(vec![0x01u8])) + .expect("LogData should have <=4 topics"), + }; + cachedb.cache.logs.push(log); + + // Add a block hash entry + cachedb + .cache + .block_hashes + .insert(U256::from(123u64), B256::from([1u8; 32])); + + let s = cachedb.pretty_print(); + assert!(s.contains("CacheDB:")); + assert!(s.contains("accounts: 1 total")); + // storage line is expected to be present for the account + assert!(s.contains("storage: 1 slots")); + + // logs and block_hashes should be reported with counts + assert!(s.contains("logs: 1 total")); + assert!(s.contains("block_hashes: 1 total")); + } + #[cfg(feature = "serde")] #[test] fn test_serialize_deserialize_cachedb() { diff --git a/crates/database/src/lib.rs b/crates/database/src/lib.rs index a1f6f5c543..ab1574a7c9 100644 --- a/crates/database/src/lib.rs +++ b/crates/database/src/lib.rs @@ -15,7 +15,7 @@ pub mod in_memory_db; pub mod states; #[cfg(feature = "alloydb")] -pub use alloydb::{AlloyDB, BlockId, DBTransportError}; +pub use alloydb::{AlloyDB, AlloyDBError, BlockId}; pub use in_memory_db::*; pub use states::{ diff --git a/crates/database/src/states/bundle_state.rs b/crates/database/src/states/bundle_state.rs index 79e9ad6b1f..8982706132 100644 --- a/crates/database/src/states/bundle_state.rs +++ b/crates/database/src/states/bundle_state.rs @@ -397,7 +397,7 @@ impl BundleRetention { /// /// This is needed to decide if there were any changes to the account. /// -/// Reverts and created when TransitionState is applied to BundleState. +/// Changes are applied and reverts are created when TransitionState is applied to BundleState. /// /// And can be used to revert BundleState to the state before transition. #[derive(Default, Clone, Debug, PartialEq, Eq)] @@ -872,8 +872,7 @@ mod tests { let acc1 = AccountInfo { balance: U256::from(10), nonce: 1, - code_hash: KECCAK_EMPTY, - code: None, + ..Default::default() }; let mut bundle_state = BundleState::default(); @@ -923,8 +922,7 @@ mod tests { Some(AccountInfo { nonce: 1, balance: U256::from(10), - code_hash: KECCAK_EMPTY, - code: None, + ..Default::default() }), HashMap::from_iter([ (slot1(), (StorageValue::from(0), StorageValue::from(10))), @@ -937,8 +935,7 @@ mod tests { Some(AccountInfo { nonce: 1, balance: U256::from(10), - code_hash: KECCAK_EMPTY, - code: None, + ..Default::default() }), HashMap::default(), ), @@ -968,8 +965,7 @@ mod tests { Some(AccountInfo { nonce: 3, balance: U256::from(20), - code_hash: KECCAK_EMPTY, - code: None, + ..Default::default() }), HashMap::from_iter([(slot1(), (StorageValue::from(0), StorageValue::from(15)))]), )], @@ -978,8 +974,7 @@ mod tests { Some(Some(AccountInfo { nonce: 1, balance: U256::from(10), - code_hash: KECCAK_EMPTY, - code: None, + ..Default::default() })), vec![(slot1(), StorageValue::from(10))], )]], @@ -995,8 +990,7 @@ mod tests { AccountInfo { nonce: 1, balance: U256::from(10), - code_hash: KECCAK_EMPTY, - code: None, + ..Default::default() }, ) .state_storage( @@ -1009,8 +1003,7 @@ mod tests { AccountInfo { nonce: 1, balance: U256::from(10), - code_hash: KECCAK_EMPTY, - code: None, + ..Default::default() }, ) .revert_address(0, account1()) @@ -1028,8 +1021,7 @@ mod tests { AccountInfo { nonce: 3, balance: U256::from(20), - code_hash: KECCAK_EMPTY, - code: None, + ..Default::default() }, ) .state_storage( @@ -1043,8 +1035,7 @@ mod tests { Some(Some(AccountInfo { nonce: 1, balance: U256::from(10), - code_hash: KECCAK_EMPTY, - code: None, + ..Default::default() })), ) .revert_storage(0, account1(), vec![(slot1(), StorageValue::from(10))]) diff --git a/crates/database/src/states/cache.rs b/crates/database/src/states/cache.rs index 7a345cc3bc..8b6870ef9b 100644 --- a/crates/database/src/states/cache.rs +++ b/crates/database/src/states/cache.rs @@ -2,8 +2,8 @@ use super::{ plain_account::PlainStorage, transition_account::TransitionAccount, CacheAccount, PlainAccount, }; use bytecode::Bytecode; -use primitives::{Address, HashMap, B256}; -use state::{Account, AccountInfo, EvmState}; +use primitives::{Address, AddressMap, B256Map, HashMap}; +use state::{Account, AccountInfo}; use std::vec::Vec; /// Cache state contains both modified and original values @@ -17,9 +17,9 @@ use std::vec::Vec; #[derive(Clone, Debug, PartialEq, Eq)] pub struct CacheState { /// Block state account with account state - pub accounts: HashMap, + pub accounts: AddressMap, /// Created contracts - pub contracts: HashMap, + pub contracts: B256Map, /// Has EIP-161 state clear enabled (Spurious Dragon hardfork) pub has_state_clear: bool, } @@ -89,14 +89,34 @@ impl CacheState { } /// Applies output of revm execution and create account transitions that are used to build BundleState. - pub fn apply_evm_state(&mut self, evm_state: EvmState) -> Vec<(Address, TransitionAccount)> { - let mut transitions = Vec::with_capacity(evm_state.len()); - for (address, account) in evm_state { - if let Some(transition) = self.apply_account_state(address, account) { - transitions.push((address, transition)); - } - } - transitions + #[inline] + pub fn apply_evm_state( + &mut self, + evm_state: impl IntoIterator, + inspect: F, + ) -> Vec<(Address, TransitionAccount)> + where + F: FnMut(&Address, &Account), + { + self.apply_evm_state_iter(evm_state, inspect).collect() + } + + /// Applies output of revm execution and creates an iterator of account transitions. + #[inline] + pub(crate) fn apply_evm_state_iter<'a, F, T>( + &'a mut self, + evm_state: T, + mut inspect: F, + ) -> impl Iterator + use<'a, F, T> + where + F: FnMut(&Address, &Account), + T: IntoIterator, + { + evm_state.into_iter().filter_map(move |(address, account)| { + inspect(&address, &account); + self.apply_account_state(address, account) + .map(|transition| (address, transition)) + }) } /// Pretty print the cache state for debugging purposes. @@ -165,7 +185,7 @@ impl CacheState { /// Applies updated account state to the cached account. /// /// Returns account transition if applicable. - fn apply_account_state( + pub(crate) fn apply_account_state( &mut self, address: Address, account: Account, diff --git a/crates/database/src/states/state.rs b/crates/database/src/states/state.rs index 22f8263343..10b7292677 100644 --- a/crates/database/src/states/state.rs +++ b/crates/database/src/states/state.rs @@ -3,13 +3,19 @@ use super::{ CacheAccount, StateBuilder, TransitionAccount, TransitionState, }; use bytecode::Bytecode; -use database_interface::{Database, DatabaseCommit, DatabaseRef, EmptyDB}; +use database_interface::{ + bal::{BalState, EvmDatabaseError}, + Database, DatabaseCommit, DatabaseRef, EmptyDB, +}; use primitives::{hash_map, Address, HashMap, StorageKey, StorageValue, B256, BLOCK_HASH_HISTORY}; -use state::{Account, AccountInfo}; +use state::{ + bal::{alloy::AlloyBal, Bal}, + Account, AccountInfo, +}; use std::{ boxed::Box, collections::{btree_map, BTreeMap}, - vec::Vec, + sync::Arc, }; /// Database boxed with a lifetime and Send @@ -44,13 +50,13 @@ pub struct State { /// /// Build reverts and state that gets applied to the state. pub transition_state: Option, - /// After block is finishes we merge those changes inside bundle + /// After block finishes we merge those changes inside bundle /// /// Bundle is used to update database and create changesets. /// /// Bundle state can be set on initialization if we want to use preloaded bundle. pub bundle_state: BundleState, - /// Addition layer that is going to be used to fetched values before fetching values + /// Additional layer that is going to be used to fetch values before fetching values /// from database /// /// Bundle is the main output of the state execution and this allows setting previous bundle @@ -63,6 +69,10 @@ pub struct State { /// /// The fork block is different or some blocks are not saved inside database. pub block_hashes: BTreeMap, + /// BAL state. + /// + /// Can contain both the BAL for reads and BAL builder that is used to build BAL. + pub bal_state: BalState, } // Have ability to call State::builder without having to specify the type. @@ -81,62 +91,6 @@ impl State { self.bundle_state.size_hint() } - /// Iterates over received balances and increment all account balances. - /// - /// **Note**: If account is not found inside cache state it will be loaded from database. - /// - /// Update will create transitions for all accounts that are updated. - /// - /// If using this to implement withdrawals, zero balances must be filtered out before calling this function. - pub fn increment_balances( - &mut self, - balances: impl IntoIterator, - ) -> Result<(), DB::Error> { - // Make transition and update cache state - let balances = balances.into_iter(); - let mut transitions = Vec::with_capacity(balances.size_hint().0); - for (address, balance) in balances { - if balance == 0 { - continue; - } - let original_account = self.load_cache_account(address)?; - transitions.push(( - address, - original_account - .increment_balance(balance) - .expect("Balance is not zero"), - )) - } - // Append transition - if let Some(s) = self.transition_state.as_mut() { - s.add_transitions(transitions) - } - Ok(()) - } - - /// Drains balances from given account and return those values. - /// - /// It is used for DAO hardfork state change to move values from given accounts. - pub fn drain_balances( - &mut self, - addresses: impl IntoIterator, - ) -> Result, DB::Error> { - // Make transition and update cache state - let mut transitions = Vec::new(); - let mut balances = Vec::new(); - for address in addresses { - let original_account = self.load_cache_account(address)?; - let (balance, transition) = original_account.drain_balance(); - balances.push(balance); - transitions.push((address, transition)) - } - // Append transition - if let Some(s) = self.transition_state.as_mut() { - s.add_transitions(transitions) - } - Ok(balances) - } - /// State clear EIP-161 is enabled in Spurious Dragon hardfork. pub fn set_state_clear_flag(&mut self, has_state_clear: bool) { self.cache.set_state_clear_flag(has_state_clear); @@ -164,7 +118,10 @@ impl State { } /// Applies evm transitions to transition state. - pub fn apply_transition(&mut self, transitions: Vec<(Address, TransitionAccount)>) { + pub fn apply_transition( + &mut self, + transitions: impl IntoIterator, + ) { // Add transition to transition state. if let Some(s) = self.transition_state.as_mut() { s.add_transitions(transitions) @@ -188,16 +145,39 @@ impl State { /// If the account is not found in the cache, it will be loaded from the /// database and inserted into the cache. pub fn load_cache_account(&mut self, address: Address) -> Result<&mut CacheAccount, DB::Error> { - match self.cache.accounts.entry(address) { + Self::load_cache_account_with( + &mut self.cache, + self.use_preloaded_bundle, + &self.bundle_state, + &mut self.database, + address, + ) + } + + /// Get a mutable reference to the [`CacheAccount`] for the given address. + /// + /// If the account is not found in the cache, it will be loaded from the + /// database and inserted into the cache. + /// + /// This function accepts destructed fields of [`Self`] as arguments and + /// returns a cached account with the lifetime of the provided cache reference. + fn load_cache_account_with<'a>( + cache: &'a mut CacheState, + use_preloaded_bundle: bool, + bundle_state: &BundleState, + database: &mut DB, + address: Address, + ) -> Result<&'a mut CacheAccount, DB::Error> { + Ok(match cache.accounts.entry(address) { hash_map::Entry::Vacant(entry) => { - if self.use_preloaded_bundle { + if use_preloaded_bundle { // Load account from bundle state - if let Some(account) = self.bundle_state.account(&address).map(Into::into) { + if let Some(account) = bundle_state.account(&address).map(Into::into) { return Ok(entry.insert(account)); } } // If not found in bundle, load it from database - let info = self.database.basic(address)?; + let info = database.basic(address)?; let account = match info { None => CacheAccount::new_loaded_not_existing(), Some(acc) if acc.is_empty() => { @@ -205,14 +185,14 @@ impl State { } Some(acc) => CacheAccount::new_loaded(acc, HashMap::default()), }; - Ok(entry.insert(account)) + entry.insert(account) } - hash_map::Entry::Occupied(entry) => Ok(entry.into_mut()), - } + hash_map::Entry::Occupied(entry) => entry.into_mut(), + }) } // TODO : Make cache aware of transitions dropping by having global transition counter. - /// Takess the [`BundleState`] changeset from the [`State`], replacing it + /// Takes the [`BundleState`] changeset from the [`State`], replacing it /// with an empty one. /// /// This will not apply any pending [`TransitionState`]. @@ -225,13 +205,99 @@ impl State { pub fn take_bundle(&mut self) -> BundleState { core::mem::take(&mut self.bundle_state) } + + /// Takes build bal from bal state. + #[inline] + pub fn take_built_bal(&mut self) -> Option { + self.bal_state.take_built_bal() + } + + /// Takes built alloy bal from bal state. + #[inline] + pub fn take_built_alloy_bal(&mut self) -> Option { + self.bal_state.take_built_alloy_bal() + } + + /// Bump BAL index. + #[inline] + pub fn bump_bal_index(&mut self) { + self.bal_state.bump_bal_index(); + } + + /// Set BAL index. + #[inline] + pub fn set_bal_index(&mut self, index: u64) { + self.bal_state.bal_index = index; + } + + /// Reset BAL index. + #[inline] + pub fn reset_bal_index(&mut self) { + self.bal_state.reset_bal_index(); + } + + /// Set BAL. + #[inline] + pub fn set_bal(&mut self, bal: Option>) { + self.bal_state.bal = bal; + } + + /// Gets storage value of address at index. + #[inline] + fn storage(&mut self, address: Address, index: StorageKey) -> Result { + // If account is not found in cache, it will be loaded from database. + let account = Self::load_cache_account_with( + &mut self.cache, + self.use_preloaded_bundle, + &self.bundle_state, + &mut self.database, + address, + )?; + + // Account will always be some, but if it is not, StorageValue::ZERO will be returned. + let is_storage_known = account.status.is_storage_known(); + Ok(account + .account + .as_mut() + .map(|account| match account.storage.entry(index) { + hash_map::Entry::Occupied(entry) => Ok(*entry.get()), + hash_map::Entry::Vacant(entry) => { + // If account was destroyed or account is newly built + // we return zero and don't ask database. + let value = if is_storage_known { + StorageValue::ZERO + } else { + self.database.storage(address, index)? + }; + entry.insert(value); + Ok(value) + } + }) + .transpose()? + .unwrap_or_default()) + } } impl Database for State { - type Error = DB::Error; + type Error = EvmDatabaseError; fn basic(&mut self, address: Address) -> Result, Self::Error> { - self.load_cache_account(address).map(|a| a.account_info()) + // if bal is existing but account is not found, error will be returned. + let account_id = self + .bal_state + .get_account_id(&address) + .map_err(EvmDatabaseError::Bal)?; + + let mut basic = self + .load_cache_account(address) + .map(|a| a.account_info()) + .map_err(EvmDatabaseError::Database)?; + // will populate account code if there was a bal change to it. If there is no change + // it will be fetched in code_by_hash. + if let Some(account_id) = account_id { + self.bal_state.basic_by_account_id(account_id, &mut basic); + } + Ok(basic) } fn code_by_hash(&mut self, code_hash: B256) -> Result { @@ -245,7 +311,10 @@ impl Database for State { } } // If not found in bundle ask database - let code = self.database.code_by_hash(code_hash)?; + let code = self + .database + .code_by_hash(code_hash) + .map_err(EvmDatabaseError::Database)?; entry.insert(code.clone()); Ok(code) } @@ -258,40 +327,42 @@ impl Database for State { address: Address, index: StorageKey, ) -> Result { - // Account is guaranteed to be loaded. - // Note that storage from bundle is already loaded with account. - if let Some(account) = self.cache.accounts.get_mut(&address) { - // Account will always be some, but if it is not, StorageValue::ZERO will be returned. - let is_storage_known = account.status.is_storage_known(); - Ok(account - .account - .as_mut() - .map(|account| match account.storage.entry(index) { - hash_map::Entry::Occupied(entry) => Ok(*entry.get()), - hash_map::Entry::Vacant(entry) => { - // If account was destroyed or account is newly built - // we return zero and don't ask database. - let value = if is_storage_known { - StorageValue::ZERO - } else { - self.database.storage(address, index)? - }; - entry.insert(value); - Ok(value) - } - }) - .transpose()? - .unwrap_or_default()) - } else { - unreachable!("For accessing any storage account is guaranteed to be loaded beforehand") + if let Some(storage) = self + .bal_state + .storage(&address, index) + .map_err(EvmDatabaseError::Bal)? + { + // return bal value if it is found + return Ok(storage); + } + self.storage(address, index) + .map_err(EvmDatabaseError::Database) + } + + fn storage_by_account_id( + &mut self, + address: Address, + account_id: usize, + key: StorageKey, + ) -> Result { + if let Some(storage) = self.bal_state.storage_by_account_id(account_id, key)? { + return Ok(storage); } + + self.database + .storage(address, key) + .map_err(EvmDatabaseError::Database) } fn block_hash(&mut self, number: u64) -> Result { match self.block_hashes.entry(number) { btree_map::Entry::Occupied(entry) => Ok(*entry.get()), btree_map::Entry::Vacant(entry) => { - let ret = *entry.insert(self.database.block_hash(number)?); + let ret = *entry.insert( + self.database + .block_hash(number) + .map_err(EvmDatabaseError::Database)?, + ); // Prune all hashes that are older than BLOCK_HASH_HISTORY let last_block = number.saturating_sub(BLOCK_HASH_HISTORY); @@ -310,28 +381,69 @@ impl Database for State { } impl DatabaseCommit for State { - fn commit(&mut self, evm_state: HashMap) { - let transitions = self.cache.apply_evm_state(evm_state); - self.apply_transition(transitions); + fn commit(&mut self, changes: HashMap) { + self.bal_state.commit(&changes); + let transitions = self.cache.apply_evm_state_iter(changes, |_, _| {}); + if let Some(s) = self.transition_state.as_mut() { + s.add_transitions(transitions) + } else { + // Advance the iter to apply all state updates. + transitions.for_each(|_| {}); + } + } + + fn commit_iter(&mut self, changes: &mut dyn Iterator) { + let transitions = self + .cache + .apply_evm_state_iter(changes, |address, account| { + self.bal_state.commit_one(*address, account); + }); + if let Some(s) = self.transition_state.as_mut() { + s.add_transitions(transitions) + } else { + // Advance the iter to apply all state updates. + transitions.for_each(|_| {}); + } } } impl DatabaseRef for State { - type Error = DB::Error; + type Error = EvmDatabaseError; fn basic_ref(&self, address: Address) -> Result, Self::Error> { + // if bal is present and account is not found, error will be returned. + let account_id = self.bal_state.get_account_id(&address)?; + // Account is already in cache + let mut loaded_account = None; if let Some(account) = self.cache.accounts.get(&address) { - return Ok(account.account_info()); - } + loaded_account = Some(account.account_info()); + }; + // If bundle state is used, check if account is in bundle state - if self.use_preloaded_bundle { + if self.use_preloaded_bundle && loaded_account.is_none() { if let Some(account) = self.bundle_state.account(&address) { - return Ok(account.account_info()); + loaded_account = Some(account.account_info()); } } + // If not found, load it from database - self.database.basic_ref(address) + if loaded_account.is_none() { + loaded_account = Some( + self.database + .basic_ref(address) + .map_err(EvmDatabaseError::Database)?, + ); + } + + // safe to unwrap as it in some in condition above + let mut account = loaded_account.unwrap(); + + // if it is inside bal, overwrite the account with the bal changes. + if let Some(account_id) = account_id { + self.bal_state.basic_by_account_id(account_id, &mut account); + } + Ok(account) } fn code_by_hash_ref(&self, code_hash: B256) -> Result { @@ -346,7 +458,9 @@ impl DatabaseRef for State { } } // If not found, load it from database - self.database.code_by_hash_ref(code_hash) + self.database + .code_by_hash_ref(code_hash) + .map_err(EvmDatabaseError::Database) } fn storage_ref( @@ -354,6 +468,11 @@ impl DatabaseRef for State { address: Address, index: StorageKey, ) -> Result { + // if bal has storage value, return it + if let Some(storage) = self.bal_state.storage(&address, index)? { + return Ok(storage); + } + // Check if account is in cache, the account is not guaranteed to be loaded if let Some(account) = self.cache.accounts.get(&address) { if let Some(plain_account) = &account.account { @@ -368,8 +487,11 @@ impl DatabaseRef for State { } } } + // If not found, load it from database - self.database.storage_ref(address, index) + self.database + .storage_ref(address, index) + .map_err(EvmDatabaseError::Database) } fn block_hash_ref(&self, number: u64) -> Result { @@ -377,7 +499,9 @@ impl DatabaseRef for State { return Ok(*entry); } // If not found, load it from database - self.database.block_hash_ref(number) + self.database + .block_hash_ref(number) + .map_err(EvmDatabaseError::Database) } } diff --git a/crates/database/src/states/state_builder.rs b/crates/database/src/states/state_builder.rs index c2047d490c..3148c271b7 100644 --- a/crates/database/src/states/state_builder.rs +++ b/crates/database/src/states/state_builder.rs @@ -1,7 +1,10 @@ use super::{cache::CacheState, state::DBBox, BundleState, State, TransitionState}; -use database_interface::{DBErrorMarker, Database, DatabaseRef, EmptyDB, WrapDatabaseRef}; +use database_interface::{ + bal::BalState, DBErrorMarker, Database, DatabaseRef, EmptyDB, WrapDatabaseRef, +}; use primitives::B256; -use std::collections::BTreeMap; +use state::bal::Bal; +use std::{collections::BTreeMap, sync::Arc}; /// Allows building of State and initializing it with different options. #[derive(Clone, Debug, PartialEq, Eq)] @@ -29,6 +32,8 @@ pub struct StateBuilder { with_background_transition_merge: bool, /// If we want to set different block hashes, with_block_hashes: BTreeMap, + /// BAL state. + bal_state: BalState, } impl StateBuilder { @@ -58,6 +63,7 @@ impl StateBuilder { with_bundle_update: false, with_background_transition_merge: false, with_block_hashes: BTreeMap::new(), + bal_state: BalState::default(), } } @@ -73,6 +79,7 @@ impl StateBuilder { with_bundle_update: self.with_bundle_update, with_background_transition_merge: self.with_background_transition_merge, with_block_hashes: self.with_block_hashes, + bal_state: self.bal_state, } } @@ -85,7 +92,7 @@ impl StateBuilder { } /// With boxed version of database. - pub fn with_database_boxed( + pub fn with_database_boxed( self, database: DBBox<'_, Error>, ) -> StateBuilder> { @@ -158,6 +165,18 @@ impl StateBuilder { } } + /// With BAL. + pub fn with_bal(mut self, bal: Arc) -> Self { + self.bal_state.bal = Some(bal); + self + } + + /// With BAL builder. + pub fn with_bal_builder(mut self) -> Self { + self.bal_state.bal_builder = Some(Bal::new()); + self + } + /// Builds the State with the configured settings. pub fn build(mut self) -> State { let use_preloaded_bundle = if self.with_cache_prestate.is_some() { @@ -175,6 +194,7 @@ impl StateBuilder { bundle_state: self.with_bundle_prestate.unwrap_or_default(), use_preloaded_bundle, block_hashes: self.with_block_hashes, + bal_state: self.bal_state, } } } diff --git a/crates/database/src/states/transition_state.rs b/crates/database/src/states/transition_state.rs index a021f34534..78fdd40e73 100644 --- a/crates/database/src/states/transition_state.rs +++ b/crates/database/src/states/transition_state.rs @@ -1,6 +1,5 @@ use super::TransitionAccount; use primitives::{hash_map::Entry, Address, HashMap}; -use std::vec::Vec; /// State of accounts in transition between transaction executions. #[derive(Clone, Default, Debug, PartialEq, Eq)] @@ -29,7 +28,10 @@ impl TransitionState { /// /// This will insert new [`TransitionAccount`]s, or update existing ones via /// [`update`][TransitionAccount::update]. - pub fn add_transitions(&mut self, transitions: Vec<(Address, TransitionAccount)>) { + pub fn add_transitions( + &mut self, + transitions: impl IntoIterator, + ) { for (address, account) in transitions { match self.transitions.entry(address) { Entry::Occupied(entry) => { diff --git a/crates/ee-tests/src/op_revm_tests.rs b/crates/ee-tests/src/op_revm_tests.rs index 3995bd727c..51530783f1 100644 --- a/crates/ee-tests/src/op_revm_tests.rs +++ b/crates/ee-tests/src/op_revm_tests.rs @@ -11,20 +11,19 @@ use revm::{ result::{ExecutionResult, OutOfGasError}, BlockEnv, CfgEnv, TxEnv, }, - context_interface::result::HaltReason, - database::{BenchmarkDB, EmptyDB, BENCH_CALLER, BENCH_CALLER_BALANCE, BENCH_TARGET}, - handler::system_call::SYSTEM_ADDRESS, - interpreter::{ - gas::{calculate_initial_tx_gas, InitialAndFloorGas}, - Interpreter, InterpreterTypes, + context_interface::{ + cfg::{gas::TOTAL_COST_FLOOR_PER_TOKEN, GasId, GasParams}, + result::HaltReason, }, + database::{BenchmarkDB, EmptyDB, State, BENCH_CALLER, BENCH_CALLER_BALANCE, BENCH_TARGET}, + handler::system_call::SYSTEM_ADDRESS, + interpreter::{gas::InitialAndFloorGas, InterpreterTypes}, precompile::{bls12_381_const, bls12_381_utils, bn254, secp256r1, u64_to_address}, - primitives::{bytes, eip7825, Address, Bytes, Log, TxKind, U256}, + primitives::{address, bytes, eip7702, eip7825, Address, Bytes, Log, TxKind, U256}, state::Bytecode, Context, ExecuteEvm, InspectEvm, Inspector, Journal, SystemCallEvm, }; -use std::path::PathBuf; -use std::vec::Vec; +use std::{path::PathBuf, vec::Vec}; // Re-export the constant for testdata directory path const TESTS_TESTDATA: &str = "tests/op_revm_testdata"; @@ -52,7 +51,7 @@ fn test_deposit_tx() { .source_hash(revm::primitives::B256::from([1u8; 32])) .build_fill(), ) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::HOLOCENE); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::HOLOCENE)); let mut evm = ctx.build_op(); @@ -84,7 +83,7 @@ fn test_halted_deposit_tx() { .source_hash(revm::primitives::B256::from([1u8; 32])) .build_fill(), ) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::HOLOCENE) + .with_cfg(CfgEnv::new_with_spec(OpSpecId::HOLOCENE)) .with_db(BenchmarkDB::new_bytecode(Bytecode::new_legacy( [opcode::POP].into(), ))); @@ -114,19 +113,8 @@ fn p256verify_test_tx( ) -> Context, CfgEnv, EmptyDB, Journal, L1BlockInfo> { const SPEC_ID: OpSpecId = OpSpecId::FJORD; - let is_eip7702_enabled = SPEC_ID >= OpSpecId::ISTHMUS; - let is_eip7623_enabled = SPEC_ID >= OpSpecId::ISTHMUS; - - let InitialAndFloorGas { initial_gas, .. } = calculate_initial_tx_gas( - SPEC_ID.into(), - &[], - false, - is_eip7702_enabled, - is_eip7623_enabled, - 0, - 0, - 0, - ); + let InitialAndFloorGas { initial_gas, .. } = + GasParams::new_spec(SPEC_ID.into()).initial_tx_gas(&[], false, 0, 0, 0); Context::op() .with_tx( @@ -138,7 +126,7 @@ fn p256verify_test_tx( ) .build_fill(), ) - .modify_cfg_chained(|cfg| cfg.spec = SPEC_ID) + .with_cfg(CfgEnv::new_with_spec(SPEC_ID)) } #[test] @@ -157,19 +145,8 @@ fn test_tx_call_p256verify() { #[test] fn test_halted_tx_call_p256verify() { const SPEC_ID: OpSpecId = OpSpecId::FJORD; - let is_eip7702_enabled = SPEC_ID >= OpSpecId::ISTHMUS; - let is_eip7623_enabled = SPEC_ID >= OpSpecId::ISTHMUS; - - let InitialAndFloorGas { initial_gas, .. } = calculate_initial_tx_gas( - SPEC_ID.into(), - &[], - false, - is_eip7702_enabled, - is_eip7623_enabled, - 0, - 0, - 0, - ); + let InitialAndFloorGas { initial_gas, .. } = + GasParams::new_spec(SPEC_ID.into()).initial_tx_gas(&[], false, 0, 0, 0); let original_gas_limit = initial_gas + secp256r1::P256VERIFY_BASE_GAS_FEE; let ctx = Context::op() @@ -182,7 +159,7 @@ fn test_halted_tx_call_p256verify() { ) .build_fill(), ) - .modify_cfg_chained(|cfg| cfg.spec = SPEC_ID); + .with_cfg(CfgEnv::new_with_spec(SPEC_ID)); let mut evm = ctx.build_op(); let output = evm.replay().unwrap(); @@ -203,19 +180,20 @@ fn bn254_pair_test_tx( spec: OpSpecId, ) -> Context, CfgEnv, EmptyDB, Journal, L1BlockInfo> { - let is_eip7702_enabled = spec >= OpSpecId::ISTHMUS; - let is_eip7623_enabled = spec >= OpSpecId::ISTHMUS; let input = Bytes::from([1; GRANITE_MAX_INPUT_SIZE + 2]); - let InitialAndFloorGas { initial_gas, .. } = calculate_initial_tx_gas( - spec.into(), - &input[..], - false, - is_eip7702_enabled, - is_eip7623_enabled, - 0, - 0, - 0, - ); + let mut gas_params = GasParams::new_spec(spec.into()); + if spec >= OpSpecId::ISTHMUS { + gas_params.override_gas([ + ( + GasId::tx_eip7702_per_empty_account_cost(), + eip7702::PER_EMPTY_ACCOUNT_COST, + ), + (GasId::tx_floor_cost_per_token(), TOTAL_COST_FLOOR_PER_TOKEN), + (GasId::tx_floor_cost_base_gas(), 21000), + ]); + } + let InitialAndFloorGas { initial_gas, .. } = + gas_params.initial_tx_gas(&input[..], false, 0, 0, 0); Context::op() .with_tx( @@ -228,7 +206,7 @@ fn bn254_pair_test_tx( ) .build_fill(), ) - .modify_cfg_chained(|cfg| cfg.spec = spec) + .with_cfg(CfgEnv::new_with_spec(spec)) } #[test] @@ -285,7 +263,7 @@ fn test_halted_tx_call_bls12_381_g1_add_out_of_gas() { l1_block.operator_fee_constant = Some(U256::ZERO); l1_block.operator_fee_scalar = Some(U256::ZERO) }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::ISTHMUS); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::ISTHMUS)); let mut evm = ctx.build_op(); @@ -322,7 +300,7 @@ fn test_halted_tx_call_bls12_381_g1_add_input_wrong_size() { l1_block.operator_fee_constant = Some(U256::ZERO); l1_block.operator_fee_scalar = Some(U256::ZERO) }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::ISTHMUS); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::ISTHMUS)); let mut evm = ctx.build_op(); let output = evm.replay().unwrap(); @@ -346,20 +324,18 @@ fn g1_msm_test_tx( ) -> Context, CfgEnv, EmptyDB, Journal, L1BlockInfo> { const SPEC_ID: OpSpecId = OpSpecId::ISTHMUS; - let is_eip7702_enabled = SPEC_ID >= OpSpecId::ISTHMUS; - let is_eip7623_enabled = SPEC_ID >= OpSpecId::ISTHMUS; - let input = Bytes::from([1; bls12_381_const::G1_MSM_INPUT_LENGTH]); - let InitialAndFloorGas { initial_gas, .. } = calculate_initial_tx_gas( - SPEC_ID.into(), - &input[..], - false, - is_eip7702_enabled, - is_eip7623_enabled, - 0, - 0, - 0, - ); + let mut gas_params = GasParams::new_spec(SPEC_ID.into()); + gas_params.override_gas([ + ( + GasId::tx_eip7702_per_empty_account_cost(), + eip7702::PER_EMPTY_ACCOUNT_COST, + ), + (GasId::tx_floor_cost_per_token(), TOTAL_COST_FLOOR_PER_TOKEN), + (GasId::tx_floor_cost_base_gas(), 21000), + ]); + let InitialAndFloorGas { initial_gas, .. } = + gas_params.initial_tx_gas(&input[..], false, 0, 0, 0); let gs1_msm_gas = bls12_381_utils::msm_required_gas( 1, &bls12_381_const::DISCOUNT_TABLE_G1_MSM, @@ -381,26 +357,24 @@ fn g1_msm_test_tx( l1_block.operator_fee_constant = Some(U256::ZERO); l1_block.operator_fee_scalar = Some(U256::ZERO) }) - .modify_cfg_chained(|cfg| cfg.spec = SPEC_ID) + .with_cfg(CfgEnv::new_with_spec(SPEC_ID)) } #[test] fn test_halted_tx_call_bls12_381_g1_msm_input_wrong_size() { const SPEC_ID: OpSpecId = OpSpecId::ISTHMUS; - let is_eip7702_enabled = SPEC_ID >= OpSpecId::ISTHMUS; - let is_eip7623_enabled = SPEC_ID >= OpSpecId::ISTHMUS; - let input = Bytes::from([1; bls12_381_const::G1_MSM_INPUT_LENGTH]); - let InitialAndFloorGas { initial_gas, .. } = calculate_initial_tx_gas( - SPEC_ID.into(), - &input[..], - false, - is_eip7702_enabled, - is_eip7623_enabled, - 0, - 0, - 0, - ); + let mut gas_params = GasParams::new_spec(SPEC_ID.into()); + gas_params.override_gas([ + ( + GasId::tx_eip7702_per_empty_account_cost(), + eip7702::PER_EMPTY_ACCOUNT_COST, + ), + (GasId::tx_floor_cost_per_token(), TOTAL_COST_FLOOR_PER_TOKEN), + (GasId::tx_floor_cost_base_gas(), 21000), + ]); + let InitialAndFloorGas { initial_gas, .. } = + gas_params.initial_tx_gas(&input[..], false, 0, 0, 0); let gs1_msm_gas = bls12_381_utils::msm_required_gas( 1, &bls12_381_const::DISCOUNT_TABLE_G1_MSM, @@ -422,7 +396,7 @@ fn test_halted_tx_call_bls12_381_g1_msm_input_wrong_size() { l1_block.operator_fee_constant = Some(U256::ZERO); l1_block.operator_fee_scalar = Some(U256::ZERO) }) - .modify_cfg_chained(|cfg| cfg.spec = SPEC_ID); + .with_cfg(CfgEnv::new_with_spec(SPEC_ID)); let mut evm = ctx.build_op(); let output = evm.replay().unwrap(); @@ -445,20 +419,18 @@ fn test_halted_tx_call_bls12_381_g1_msm_input_wrong_size() { #[test] fn test_halted_tx_call_bls12_381_g1_msm_out_of_gas() { const SPEC_ID: OpSpecId = OpSpecId::ISTHMUS; - let is_eip7702_enabled = SPEC_ID >= OpSpecId::ISTHMUS; - let is_eip7623_enabled = SPEC_ID >= OpSpecId::ISTHMUS; - let input = Bytes::from([1; bls12_381_const::G1_MSM_INPUT_LENGTH]); - let InitialAndFloorGas { initial_gas, .. } = calculate_initial_tx_gas( - SPEC_ID.into(), - &input[..], - false, - is_eip7702_enabled, - is_eip7623_enabled, - 0, - 0, - 0, - ); + let mut gas_params = GasParams::new_spec(SPEC_ID.into()); + gas_params.override_gas([ + ( + GasId::tx_eip7702_per_empty_account_cost(), + eip7702::PER_EMPTY_ACCOUNT_COST, + ), + (GasId::tx_floor_cost_per_token(), TOTAL_COST_FLOOR_PER_TOKEN), + (GasId::tx_floor_cost_base_gas(), 21000), + ]); + let InitialAndFloorGas { initial_gas, .. } = + gas_params.initial_tx_gas(&input[..], false, 0, 0, 0); let gs1_msm_gas = bls12_381_utils::msm_required_gas( 1, &bls12_381_const::DISCOUNT_TABLE_G1_MSM, @@ -480,7 +452,7 @@ fn test_halted_tx_call_bls12_381_g1_msm_out_of_gas() { l1_block.operator_fee_constant = Some(U256::ZERO); l1_block.operator_fee_scalar = Some(U256::ZERO) }) - .modify_cfg_chained(|cfg| cfg.spec = SPEC_ID); + .with_cfg(CfgEnv::new_with_spec(SPEC_ID)); let mut evm = ctx.build_op(); let output = evm.replay().unwrap(); @@ -538,7 +510,7 @@ fn test_halted_tx_call_bls12_381_g2_add_out_of_gas() { l1_block.operator_fee_constant = Some(U256::ZERO); l1_block.operator_fee_scalar = Some(U256::ZERO) }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::ISTHMUS); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::ISTHMUS)); let mut evm = ctx.build_op(); @@ -575,7 +547,7 @@ fn test_halted_tx_call_bls12_381_g2_add_input_wrong_size() { l1_block.operator_fee_constant = Some(U256::ZERO); l1_block.operator_fee_scalar = Some(U256::ZERO) }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::ISTHMUS); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::ISTHMUS)); let mut evm = ctx.build_op(); @@ -600,20 +572,18 @@ fn g2_msm_test_tx( ) -> Context, CfgEnv, EmptyDB, Journal, L1BlockInfo> { const SPEC_ID: OpSpecId = OpSpecId::ISTHMUS; - let is_eip7702_enabled = SPEC_ID >= OpSpecId::ISTHMUS; - let is_eip7623_enabled = SPEC_ID >= OpSpecId::ISTHMUS; - let input = Bytes::from([1; bls12_381_const::G2_MSM_INPUT_LENGTH]); - let InitialAndFloorGas { initial_gas, .. } = calculate_initial_tx_gas( - SPEC_ID.into(), - &input[..], - false, - is_eip7702_enabled, - is_eip7623_enabled, - 0, - 0, - 0, - ); + let mut gas_params = GasParams::new_spec(SPEC_ID.into()); + gas_params.override_gas([ + ( + GasId::tx_eip7702_per_empty_account_cost(), + eip7702::PER_EMPTY_ACCOUNT_COST, + ), + (GasId::tx_floor_cost_per_token(), TOTAL_COST_FLOOR_PER_TOKEN), + (GasId::tx_floor_cost_base_gas(), 21000), + ]); + let InitialAndFloorGas { initial_gas, .. } = + gas_params.initial_tx_gas(&input[..], false, 0, 0, 0); let gs2_msm_gas = bls12_381_utils::msm_required_gas( 1, &bls12_381_const::DISCOUNT_TABLE_G2_MSM, @@ -635,26 +605,24 @@ fn g2_msm_test_tx( l1_block.operator_fee_constant = Some(U256::ZERO); l1_block.operator_fee_scalar = Some(U256::ZERO) }) - .modify_cfg_chained(|cfg| cfg.spec = SPEC_ID) + .with_cfg(CfgEnv::new_with_spec(SPEC_ID)) } #[test] fn test_halted_tx_call_bls12_381_g2_msm_input_wrong_size() { const SPEC_ID: OpSpecId = OpSpecId::ISTHMUS; - let is_eip7702_enabled = SPEC_ID >= OpSpecId::ISTHMUS; - let is_eip7623_enabled = SPEC_ID >= OpSpecId::ISTHMUS; - let input = Bytes::from([1; bls12_381_const::G2_MSM_INPUT_LENGTH]); - let InitialAndFloorGas { initial_gas, .. } = calculate_initial_tx_gas( - SPEC_ID.into(), - &input[..], - false, - is_eip7702_enabled, - is_eip7623_enabled, - 0, - 0, - 0, - ); + let mut gas_params = GasParams::new_spec(SPEC_ID.into()); + gas_params.override_gas([ + ( + GasId::tx_eip7702_per_empty_account_cost(), + eip7702::PER_EMPTY_ACCOUNT_COST, + ), + (GasId::tx_floor_cost_per_token(), TOTAL_COST_FLOOR_PER_TOKEN), + (GasId::tx_floor_cost_base_gas(), 21000), + ]); + let InitialAndFloorGas { initial_gas, .. } = + gas_params.initial_tx_gas(&input[..], false, 0, 0, 0); let gs2_msm_gas = bls12_381_utils::msm_required_gas( 1, &bls12_381_const::DISCOUNT_TABLE_G2_MSM, @@ -676,7 +644,7 @@ fn test_halted_tx_call_bls12_381_g2_msm_input_wrong_size() { l1_block.operator_fee_constant = Some(U256::ZERO); l1_block.operator_fee_scalar = Some(U256::ZERO) }) - .modify_cfg_chained(|cfg| cfg.spec = SPEC_ID); + .with_cfg(CfgEnv::new_with_spec(SPEC_ID)); let mut evm = ctx.build_op(); let output = evm.replay().unwrap(); @@ -699,20 +667,18 @@ fn test_halted_tx_call_bls12_381_g2_msm_input_wrong_size() { #[test] fn test_halted_tx_call_bls12_381_g2_msm_out_of_gas() { const SPEC_ID: OpSpecId = OpSpecId::ISTHMUS; - let is_eip7702_enabled = SPEC_ID >= OpSpecId::ISTHMUS; - let is_eip7623_enabled = SPEC_ID >= OpSpecId::ISTHMUS; - let input = Bytes::from([1; bls12_381_const::G2_MSM_INPUT_LENGTH]); - let InitialAndFloorGas { initial_gas, .. } = calculate_initial_tx_gas( - SPEC_ID.into(), - &input[..], - false, - is_eip7702_enabled, - is_eip7623_enabled, - 0, - 0, - 0, - ); + let mut gas_params = GasParams::new_spec(SPEC_ID.into()); + gas_params.override_gas([ + ( + GasId::tx_eip7702_per_empty_account_cost(), + eip7702::PER_EMPTY_ACCOUNT_COST, + ), + (GasId::tx_floor_cost_per_token(), TOTAL_COST_FLOOR_PER_TOKEN), + (GasId::tx_floor_cost_base_gas(), 21000), + ]); + let InitialAndFloorGas { initial_gas, .. } = + gas_params.initial_tx_gas(&input[..], false, 0, 0, 0); let gs2_msm_gas = bls12_381_utils::msm_required_gas( 1, &bls12_381_const::DISCOUNT_TABLE_G2_MSM, @@ -734,7 +700,7 @@ fn test_halted_tx_call_bls12_381_g2_msm_out_of_gas() { l1_block.operator_fee_constant = Some(U256::ZERO); l1_block.operator_fee_scalar = Some(U256::ZERO) }) - .modify_cfg_chained(|cfg| cfg.spec = SPEC_ID); + .with_cfg(CfgEnv::new_with_spec(SPEC_ID)); let mut evm = ctx.build_op(); let output = evm.replay().unwrap(); @@ -780,20 +746,18 @@ fn bl12_381_pairing_test_tx( ) -> Context, CfgEnv, EmptyDB, Journal, L1BlockInfo> { const SPEC_ID: OpSpecId = OpSpecId::ISTHMUS; - let is_eip7702_enabled = SPEC_ID >= OpSpecId::ISTHMUS; - let is_eip7623_enabled = SPEC_ID >= OpSpecId::ISTHMUS; - let input = Bytes::from([1; bls12_381_const::PAIRING_INPUT_LENGTH]); - let InitialAndFloorGas { initial_gas, .. } = calculate_initial_tx_gas( - SPEC_ID.into(), - &input[..], - false, - is_eip7702_enabled, - is_eip7623_enabled, - 0, - 0, - 0, - ); + let mut gas_params = GasParams::new_spec(SPEC_ID.into()); + gas_params.override_gas([ + ( + GasId::tx_eip7702_per_empty_account_cost(), + eip7702::PER_EMPTY_ACCOUNT_COST, + ), + (GasId::tx_floor_cost_per_token(), TOTAL_COST_FLOOR_PER_TOKEN), + (GasId::tx_floor_cost_base_gas(), 21000), + ]); + let InitialAndFloorGas { initial_gas, .. } = + gas_params.initial_tx_gas(&input[..], false, 0, 0, 0); let pairing_gas: u64 = bls12_381_const::PAIRING_MULTIPLIER_BASE + bls12_381_const::PAIRING_OFFSET_BASE; @@ -813,26 +777,24 @@ fn bl12_381_pairing_test_tx( l1_block.operator_fee_constant = Some(U256::ZERO); l1_block.operator_fee_scalar = Some(U256::ZERO) }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::ISTHMUS) + .with_cfg(CfgEnv::new_with_spec(OpSpecId::ISTHMUS)) } #[test] fn test_halted_tx_call_bls12_381_pairing_input_wrong_size() { const SPEC_ID: OpSpecId = OpSpecId::ISTHMUS; - let is_eip7702_enabled = SPEC_ID >= OpSpecId::ISTHMUS; - let is_eip7623_enabled = SPEC_ID >= OpSpecId::ISTHMUS; - let input = Bytes::from([1; bls12_381_const::PAIRING_INPUT_LENGTH]); - let InitialAndFloorGas { initial_gas, .. } = calculate_initial_tx_gas( - SPEC_ID.into(), - &input[..], - false, - is_eip7702_enabled, - is_eip7623_enabled, - 0, - 0, - 0, - ); + let mut gas_params = GasParams::new_spec(SPEC_ID.into()); + gas_params.override_gas([ + ( + GasId::tx_eip7702_per_empty_account_cost(), + eip7702::PER_EMPTY_ACCOUNT_COST, + ), + (GasId::tx_floor_cost_per_token(), TOTAL_COST_FLOOR_PER_TOKEN), + (GasId::tx_floor_cost_base_gas(), 21000), + ]); + let InitialAndFloorGas { initial_gas, .. } = + gas_params.initial_tx_gas(&input[..], false, 0, 0, 0); let pairing_gas: u64 = bls12_381_const::PAIRING_MULTIPLIER_BASE + bls12_381_const::PAIRING_OFFSET_BASE; @@ -851,7 +813,7 @@ fn test_halted_tx_call_bls12_381_pairing_input_wrong_size() { l1_block.operator_fee_constant = Some(U256::ZERO); l1_block.operator_fee_scalar = Some(U256::ZERO) }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::ISTHMUS); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::ISTHMUS)); let mut evm = ctx.build_op(); let output = evm.replay().unwrap(); @@ -874,20 +836,18 @@ fn test_halted_tx_call_bls12_381_pairing_input_wrong_size() { #[test] fn test_halted_tx_call_bls12_381_pairing_out_of_gas() { const SPEC_ID: OpSpecId = OpSpecId::ISTHMUS; - let is_eip7702_enabled = SPEC_ID >= OpSpecId::ISTHMUS; - let is_eip7623_enabled = SPEC_ID >= OpSpecId::ISTHMUS; - let input = Bytes::from([1; bls12_381_const::PAIRING_INPUT_LENGTH]); - let InitialAndFloorGas { initial_gas, .. } = calculate_initial_tx_gas( - SPEC_ID.into(), - &input[..], - false, - is_eip7702_enabled, - is_eip7623_enabled, - 0, - 0, - 0, - ); + let mut gas_params = GasParams::new_spec(SPEC_ID.into()); + gas_params.override_gas([ + ( + GasId::tx_eip7702_per_empty_account_cost(), + eip7702::PER_EMPTY_ACCOUNT_COST, + ), + (GasId::tx_floor_cost_per_token(), TOTAL_COST_FLOOR_PER_TOKEN), + (GasId::tx_floor_cost_base_gas(), 21000), + ]); + let InitialAndFloorGas { initial_gas, .. } = + gas_params.initial_tx_gas(&input[..], false, 0, 0, 0); let pairing_gas: u64 = bls12_381_const::PAIRING_MULTIPLIER_BASE + bls12_381_const::PAIRING_OFFSET_BASE; @@ -906,7 +866,7 @@ fn test_halted_tx_call_bls12_381_pairing_out_of_gas() { l1_block.operator_fee_constant = Some(U256::ZERO); l1_block.operator_fee_scalar = Some(U256::ZERO) }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::ISTHMUS); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::ISTHMUS)); let mut evm = ctx.build_op(); let output = evm.replay().unwrap(); @@ -951,20 +911,18 @@ fn test_tx_call_bls12_381_pairing_wrong_input_layout() { #[test] fn test_halted_tx_call_bls12_381_map_fp_to_g1_out_of_gas() { const SPEC_ID: OpSpecId = OpSpecId::ISTHMUS; - let is_eip7702_enabled = SPEC_ID >= OpSpecId::ISTHMUS; - let is_eip7623_enabled = SPEC_ID >= OpSpecId::ISTHMUS; - let input = Bytes::from([1; bls12_381_const::PADDED_FP_LENGTH]); - let InitialAndFloorGas { initial_gas, .. } = calculate_initial_tx_gas( - SPEC_ID.into(), - &input[..], - false, - is_eip7702_enabled, - is_eip7623_enabled, - 0, - 0, - 0, - ); + let mut gas_params = GasParams::new_spec(SPEC_ID.into()); + gas_params.override_gas([ + ( + GasId::tx_eip7702_per_empty_account_cost(), + eip7702::PER_EMPTY_ACCOUNT_COST, + ), + (GasId::tx_floor_cost_per_token(), TOTAL_COST_FLOOR_PER_TOKEN), + (GasId::tx_floor_cost_base_gas(), 21000), + ]); + let InitialAndFloorGas { initial_gas, .. } = + gas_params.initial_tx_gas(&input[..], false, 0, 0, 0); let ctx = Context::op() .with_tx( @@ -981,7 +939,7 @@ fn test_halted_tx_call_bls12_381_map_fp_to_g1_out_of_gas() { l1_block.operator_fee_constant = Some(U256::ZERO); l1_block.operator_fee_scalar = Some(U256::ZERO) }) - .modify_cfg_chained(|cfg| cfg.spec = SPEC_ID); + .with_cfg(CfgEnv::new_with_spec(SPEC_ID)); let mut evm = ctx.build_op(); let output = evm.replay().unwrap(); @@ -1004,20 +962,18 @@ fn test_halted_tx_call_bls12_381_map_fp_to_g1_out_of_gas() { #[test] fn test_halted_tx_call_bls12_381_map_fp_to_g1_input_wrong_size() { const SPEC_ID: OpSpecId = OpSpecId::ISTHMUS; - let is_eip7702_enabled = SPEC_ID >= OpSpecId::ISTHMUS; - let is_eip7623_enabled = SPEC_ID >= OpSpecId::ISTHMUS; - let input = Bytes::from([1; bls12_381_const::PADDED_FP_LENGTH]); - let InitialAndFloorGas { initial_gas, .. } = calculate_initial_tx_gas( - SPEC_ID.into(), - &input[..], - false, - is_eip7702_enabled, - is_eip7623_enabled, - 0, - 0, - 0, - ); + let mut gas_params = GasParams::new_spec(SPEC_ID.into()); + gas_params.override_gas([ + ( + GasId::tx_eip7702_per_empty_account_cost(), + eip7702::PER_EMPTY_ACCOUNT_COST, + ), + (GasId::tx_floor_cost_per_token(), TOTAL_COST_FLOOR_PER_TOKEN), + (GasId::tx_floor_cost_base_gas(), 21000), + ]); + let InitialAndFloorGas { initial_gas, .. } = + gas_params.initial_tx_gas(&input[..], false, 0, 0, 0); let ctx = Context::op() .with_tx( @@ -1034,7 +990,7 @@ fn test_halted_tx_call_bls12_381_map_fp_to_g1_input_wrong_size() { l1_block.operator_fee_constant = Some(U256::ZERO); l1_block.operator_fee_scalar = Some(U256::ZERO) }) - .modify_cfg_chained(|cfg| cfg.spec = SPEC_ID); + .with_cfg(CfgEnv::new_with_spec(SPEC_ID)); let mut evm = ctx.build_op(); let output = evm.replay().unwrap(); @@ -1057,20 +1013,18 @@ fn test_halted_tx_call_bls12_381_map_fp_to_g1_input_wrong_size() { #[test] fn test_halted_tx_call_bls12_381_map_fp2_to_g2_out_of_gas() { const SPEC_ID: OpSpecId = OpSpecId::ISTHMUS; - let is_eip7702_enabled = SPEC_ID >= OpSpecId::ISTHMUS; - let is_eip7623_enabled = SPEC_ID >= OpSpecId::ISTHMUS; - let input = Bytes::from([1; bls12_381_const::PADDED_FP2_LENGTH]); - let InitialAndFloorGas { initial_gas, .. } = calculate_initial_tx_gas( - SPEC_ID.into(), - &input[..], - false, - is_eip7702_enabled, - is_eip7623_enabled, - 0, - 0, - 0, - ); + let mut gas_params = GasParams::new_spec(SPEC_ID.into()); + gas_params.override_gas([ + ( + GasId::tx_eip7702_per_empty_account_cost(), + eip7702::PER_EMPTY_ACCOUNT_COST, + ), + (GasId::tx_floor_cost_per_token(), TOTAL_COST_FLOOR_PER_TOKEN), + (GasId::tx_floor_cost_base_gas(), 21000), + ]); + let InitialAndFloorGas { initial_gas, .. } = + gas_params.initial_tx_gas(&input[..], false, 0, 0, 0); let ctx = Context::op() .with_tx( @@ -1087,7 +1041,7 @@ fn test_halted_tx_call_bls12_381_map_fp2_to_g2_out_of_gas() { l1_block.operator_fee_constant = Some(U256::ZERO); l1_block.operator_fee_scalar = Some(U256::ZERO) }) - .modify_cfg_chained(|cfg| cfg.spec = SPEC_ID); + .with_cfg(CfgEnv::new_with_spec(SPEC_ID)); let mut evm = ctx.build_op(); let output = evm.replay().unwrap(); @@ -1107,23 +1061,59 @@ fn test_halted_tx_call_bls12_381_map_fp2_to_g2_out_of_gas() { ); } +#[test] +fn test_l1block_load_for_pre_regolith() { + const SPEC_ID: OpSpecId = OpSpecId::REGOLITH; + + let ctx = Context::op() + .with_tx( + OpTransaction::builder() + .base( + TxEnv::builder() + .caller(BENCH_CALLER) + .kind(TxKind::Call(address!( + "0x0000000000000000000000000000000000100000" + ))) + .value(U256::from(1)) + .gas_limit(100_000), + ) + .build_fill(), + ) + .modify_chain_chained(|l1_block| { + l1_block.l2_block = None; + }) + .with_cfg(CfgEnv::new_with_spec(SPEC_ID)); + + let mut evm = ctx + .with_db( + State::builder() + .with_database(BenchmarkDB::default()) + .build(), + ) + .build_op(); + let output = evm.replay().unwrap(); + + // assert out of gas + assert!(output.result.is_success()); + + compare_or_save_op_testdata("test_l1block_load_for_pre_regolith.json", &output); +} + #[test] fn test_halted_tx_call_bls12_381_map_fp2_to_g2_input_wrong_size() { const SPEC_ID: OpSpecId = OpSpecId::ISTHMUS; - let is_eip7702_enabled = SPEC_ID >= OpSpecId::ISTHMUS; - let is_eip7623_enabled = SPEC_ID >= OpSpecId::ISTHMUS; - let input = Bytes::from([1; bls12_381_const::PADDED_FP2_LENGTH]); - let InitialAndFloorGas { initial_gas, .. } = calculate_initial_tx_gas( - SPEC_ID.into(), - &input[..], - false, - is_eip7702_enabled, - is_eip7623_enabled, - 0, - 0, - 0, - ); + let mut gas_params = GasParams::new_spec(SPEC_ID.into()); + gas_params.override_gas([ + ( + GasId::tx_eip7702_per_empty_account_cost(), + eip7702::PER_EMPTY_ACCOUNT_COST, + ), + (GasId::tx_floor_cost_per_token(), TOTAL_COST_FLOOR_PER_TOKEN), + (GasId::tx_floor_cost_base_gas(), 21000), + ]); + let InitialAndFloorGas { initial_gas, .. } = + gas_params.initial_tx_gas(&input[..], false, 0, 0, 0); let ctx = Context::op() .with_tx( @@ -1140,7 +1130,7 @@ fn test_halted_tx_call_bls12_381_map_fp2_to_g2_input_wrong_size() { l1_block.operator_fee_constant = Some(U256::ZERO); l1_block.operator_fee_scalar = Some(U256::ZERO) }) - .modify_cfg_chained(|cfg| cfg.spec = SPEC_ID); + .with_cfg(CfgEnv::new_with_spec(SPEC_ID)); let mut evm = ctx.build_op(); let output = evm.replay().unwrap(); @@ -1216,8 +1206,8 @@ struct LogInspector { } impl Inspector for LogInspector { - fn log(&mut self, _interp: &mut Interpreter, _context: &mut CTX, log: Log) { - self.logs.push(log) + fn log(&mut self, _context: &mut CTX, log: Log) { + self.logs.push(log); } } diff --git a/crates/ee-tests/src/revm_tests.rs b/crates/ee-tests/src/revm_tests.rs index b89459ebfc..fc66a77dfe 100644 --- a/crates/ee-tests/src/revm_tests.rs +++ b/crates/ee-tests/src/revm_tests.rs @@ -3,7 +3,7 @@ use crate::TestdataConfig; use revm::{ bytecode::opcode, - context::{ContextTr, TxEnv}, + context::{CfgEnv, ContextTr, TxEnv}, database::{BenchmarkDB, BENCH_CALLER, BENCH_TARGET}, primitives::{address, b256, hardfork::SpecId, Bytes, TxKind, KECCAK_EMPTY, U256}, state::{AccountStatus, Bytecode}, @@ -38,7 +38,7 @@ const SELFDESTRUCT_BYTECODE: &[u8] = &[ #[test] fn test_selfdestruct_multi_tx() { let mut evm = Context::mainnet() - .modify_cfg_chained(|cfg| cfg.spec = SpecId::BERLIN) + .with_cfg(CfgEnv::new_with_spec(SpecId::BERLIN)) .with_db(BenchmarkDB::new_bytecode(Bytecode::new_legacy( SELFDESTRUCT_BYTECODE.into(), ))) @@ -85,7 +85,7 @@ fn test_selfdestruct_multi_tx() { fn test_multi_tx_create() { let mut evm = Context::mainnet() .modify_cfg_chained(|cfg| { - cfg.spec = SpecId::BERLIN; + cfg.set_spec_and_mainnet_gas_params(SpecId::BERLIN); cfg.disable_nonce_check = true; }) .with_db(BenchmarkDB::new_bytecode(Bytecode::new())) @@ -217,7 +217,7 @@ fn deployment_contract(bytes: &[u8]) -> Bytes { #[test] fn test_frame_stack_index() { let mut evm = Context::mainnet() - .modify_cfg_chained(|cfg| cfg.spec = SpecId::BERLIN) + .with_cfg(CfgEnv::new_with_spec(SpecId::BERLIN)) .with_db(BenchmarkDB::new_bytecode(Bytecode::new_legacy( SELFDESTRUCT_BYTECODE.into(), ))) diff --git a/crates/ee-tests/tests/op_revm_testdata/test_deposit_tx.json b/crates/ee-tests/tests/op_revm_testdata/test_deposit_tx.json index 9b2029ce56..372cec3c7b 100644 --- a/crates/ee-tests/tests/op_revm_testdata/test_deposit_tx.json +++ b/crates/ee-tests/tests/op_revm_testdata/test_deposit_tx.json @@ -18,8 +18,13 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -27,6 +32,26 @@ "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "nonce": 1 }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, "status": "Touched | LoadedAsNotExisting", "storage": {}, "transaction_id": 0 diff --git a/crates/ee-tests/tests/op_revm_testdata/test_halted_deposit_tx.json b/crates/ee-tests/tests/op_revm_testdata/test_halted_deposit_tx.json index 7e7b36361a..58a09b2f05 100644 --- a/crates/ee-tests/tests/op_revm_testdata/test_halted_deposit_tx.json +++ b/crates/ee-tests/tests/op_revm_testdata/test_halted_deposit_tx.json @@ -13,8 +13,13 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -22,6 +27,26 @@ "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "nonce": 1 }, + "original_info": { + "balance": "0x2386f26fc10000", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, "status": "Touched", "storage": {}, "transaction_id": 0 @@ -33,8 +58,37 @@ "LegacyAnalyzed": { "bytecode": "0x5000", "jump_table": { - "len": 1, - "table": "0x00" + "bits": 1, + "data": [ + 0 + ], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 1 + } + }, + "code_hash": "0x7b2ab94bb7d45041581aa3757ae020084674ccad6f75dc3750eb2ea8a92c4e9a", + "nonce": 1 + }, + "original_info": { + "balance": "0x2386f26fc10000", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x5000", + "jump_table": { + "bits": 1, + "data": [ + 0 + ], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 1 } diff --git a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g1_add_input_wrong_size.json b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g1_add_input_wrong_size.json index 8cd1b7f9bb..0e26bc9093 100644 --- a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g1_add_input_wrong_size.json +++ b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g1_add_input_wrong_size.json @@ -17,8 +17,13 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -26,6 +31,26 @@ "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "nonce": 1 }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, "status": "Touched | LoadedAsNotExisting", "storage": {}, "transaction_id": 0 @@ -37,8 +62,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -57,8 +107,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -77,8 +152,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -97,8 +197,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } diff --git a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g1_add_out_of_gas.json b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g1_add_out_of_gas.json index 47e57970b0..950f569e48 100644 --- a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g1_add_out_of_gas.json +++ b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g1_add_out_of_gas.json @@ -17,8 +17,13 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -26,6 +31,26 @@ "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "nonce": 1 }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, "status": "Touched | LoadedAsNotExisting", "storage": {}, "transaction_id": 0 @@ -37,8 +62,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -57,8 +107,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -77,8 +152,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -97,8 +197,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } diff --git a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g1_msm_input_wrong_size.json b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g1_msm_input_wrong_size.json index 680feb30be..9455411d81 100644 --- a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g1_msm_input_wrong_size.json +++ b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g1_msm_input_wrong_size.json @@ -17,8 +17,13 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -26,6 +31,26 @@ "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "nonce": 1 }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, "status": "Touched | LoadedAsNotExisting", "storage": {}, "transaction_id": 0 @@ -37,8 +62,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -57,8 +107,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -77,8 +152,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -97,8 +197,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } diff --git a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g1_msm_out_of_gas.json b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g1_msm_out_of_gas.json index cfe195eb3e..7c18145fba 100644 --- a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g1_msm_out_of_gas.json +++ b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g1_msm_out_of_gas.json @@ -17,8 +17,13 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -26,6 +31,26 @@ "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "nonce": 1 }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, "status": "Touched | LoadedAsNotExisting", "storage": {}, "transaction_id": 0 @@ -37,8 +62,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -57,8 +107,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -77,8 +152,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -97,8 +197,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } diff --git a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g1_msm_wrong_input_layout.json b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g1_msm_wrong_input_layout.json index 67a214f020..7dc99a0591 100644 --- a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g1_msm_wrong_input_layout.json +++ b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g1_msm_wrong_input_layout.json @@ -17,8 +17,13 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -26,6 +31,26 @@ "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "nonce": 1 }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, "status": "Touched | LoadedAsNotExisting", "storage": {}, "transaction_id": 0 @@ -37,8 +62,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -57,8 +107,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -77,8 +152,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -97,8 +197,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } diff --git a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g2_add_input_wrong_size.json b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g2_add_input_wrong_size.json index c7bd90d5d5..63122526d7 100644 --- a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g2_add_input_wrong_size.json +++ b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g2_add_input_wrong_size.json @@ -17,8 +17,13 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -26,6 +31,26 @@ "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "nonce": 1 }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, "status": "Touched | LoadedAsNotExisting", "storage": {}, "transaction_id": 0 @@ -37,8 +62,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -57,8 +107,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -77,8 +152,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -97,8 +197,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } diff --git a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g2_add_out_of_gas.json b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g2_add_out_of_gas.json index 36262bb3be..25010ee722 100644 --- a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g2_add_out_of_gas.json +++ b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g2_add_out_of_gas.json @@ -17,8 +17,13 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -26,6 +31,26 @@ "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "nonce": 1 }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, "status": "Touched | LoadedAsNotExisting", "storage": {}, "transaction_id": 0 @@ -37,8 +62,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -57,8 +107,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -77,8 +152,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -97,8 +197,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } diff --git a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g2_msm_input_wrong_size.json b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g2_msm_input_wrong_size.json index fa95ae5ed2..4aeddf5b9c 100644 --- a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g2_msm_input_wrong_size.json +++ b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g2_msm_input_wrong_size.json @@ -17,8 +17,13 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -26,6 +31,26 @@ "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "nonce": 1 }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, "status": "Touched | LoadedAsNotExisting", "storage": {}, "transaction_id": 0 @@ -37,8 +62,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -57,8 +107,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -77,8 +152,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -97,8 +197,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } diff --git a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g2_msm_out_of_gas.json b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g2_msm_out_of_gas.json index 6b975d9658..9cc62f89ea 100644 --- a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g2_msm_out_of_gas.json +++ b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g2_msm_out_of_gas.json @@ -17,8 +17,13 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -26,6 +31,26 @@ "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "nonce": 1 }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, "status": "Touched | LoadedAsNotExisting", "storage": {}, "transaction_id": 0 @@ -37,8 +62,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -57,8 +107,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -77,8 +152,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -97,8 +197,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } diff --git a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g2_msm_wrong_input_layout.json b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g2_msm_wrong_input_layout.json index caa0a01cdc..1d1909f9b0 100644 --- a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g2_msm_wrong_input_layout.json +++ b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_g2_msm_wrong_input_layout.json @@ -17,8 +17,13 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -26,6 +31,26 @@ "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "nonce": 1 }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, "status": "Touched | LoadedAsNotExisting", "storage": {}, "transaction_id": 0 @@ -37,8 +62,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -57,8 +107,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -77,8 +152,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -97,8 +197,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } diff --git a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_map_fp2_to_g2_input_wrong_size.json b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_map_fp2_to_g2_input_wrong_size.json index 61b28b9eba..853528fd5a 100644 --- a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_map_fp2_to_g2_input_wrong_size.json +++ b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_map_fp2_to_g2_input_wrong_size.json @@ -17,8 +17,13 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -26,6 +31,26 @@ "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "nonce": 1 }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, "status": "Touched | LoadedAsNotExisting", "storage": {}, "transaction_id": 0 @@ -37,8 +62,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -57,8 +107,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -77,8 +152,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -97,8 +197,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } diff --git a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_map_fp2_to_g2_out_of_gas.json b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_map_fp2_to_g2_out_of_gas.json index 5870684c57..2c1db84393 100644 --- a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_map_fp2_to_g2_out_of_gas.json +++ b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_map_fp2_to_g2_out_of_gas.json @@ -17,8 +17,13 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -26,6 +31,26 @@ "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "nonce": 1 }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, "status": "Touched | LoadedAsNotExisting", "storage": {}, "transaction_id": 0 @@ -37,8 +62,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -57,8 +107,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -77,8 +152,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -97,8 +197,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } diff --git a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_map_fp_to_g1_input_wrong_size.json b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_map_fp_to_g1_input_wrong_size.json index f3c95534dc..fe3a4021ac 100644 --- a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_map_fp_to_g1_input_wrong_size.json +++ b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_map_fp_to_g1_input_wrong_size.json @@ -17,8 +17,13 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -26,6 +31,26 @@ "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "nonce": 1 }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, "status": "Touched | LoadedAsNotExisting", "storage": {}, "transaction_id": 0 @@ -37,8 +62,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -57,8 +107,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -77,8 +152,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -97,8 +197,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } diff --git a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_map_fp_to_g1_out_of_gas.json b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_map_fp_to_g1_out_of_gas.json index 2967d7d35d..89e9095030 100644 --- a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_map_fp_to_g1_out_of_gas.json +++ b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_map_fp_to_g1_out_of_gas.json @@ -17,8 +17,13 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -26,6 +31,26 @@ "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "nonce": 1 }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, "status": "Touched | LoadedAsNotExisting", "storage": {}, "transaction_id": 0 @@ -37,8 +62,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -57,8 +107,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -77,8 +152,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -97,8 +197,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } diff --git a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_pairing_input_wrong_size.json b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_pairing_input_wrong_size.json index 7f68950948..17dc87be7f 100644 --- a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_pairing_input_wrong_size.json +++ b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_pairing_input_wrong_size.json @@ -17,8 +17,13 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -26,6 +31,26 @@ "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "nonce": 1 }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, "status": "Touched | LoadedAsNotExisting", "storage": {}, "transaction_id": 0 @@ -37,8 +62,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -57,8 +107,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -77,8 +152,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -97,8 +197,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } diff --git a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_pairing_out_of_gas.json b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_pairing_out_of_gas.json index 23c2a935d7..1ba8f45422 100644 --- a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_pairing_out_of_gas.json +++ b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_pairing_out_of_gas.json @@ -17,8 +17,13 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -26,6 +31,26 @@ "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "nonce": 1 }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, "status": "Touched | LoadedAsNotExisting", "storage": {}, "transaction_id": 0 @@ -37,8 +62,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -57,8 +107,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -77,8 +152,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -97,8 +197,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } diff --git a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_pairing_wrong_input_layout.json b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_pairing_wrong_input_layout.json index 8638f098c5..ae98ddb0f9 100644 --- a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_pairing_wrong_input_layout.json +++ b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bls12_381_pairing_wrong_input_layout.json @@ -17,8 +17,13 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -26,6 +31,26 @@ "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "nonce": 1 }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, "status": "Touched | LoadedAsNotExisting", "storage": {}, "transaction_id": 0 @@ -37,8 +62,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -57,8 +107,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -77,8 +152,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -97,8 +197,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } diff --git a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bn254_pair_fjord.json b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bn254_pair_fjord.json index 70b6029f13..bf5699cb21 100644 --- a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bn254_pair_fjord.json +++ b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bn254_pair_fjord.json @@ -17,8 +17,13 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -26,6 +31,26 @@ "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "nonce": 1 }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, "status": "Touched | LoadedAsNotExisting", "storage": {}, "transaction_id": 0 @@ -37,8 +62,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -57,8 +107,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -77,8 +152,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -97,8 +197,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } diff --git a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bn254_pair_granite.json b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bn254_pair_granite.json index e22fcb268a..26f2d94179 100644 --- a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bn254_pair_granite.json +++ b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_bn254_pair_granite.json @@ -17,8 +17,13 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -26,6 +31,26 @@ "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "nonce": 1 }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, "status": "Touched | LoadedAsNotExisting", "storage": {}, "transaction_id": 0 @@ -37,8 +62,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -57,8 +107,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -77,8 +152,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -97,8 +197,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } diff --git a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_p256verify.json b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_p256verify.json index 8c6633224c..60b5ebf8b4 100644 --- a/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_p256verify.json +++ b/crates/ee-tests/tests/op_revm_testdata/test_halted_tx_call_p256verify.json @@ -17,8 +17,13 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -26,6 +31,26 @@ "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "nonce": 1 }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, "status": "Touched | LoadedAsNotExisting", "storage": {}, "transaction_id": 0 @@ -37,8 +62,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -57,8 +107,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -77,8 +152,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -97,8 +197,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } diff --git a/crates/ee-tests/tests/op_revm_testdata/test_l1block_load_for_pre_regolith.json b/crates/ee-tests/tests/op_revm_testdata/test_l1block_load_for_pre_regolith.json new file mode 100644 index 0000000000..a5bc954780 --- /dev/null +++ b/crates/ee-tests/tests/op_revm_testdata/test_l1block_load_for_pre_regolith.json @@ -0,0 +1,271 @@ +{ + "result": { + "Success": { + "gas_refunded": 0, + "gas_used": 21000, + "logs": [], + "output": { + "Call": "0x" + }, + "reason": "Stop" + } + }, + "state": { + "0x0000000000000000000000000000000000000000": { + "info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "status": "Touched | LoadedAsNotExisting", + "storage": {}, + "transaction_id": 0 + }, + "0x0000000000000000000000000000000000100000": { + "info": { + "balance": "0x1", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "status": "Touched | LoadedAsNotExisting", + "storage": {}, + "transaction_id": 0 + }, + "0x4200000000000000000000000000000000000019": { + "info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "status": "Touched | LoadedAsNotExisting", + "storage": {}, + "transaction_id": 0 + }, + "0x420000000000000000000000000000000000001a": { + "info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "status": "Touched | LoadedAsNotExisting", + "storage": {}, + "transaction_id": 0 + }, + "0x420000000000000000000000000000000000001b": { + "info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "status": "Touched | LoadedAsNotExisting", + "storage": {}, + "transaction_id": 0 + }, + "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee": { + "info": { + "balance": "0x2386f26fc0ffff", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 1 + }, + "original_info": { + "balance": "0x2386f26fc10000", + "code": null, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "status": "Touched", + "storage": {}, + "transaction_id": 0 + } + } +} \ No newline at end of file diff --git a/crates/ee-tests/tests/op_revm_testdata/test_log_inspector.json b/crates/ee-tests/tests/op_revm_testdata/test_log_inspector.json index 1e8fbb1365..f38b1174d6 100644 --- a/crates/ee-tests/tests/op_revm_testdata/test_log_inspector.json +++ b/crates/ee-tests/tests/op_revm_testdata/test_log_inspector.json @@ -24,8 +24,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -44,8 +69,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -64,8 +114,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -84,8 +159,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -104,8 +204,13 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -113,6 +218,12 @@ "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "nonce": 1 }, + "original_info": { + "balance": "0x2386f26fc10000", + "code": null, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, "status": "Touched", "storage": {}, "transaction_id": 0 @@ -124,8 +235,37 @@ "LegacyAnalyzed": { "bytecode": "0x600080a000", "jump_table": { - "len": 5, - "table": "0x00" + "bits": 5, + "data": [ + 0 + ], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 5 + } + }, + "code_hash": "0x8013c386d5a03c3fa0a6ccbfefcf7d91471dedba2bb94eefef57f916e5929a8d", + "nonce": 1 + }, + "original_info": { + "balance": "0x2386f26fc10000", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x600080a000", + "jump_table": { + "bits": 5, + "data": [ + 0 + ], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 5 } diff --git a/crates/ee-tests/tests/op_revm_testdata/test_tx_call_p256verify.json b/crates/ee-tests/tests/op_revm_testdata/test_tx_call_p256verify.json index e63ed6319d..99c993c263 100644 --- a/crates/ee-tests/tests/op_revm_testdata/test_tx_call_p256verify.json +++ b/crates/ee-tests/tests/op_revm_testdata/test_tx_call_p256verify.json @@ -18,8 +18,13 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -27,6 +32,26 @@ "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "nonce": 1 }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, "status": "Touched | LoadedAsNotExisting", "storage": {}, "transaction_id": 0 @@ -38,8 +63,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -58,8 +108,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -78,8 +153,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -98,8 +198,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } diff --git a/crates/ee-tests/tests/revm_testdata/test_multi_tx_create.json b/crates/ee-tests/tests/revm_testdata/test_multi_tx_create.json index 44927c973d..bbb9aba179 100644 --- a/crates/ee-tests/tests/revm_testdata/test_multi_tx_create.json +++ b/crates/ee-tests/tests/revm_testdata/test_multi_tx_create.json @@ -46,8 +46,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -66,8 +91,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -77,7 +127,7 @@ }, "status": "Touched | LoadedAsNotExisting", "storage": {}, - "transaction_id": 1 + "transaction_id": 0 }, "0x84bcbaa99ae6d1f7f70b37d5f6c27c9631eeb2f2": { "info": { @@ -86,8 +136,15 @@ "LegacyAnalyzed": { "bytecode": "0x61ffffff00", "jump_table": { - "len": 5, - "table": "0x00" + "bits": 5, + "data": [ + 0 + ], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 5 } @@ -95,6 +152,26 @@ "code_hash": "0x9125466aa9ef15459d85e7318f6d3bdc5f6978c0565bee37a8e768d7c202a67a", "nonce": 1 }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, "status": "Created | CreatedLocal | SelfDestructed | Touched | LoadedAsNotExisting", "storage": {}, "transaction_id": 2 @@ -106,8 +183,13 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -115,6 +197,26 @@ "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "nonce": 1 }, + "original_info": { + "balance": "0x2386f26fc10000", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, "status": "Touched", "storage": {}, "transaction_id": 2 diff --git a/crates/ee-tests/tests/revm_testdata/test_selfdestruct_multi_tx.json b/crates/ee-tests/tests/revm_testdata/test_selfdestruct_multi_tx.json index 2e2fabbab6..27bdb42e70 100644 --- a/crates/ee-tests/tests/revm_testdata/test_selfdestruct_multi_tx.json +++ b/crates/ee-tests/tests/revm_testdata/test_selfdestruct_multi_tx.json @@ -29,8 +29,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -49,8 +74,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -69,8 +119,13 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } @@ -78,6 +133,26 @@ "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "nonce": 2 }, + "original_info": { + "balance": "0x2386f26fc10000", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 1 + }, "status": "Touched", "storage": {}, "transaction_id": 1 @@ -89,8 +164,33 @@ "LegacyAnalyzed": { "bytecode": "0x00", "jump_table": { - "len": 0, - "table": "0x" + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" + }, + "original_len": 0 + } + }, + "code_hash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": 0 + }, + "original_info": { + "balance": "0x0", + "code": { + "LegacyAnalyzed": { + "bytecode": "0x00", + "jump_table": { + "bits": 0, + "data": [], + "head": { + "index": 0, + "width": 8 + }, + "order": "bitvec::order::Lsb0" }, "original_len": 0 } diff --git a/crates/handler/CHANGELOG.md b/crates/handler/CHANGELOG.md index 476f37f264..d17188fdc6 100644 --- a/crates/handler/CHANGELOG.md +++ b/crates/handler/CHANGELOG.md @@ -7,6 +7,92 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [15.0.0](https://github.com/bluealloy/revm/compare/revm-handler-v14.1.0...revm-handler-v15.0.0) - 2026-01-15 + +### Added + +- add `new_oog` helpers to InterpreterResult, CallOutcome, CreateOutcome, and FrameResult ([#3309](https://github.com/bluealloy/revm/pull/3309)) +- new gas params, tx initial gas and codedeposit ([#3260](https://github.com/bluealloy/revm/pull/3260)) +- move GasParams to Cfg ([#3229](https://github.com/bluealloy/revm/pull/3229)) +- Gas params ([#3132](https://github.com/bluealloy/revm/pull/3132)) +- *(create)* Implement Cache for CreateInputs::created_address ([#3218](https://github.com/bluealloy/revm/pull/3218)) +- JournaledAccount sload/sstore ([#3201](https://github.com/bluealloy/revm/pull/3201)) +- Restrict Database::Error. JournaledAccountTr ([#3199](https://github.com/bluealloy/revm/pull/3199)) + +### Fixed + +- *(create)* Fix CreateInputs::created_address Cache invalidation ([#3222](https://github.com/bluealloy/revm/pull/3222)) + +### Other + +- apply improvements from ai-bot labeled PRs ([#3297](https://github.com/bluealloy/revm/pull/3297)) +- fix typos, grammar errors, and improve documentation consistency ([#3294](https://github.com/bluealloy/revm/pull/3294)) +- happy new year, 2026 licence ([#3272](https://github.com/bluealloy/revm/pull/3272)) +- description of `Handler::validate_against_state_and_deduct_caller` should match responsibilities ([#3256](https://github.com/bluealloy/revm/pull/3256)) +- *(handler)* eliminate code duplication in transaction validation ([#3243](https://github.com/bluealloy/revm/pull/3243)) +- *(handler)* validate_initial_tx_gas takes &mut Evm ([#3235](https://github.com/bluealloy/revm/pull/3235)) +- *(clippy)* remove unused imports ([#3227](https://github.com/bluealloy/revm/pull/3227)) +- optimize vector initialization using size hints ([#3200](https://github.com/bluealloy/revm/pull/3200)) +- apply_auth_list helper fn ([#3187](https://github.com/bluealloy/revm/pull/3187)) +- *(fmt)* merge all imports ([#3184](https://github.com/bluealloy/revm/pull/3184)) + +## [14.1.0](https://github.com/bluealloy/revm/compare/revm-handler-v14.0.0...revm-handler-v14.1.0) - 2025-11-14 + +### Other + +- updated the following local packages: revm-context-interface, revm-context, revm-database, revm-interpreter + +## [14.0.0](https://github.com/bluealloy/revm/compare/revm-handler-v12.0.2...revm-handler-v14.0.0) - 2025-11-10 + +### Added + +- process precompile logs to inspector ([#3148](https://github.com/bluealloy/revm/pull/3148)) +- add gas refund to PrecompileOutput ([#3152](https://github.com/bluealloy/revm/pull/3152)) + +### Other + +- merge v98 versions bumps ([#3155](https://github.com/bluealloy/revm/pull/3155)) + +## [12.0.2](https://github.com/bluealloy/revm/compare/revm-handler-v12.0.1...revm-handler-v12.0.2) - 2025-11-10 + +### Other + +- updated the following local packages: revm-database, revm-context + +## [12.0.1](https://github.com/bluealloy/revm/compare/revm-handler-v12.0.0...revm-handler-v12.0.1) - 2025-11-07 + +### Other + +- updated the following local packages: revm-primitives, revm-context, revm-bytecode, revm-state, revm-database-interface, revm-context-interface, revm-database, revm-interpreter, revm-precompile + +## [12.0.0](https://github.com/bluealloy/revm/compare/revm-handler-v11.2.0...revm-handler-v12.0.0) - 2025-10-30 + +### Added + +- JournaledAccount, a nice way to update and track changes ([#3086](https://github.com/bluealloy/revm/pull/3086)) +- dont load access list immediately ([#3116](https://github.com/bluealloy/revm/pull/3116)) + +### Fixed + +- hook up Cfg::memory_limit ([#3129](https://github.com/bluealloy/revm/pull/3129)) + +### Other + +- *(op)* use helper function in validate against state ([#3069](https://github.com/bluealloy/revm/pull/3069)) +- remove redundant alloy-eip7702 from handler dev-dependencies ([#3105](https://github.com/bluealloy/revm/pull/3105)) + +## [11.2.0](https://github.com/bluealloy/revm/compare/revm-handler-v11.1.2...revm-handler-v11.2.0) - 2025-10-17 + +### Added + +- Optional Bytecode in CallInput ([#3110](https://github.com/bluealloy/revm/pull/3110)) + +## [11.1.2](https://github.com/bluealloy/revm/compare/revm-handler-v11.1.1...revm-handler-v11.1.2) - 2025-10-15 + +### Other + +- updated the following local packages: revm-bytecode, revm-state, revm-database-interface, revm-context-interface, revm-context, revm-database, revm-interpreter + ## [11.1.1](https://github.com/bluealloy/revm/compare/revm-handler-v11.1.0...revm-handler-v11.1.1) - 2025-10-15 ### Other diff --git a/crates/handler/Cargo.toml b/crates/handler/Cargo.toml index cf90f99835..99396c4ab1 100644 --- a/crates/handler/Cargo.toml +++ b/crates/handler/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "revm-handler" description = "Revm handler crates" -version = "11.1.1" +version = "15.0.0" authors.workspace = true edition.workspace = true keywords.workspace = true @@ -36,7 +36,6 @@ serde = { workspace = true, optional = true } [dev-dependencies] database.workspace = true -alloy-eip7702.workspace = true alloy-provider.workspace = true alloy-signer.workspace = true alloy-signer-local.workspace = true @@ -45,7 +44,6 @@ alloy-signer-local.workspace = true default = ["std"] std = [ "serde?/std", - "alloy-eip7702/std", "bytecode/std", "context/std", "context-interface/std", @@ -61,7 +59,6 @@ serde = [ "primitives/serde", "state/serde", "context-interface/serde", - "alloy-eip7702/serde", "bytecode/serde", "context/serde", "database/serde", diff --git a/crates/handler/LICENSE b/crates/handler/LICENSE index be6d350ebe..7edb0405ff 100644 --- a/crates/handler/LICENSE +++ b/crates/handler/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021-2025 draganrakita +Copyright (c) 2021-2026 draganrakita Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/crates/handler/src/api.rs b/crates/handler/src/api.rs index a2faacf186..f6428781e3 100644 --- a/crates/handler/src/api.rs +++ b/crates/handler/src/api.rs @@ -90,7 +90,8 @@ pub trait ExecuteEvm { &mut self, txs: impl Iterator, ) -> Result, TransactionIndexedError> { - let mut outputs = Vec::new(); + let (lower, _) = txs.size_hint(); + let mut outputs = Vec::with_capacity(lower); for (index, tx) in txs.enumerate() { outputs.push( self.transact_one(tx) diff --git a/crates/handler/src/execution.rs b/crates/handler/src/execution.rs index aecda06d20..d326974642 100644 --- a/crates/handler/src/execution.rs +++ b/crates/handler/src/execution.rs @@ -17,14 +17,13 @@ pub fn create_init_frame( match tx.kind() { TxKind::Call(target_address) => { - let (bytecode, bytecode_hash) = bytecode.unwrap_or_default(); + let known_bytecode = bytecode.map(|(code, hash)| (hash, code)); FrameInput::Call(Box::new(CallInputs { input: CallInput::Bytes(input), gas_limit, target_address, bytecode_address: target_address, - bytecode, - bytecode_hash, + known_bytecode, caller: tx.caller(), value: CallValue::Transfer(tx.value()), scheme: CallScheme::Call, @@ -32,12 +31,12 @@ pub fn create_init_frame( return_memory_offset: 0..0, })) } - TxKind::Create => FrameInput::Create(Box::new(CreateInputs { - caller: tx.caller(), - scheme: CreateScheme::Create, - value: tx.value(), - init_code: input, + TxKind::Create => FrameInput::Create(Box::new(CreateInputs::new( + tx.caller(), + CreateScheme::Create, + tx.value(), + input, gas_limit, - })), + ))), } } diff --git a/crates/handler/src/frame.rs b/crates/handler/src/frame.rs index df97956023..f5aea40738 100644 --- a/crates/handler/src/frame.rs +++ b/crates/handler/src/frame.rs @@ -1,21 +1,19 @@ -use crate::evm::FrameTr; -use crate::item_or_result::FrameInitOrResult; -use crate::{precompile_provider::PrecompileProvider, ItemOrResult}; -use crate::{CallFrame, CreateFrame, FrameData, FrameResult}; +use crate::{ + evm::FrameTr, item_or_result::FrameInitOrResult, precompile_provider::PrecompileProvider, + CallFrame, CreateFrame, FrameData, FrameResult, ItemOrResult, +}; use context::result::FromStringError; -use context_interface::context::ContextError; -use context_interface::local::{FrameToken, OutFrame}; -use context_interface::ContextTr; use context_interface::{ - journaled_state::{JournalCheckpoint, JournalTr}, - Cfg, Database, + context::ContextError, + journaled_state::{account::JournaledAccountTr, JournalCheckpoint, JournalTr}, + local::{FrameToken, OutFrame}, + Cfg, ContextTr, Database, }; use core::cmp::min; use derive_where::derive_where; -use interpreter::interpreter_action::FrameInit; use interpreter::{ - gas, interpreter::{EthInterpreter, ExtBytecode}, + interpreter_action::FrameInit, interpreter_types::ReturnData, CallInput, CallInputs, CallOutcome, CallValue, CreateInputs, CreateOutcome, CreateScheme, FrameInput, Gas, InputsImpl, InstructionResult, Interpreter, InterpreterAction, @@ -24,11 +22,10 @@ use interpreter::{ use primitives::{ constants::CALL_STACK_LIMIT, hardfork::SpecId::{self, HOMESTEAD, LONDON, SPURIOUS_DRAGON}, + keccak256, Address, Bytes, U256, }; -use primitives::{keccak256, Address, Bytes, U256}; use state::Bytecode; -use std::borrow::ToOwned; -use std::boxed::Box; +use std::{borrow::ToOwned, boxed::Box, vec::Vec}; /// Frame implementation for Ethereum. #[derive_where(Clone, Debug; IW, @@ -103,6 +100,7 @@ pub type ContextTrDbError = <::Db as Database>::Error; impl EthFrame { /// Clear and initialize a frame. #[allow(clippy::too_many_arguments)] + #[inline(always)] pub fn clear( &mut self, data: FrameData, @@ -155,6 +153,8 @@ impl EthFrame { output: Bytes::new(), }, memory_offset: inputs.return_memory_offset.clone(), + was_precompile_called: false, + precompile_call_logs: Vec::new(), }))) }; @@ -190,19 +190,37 @@ impl EthFrame { let gas_limit = inputs.gas_limit; if let Some(result) = precompiles.run(ctx, &inputs).map_err(ERROR::from_string)? { + let mut logs = Vec::new(); if result.result.is_ok() { ctx.journal_mut().checkpoint_commit(); } else { + // clone logs that precompile created, only possible with custom precompiles. + // checkpoint.log_i will be always correct. + logs = ctx.journal_mut().logs()[checkpoint.log_i..].to_vec(); ctx.journal_mut().checkpoint_revert(checkpoint); } return Ok(ItemOrResult::Result(FrameResult::Call(CallOutcome { result, memory_offset: inputs.return_memory_offset.clone(), + was_precompile_called: true, + precompile_call_logs: logs, }))); } - let bytecode = inputs.bytecode.clone(); - let bytecode_hash = inputs.bytecode_hash; + // Get bytecode and hash - either from known_bytecode or load from account + let (bytecode, bytecode_hash) = if let Some((hash, code)) = inputs.known_bytecode.clone() { + // Use provided bytecode and hash + (code, hash) + } else { + // Load account and get its bytecode + let account = ctx + .journal_mut() + .load_account_with_code(inputs.bytecode_address)?; + ( + account.info.code.clone().unwrap_or_default(), + account.info.code_hash, + ) + }; // Returns success if bytecode is empty. if bytecode.is_empty() { @@ -245,7 +263,7 @@ impl EthFrame { Ok(ItemOrResult::Result(FrameResult::Create(CreateOutcome { result: InterpreterResult { result: e, - gas: Gas::new(inputs.gas_limit), + gas: Gas::new(inputs.gas_limit()), output: Bytes::new(), }, address: None, @@ -258,42 +276,42 @@ impl EthFrame { } // Fetch balance of caller. - let caller_info = &mut context.journal_mut().load_account(inputs.caller)?.data.info; + let journal = context.journal_mut(); + let mut caller_info = journal.load_account_mut(inputs.caller())?; // Check if caller has enough balance to send to the created contract. - if caller_info.balance < inputs.value { + // decrement of balance is done in the create_account_checkpoint. + if *caller_info.balance() < inputs.value() { return return_error(InstructionResult::OutOfFunds); } // Increase nonce of caller and check if it overflows - let old_nonce = caller_info.nonce; - let Some(new_nonce) = old_nonce.checked_add(1) else { + let old_nonce = caller_info.nonce(); + if !caller_info.bump_nonce() { return return_error(InstructionResult::Return); }; - caller_info.nonce = new_nonce; - context - .journal_mut() - .nonce_bump_journal_entry(inputs.caller); // Create address let mut init_code_hash = None; - let created_address = match inputs.scheme { - CreateScheme::Create => inputs.caller.create(old_nonce), + let created_address = match inputs.scheme() { + CreateScheme::Create => inputs.caller().create(old_nonce), CreateScheme::Create2 { salt } => { - let init_code_hash = *init_code_hash.insert(keccak256(&inputs.init_code)); - inputs.caller.create2(salt.to_be_bytes(), init_code_hash) + let init_code_hash = *init_code_hash.insert(keccak256(inputs.init_code())); + inputs.caller().create2(salt.to_be_bytes(), init_code_hash) } CreateScheme::Custom { address } => address, }; + drop(caller_info); // Drop caller info to avoid borrow checker issues. + // warm load account. - context.journal_mut().load_account(created_address)?; + journal.load_account(created_address)?; // Create account, transfer funds and make the journal checkpoint. let checkpoint = match context.journal_mut().create_account_checkpoint( - inputs.caller, + inputs.caller(), created_address, - inputs.value, + inputs.value(), spec, ) { Ok(checkpoint) => checkpoint, @@ -301,18 +319,18 @@ impl EthFrame { }; let bytecode = ExtBytecode::new_with_optional_hash( - Bytecode::new_legacy(inputs.init_code.clone()), + Bytecode::new_legacy(inputs.init_code().clone()), init_code_hash, ); let interpreter_input = InputsImpl { target_address: created_address, - caller_address: inputs.caller, + caller_address: inputs.caller(), bytecode_address: None, input: CallInput::Bytes(Bytes::new()), - call_value: inputs.value, + call_value: inputs.value(), }; - let gas_limit = inputs.gas_limit; + let gas_limit = inputs.gas_limit(); this.get(EthFrame::invalid).clear( FrameData::Create(CreateFrame { created_address }), @@ -369,8 +387,6 @@ impl EthFrame { context: &mut CTX, next_action: InterpreterAction, ) -> Result, ERROR> { - let spec = context.cfg().spec().into(); - // Run interpreter let mut interpreter_result = match next_action { @@ -401,16 +417,13 @@ impl EthFrame { ))) } FrameData::Create(frame) => { - let max_code_size = context.cfg().max_code_size(); - let is_eip3541_disabled = context.cfg().is_eip3541_disabled(); + let (cfg, journal) = context.cfg_journal_mut(); return_create( - context.journal_mut(), + journal, + cfg, self.checkpoint, &mut interpreter_result, frame.created_address, - max_code_size, - is_eip3541_disabled, - spec, ); ItemOrResult::Result(FrameResult::Create(CreateOutcome::new( @@ -516,15 +529,17 @@ impl EthFrame { } /// Handles the result of a CREATE operation, including validation and state updates. -pub fn return_create( +pub fn return_create( journal: &mut JOURNAL, + cfg: CFG, checkpoint: JournalCheckpoint, interpreter_result: &mut InterpreterResult, address: Address, - max_code_size: usize, - is_eip3541_disabled: bool, - spec_id: SpecId, ) { + let max_code_size = cfg.max_code_size(); + let is_eip3541_disabled = cfg.is_eip3541_disabled(); + let spec_id = cfg.spec().into(); + // If return is not ok revert and return. if !interpreter_result.result.is_ok() { journal.checkpoint_revert(checkpoint); @@ -550,7 +565,9 @@ pub fn return_create( interpreter_result.result = InstructionResult::CreateContractSizeLimit; return; } - let gas_for_code = interpreter_result.output.len() as u64 * gas::CODEDEPOSIT; + let gas_for_code = cfg + .gas_params() + .code_deposit_cost(interpreter_result.output.len()); if !interpreter_result.gas.record_cost(gas_for_code) { // Record code deposit gas cost and check if we are out of gas. // EIP-2 point 3: If contract creation does not have enough gas to pay for the diff --git a/crates/handler/src/frame_data.rs b/crates/handler/src/frame_data.rs index fab41ca813..de085e174e 100644 --- a/crates/handler/src/frame_data.rs +++ b/crates/handler/src/frame_data.rs @@ -42,6 +42,18 @@ pub enum FrameResult { } impl FrameResult { + /// Creates a new call frame result for an out-of-gas error. + #[inline] + pub fn new_call_oog(gas_limit: u64, memory_offset: core::ops::Range) -> Self { + Self::Call(CallOutcome::new_oog(gas_limit, memory_offset)) + } + + /// Creates a new create frame result for an out-of-gas error. + #[inline] + pub fn new_create_oog(gas_limit: u64) -> Self { + Self::Create(CreateOutcome::new_oog(gas_limit)) + } + /// Casts frame result to interpreter result. #[inline] pub fn into_interpreter_result(self) -> InterpreterResult { diff --git a/crates/handler/src/handler.rs b/crates/handler/src/handler.rs index 30dc667eba..bff8b38d1e 100644 --- a/crates/handler/src/handler.rs +++ b/crates/handler/src/handler.rs @@ -1,17 +1,19 @@ use crate::{ - evm::FrameTr, execution, post_execution, pre_execution, validation, EvmTr, FrameResult, - ItemOrResult, + evm::FrameTr, + execution, post_execution, + pre_execution::{self, apply_eip7702_auth_list}, + validation, EvmTr, FrameResult, ItemOrResult, +}; +use context::{ + result::{ExecutionResult, FromStringError}, + LocalContextTr, }; -use context::result::{ExecutionResult, FromStringError}; -use context::LocalContextTr; -use context_interface::context::ContextError; -use context_interface::ContextTr; use context_interface::{ + context::ContextError, result::{HaltReasonTr, InvalidHeader, InvalidTransaction}, - Cfg, Database, JournalTr, Transaction, + Cfg, ContextTr, Database, JournalTr, Transaction, }; -use interpreter::interpreter_action::FrameInit; -use interpreter::{Gas, InitialAndFloorGas, SharedMemory}; +use interpreter::{interpreter_action::FrameInit, Gas, InitialAndFloorGas, SharedMemory}; use primitives::U256; use state::Bytecode; @@ -250,14 +252,16 @@ pub trait Handler { /// /// Verifies the initial cost does not exceed the transaction gas limit. #[inline] - fn validate_initial_tx_gas(&self, evm: &Self::Evm) -> Result { + fn validate_initial_tx_gas( + &self, + evm: &mut Self::Evm, + ) -> Result { let ctx = evm.ctx_ref(); validation::validate_initial_tx_gas( ctx.tx(), ctx.cfg().spec().into(), + ctx.cfg().gas_params(), ctx.cfg().is_eip7623_disabled(), - ctx.cfg().is_eip7702_enabled(), - ctx.cfg().is_eip7623_enabled(), ) .map_err(From::from) } @@ -276,10 +280,14 @@ pub trait Handler { /// Returns the gas refund amount specified by EIP-7702. #[inline] fn apply_eip7702_auth_list(&self, evm: &mut Self::Evm) -> Result { - pre_execution::apply_eip7702_auth_list(evm.ctx()) + apply_eip7702_auth_list(evm.ctx_mut()) } - /// Deducts maximum possible fee and transfer value from caller's balance. + /// Deducts the maximum possible fee from caller's balance. + /// + /// If cfg.is_balance_check_disabled, this method will add back enough funds to ensure that + /// the caller's balance is at least tx.value() before returning. Note that the amount of funds + /// added back in this case may exceed the maximum fee. /// /// Unused fees are returned to caller after execution completes. #[inline] @@ -300,15 +308,16 @@ pub trait Handler { gas_limit: u64, ) -> Result { let ctx = evm.ctx_mut(); - let memory = SharedMemory::new_with_buffer(ctx.local().shared_memory_buffer().clone()); + let mut memory = SharedMemory::new_with_buffer(ctx.local().shared_memory_buffer().clone()); + memory.set_memory_limit(ctx.cfg().memory_limit()); let (tx, journal) = ctx.tx_journal_mut(); let bytecode = if let Some(&to) = tx.kind().to() { - let account = &journal.load_account_code(to)?.info; + let account = &journal.load_account_with_code(to)?.info; if let Some(Bytecode::Eip7702(eip7702_bytecode)) = &account.code { let delegated_address = eip7702_bytecode.delegated_address; - let account = &journal.load_account_code(delegated_address)?.info; + let account = &journal.load_account_with_code(delegated_address)?.info; Some(( account.code.clone().unwrap_or_default(), account.code_hash(), diff --git a/crates/handler/src/instructions.rs b/crates/handler/src/instructions.rs index b45582f0d4..89bcf663c0 100644 --- a/crates/handler/src/instructions.rs +++ b/crates/handler/src/instructions.rs @@ -1,12 +1,13 @@ use auto_impl::auto_impl; use interpreter::{ - instructions::{instruction_table, InstructionTable}, + instructions::{instruction_table_gas_changes_spec, InstructionTable}, Host, Instruction, InterpreterTypes, }; +use primitives::hardfork::SpecId; use std::boxed::Box; /// Stores instructions for EVM. -#[auto_impl(&, Arc, Rc)] +#[auto_impl(&mut, Box)] pub trait InstructionProvider { /// Context type. type Context; @@ -22,6 +23,8 @@ pub trait InstructionProvider { pub struct EthInstructions { /// Table containing instruction implementations indexed by opcode. pub instruction_table: Box>, + /// Spec that is used to set gas costs for instructions. + pub spec: SpecId, } impl Clone for EthInstructions @@ -31,6 +34,7 @@ where fn clone(&self) -> Self { Self { instruction_table: self.instruction_table.clone(), + spec: self.spec, } } } @@ -42,14 +46,21 @@ where { /// Returns `EthInstructions` with mainnet spec. pub fn new_mainnet() -> Self { - Self::new(instruction_table::()) + let spec = SpecId::default(); + Self::new(instruction_table_gas_changes_spec(spec), spec) + } + + /// Returns `EthInstructions` with mainnet spec. + pub fn new_mainnet_with_spec(spec: SpecId) -> Self { + Self::new(instruction_table_gas_changes_spec(spec), spec) } /// Returns a new instance of `EthInstructions` with custom instruction table. #[inline] - pub fn new(base_table: InstructionTable) -> Self { + pub fn new(base_table: InstructionTable, spec: SpecId) -> Self { Self { instruction_table: Box::new(base_table), + spec, } } diff --git a/crates/handler/src/mainnet_builder.rs b/crates/handler/src/mainnet_builder.rs index 69e981c530..46baa54fc8 100644 --- a/crates/handler/src/mainnet_builder.rs +++ b/crates/handler/src/mainnet_builder.rs @@ -36,11 +36,12 @@ where type Context = Self; fn build_mainnet(self) -> MainnetEvm { + let spec = self.cfg.spec().into(); Evm { ctx: self, inspector: (), - instruction: EthInstructions::default(), - precompiles: EthPrecompiles::default(), + instruction: EthInstructions::new_mainnet_with_spec(spec), + precompiles: EthPrecompiles::new(spec), frame_stack: FrameStack::new_prealloc(8), } } @@ -49,11 +50,12 @@ where self, inspector: INSP, ) -> MainnetEvm { + let spec = self.cfg.spec().into(); Evm { ctx: self, inspector, - instruction: EthInstructions::default(), - precompiles: EthPrecompiles::default(), + instruction: EthInstructions::new_mainnet_with_spec(spec), + precompiles: EthPrecompiles::new(spec), frame_stack: FrameStack::new_prealloc(8), } } @@ -73,8 +75,7 @@ impl MainContext for Context, #[cfg(test)] mod test { - use crate::ExecuteEvm; - use crate::{MainBuilder, MainContext}; + use crate::{ExecuteEvm, MainBuilder, MainContext}; use alloy_signer::{Either, SignerSync}; use alloy_signer_local::PrivateKeySigner; use bytecode::{ @@ -84,8 +85,7 @@ mod test { use context::{Context, TxEnv}; use context_interface::transaction::Authorization; use database::{BenchmarkDB, EEADDRESS, FFADDRESS}; - use primitives::{hardfork::SpecId, TxKind, U256}; - use primitives::{StorageKey, StorageValue}; + use primitives::{hardfork::SpecId, StorageKey, StorageValue, TxKind, U256}; #[test] fn sanity_eip7702_tx() { @@ -101,7 +101,7 @@ mod test { let bytecode = Bytecode::new_legacy([PUSH1, 0x01, PUSH1, 0x01, SSTORE].into()); let ctx = Context::mainnet() - .modify_cfg_chained(|cfg| cfg.spec = SpecId::PRAGUE) + .modify_cfg_chained(|cfg| cfg.set_spec_and_mainnet_gas_params(SpecId::PRAGUE)) .with_db(BenchmarkDB::new_bytecode(bytecode)); let mut evm = ctx.build_mainnet(); diff --git a/crates/handler/src/post_execution.rs b/crates/handler/src/post_execution.rs index b299831a23..b5a8bef35d 100644 --- a/crates/handler/src/post_execution.rs +++ b/crates/handler/src/post_execution.rs @@ -1,4 +1,5 @@ use crate::FrameResult; +use context::journaled_state::account::JournaledAccountTr; use context_interface::{ journaled_state::JournalTr, result::{ExecutionResult, HaltReason, HaltReasonTr}, @@ -39,12 +40,15 @@ pub fn reimburse_caller( let effective_gas_price = context.tx().effective_gas_price(basefee); // Return balance of not spend gas. - context.journal_mut().balance_incr( - caller, - U256::from( - effective_gas_price.saturating_mul((gas.remaining() + gas.refunded() as u64) as u128), - ) + additional_refund, - )?; + context + .journal_mut() + .load_account_mut(caller)? + .incr_balance( + U256::from( + effective_gas_price + .saturating_mul((gas.remaining() + gas.refunded() as u64) as u128), + ) + additional_refund, + ); Ok(()) } @@ -55,23 +59,22 @@ pub fn reward_beneficiary( context: &mut CTX, gas: &Gas, ) -> Result<(), ::Error> { - let beneficiary = context.block().beneficiary(); - let basefee = context.block().basefee() as u128; - let effective_gas_price = context.tx().effective_gas_price(basefee); + let (block, tx, cfg, journal, _, _) = context.all_mut(); + let basefee = block.basefee() as u128; + let effective_gas_price = tx.effective_gas_price(basefee); // Transfer fee to coinbase/beneficiary. // EIP-1559 discard basefee for coinbase transfer. Basefee amount of gas is discarded. - let coinbase_gas_price = if context.cfg().spec().into().is_enabled_in(SpecId::LONDON) { + let coinbase_gas_price = if cfg.spec().into().is_enabled_in(SpecId::LONDON) { effective_gas_price.saturating_sub(basefee) } else { effective_gas_price }; // reward beneficiary - context.journal_mut().balance_incr( - beneficiary, - U256::from(coinbase_gas_price * gas.used() as u128), - )?; + journal + .load_account_mut(block.beneficiary())? + .incr_balance(U256::from(coinbase_gas_price * gas.used() as u128)); Ok(()) } diff --git a/crates/handler/src/pre_execution.rs b/crates/handler/src/pre_execution.rs index 95174ae3c4..88dc789b83 100644 --- a/crates/handler/src/pre_execution.rs +++ b/crates/handler/src/pre_execution.rs @@ -4,17 +4,14 @@ use crate::{EvmTr, PrecompileProvider}; use bytecode::Bytecode; -use context_interface::transaction::{AccessListItemTr, AuthorizationTr}; -use context_interface::ContextTr; use context_interface::{ - journaled_state::JournalTr, + journaled_state::{account::JournaledAccountTr, JournalTr}, result::InvalidTransaction, - transaction::{Transaction, TransactionType}, - Block, Cfg, Database, + transaction::{AccessListItemTr, AuthorizationTr, Transaction, TransactionType}, + Block, Cfg, ContextTr, Database, }; use core::cmp::Ordering; -use primitives::StorageKey; -use primitives::{eip7702, hardfork::SpecId, KECCAK_EMPTY, U256}; +use primitives::{eip7702, hardfork::SpecId, Address, HashMap, HashSet, StorageKey, U256}; use state::AccountInfo; /// Loads and warms accounts for execution, including precompiles and access list. @@ -53,22 +50,38 @@ pub fn load_accounts< // legacy is only tx type that does not have access list. if tx.tx_type() != TransactionType::Legacy { if let Some(access_list) = tx.access_list() { + let mut map: HashMap> = HashMap::default(); for item in access_list { - journal.warm_account_and_storage( - *item.address(), - item.storage_slots().map(|i| StorageKey::from_be_bytes(i.0)), - )?; + map.entry(*item.address()) + .or_default() + .extend(item.storage_slots().map(|key| U256::from_be_bytes(key.0))); } + journal.warm_access_list(map); } } Ok(()) } +/// Validates caller account nonce and code according to EIP-3607. +#[inline] +pub fn validate_account_nonce_and_code_with_components( + caller_info: &AccountInfo, + tx: impl Transaction, + cfg: impl Cfg, +) -> Result<(), InvalidTransaction> { + validate_account_nonce_and_code( + caller_info, + tx.nonce(), + cfg.is_eip3607_disabled(), + cfg.is_nonce_check_disabled(), + ) +} + /// Validates caller account nonce and code according to EIP-3607. #[inline] pub fn validate_account_nonce_and_code( - caller_info: &mut AccountInfo, + caller_info: &AccountInfo, tx_nonce: u64, is_eip3607_disabled: bool, is_nonce_check_disabled: bool, @@ -151,24 +164,25 @@ pub fn validate_against_state_and_deduct_caller< let (block, tx, cfg, journal, _, _) = context.all_mut(); // Load caller's account. - let caller_account = journal.load_account_code(tx.caller())?.data; - - validate_account_nonce_and_code( - &mut caller_account.info, - tx.nonce(), - cfg.is_eip3607_disabled(), - cfg.is_nonce_check_disabled(), - )?; + let mut caller = journal.load_account_with_code_mut(tx.caller())?.data; - let new_balance = calculate_caller_fee(caller_account.info.balance, tx, block, cfg)?; + validate_account_nonce_and_code_with_components(&caller.account().info, tx, cfg)?; - let old_balance = caller_account.caller_initial_modification(new_balance, tx.kind().is_call()); + let new_balance = calculate_caller_fee(*caller.balance(), tx, block, cfg)?; - journal.caller_accounting_journal_entry(tx.caller(), old_balance, tx.kind().is_call()); + caller.set_balance(new_balance); + if tx.kind().is_call() { + caller.bump_nonce(); + } Ok(()) } /// Apply EIP-7702 auth list and return number gas refund on already created accounts. +/// +/// Note that this function will do nothing if the transaction type is not EIP-7702. +/// If you need to apply auth list for other transaction types, use [`apply_auth_list`] function. +/// +/// Internally uses [`apply_auth_list`] function. #[inline] pub fn apply_eip7702_auth_list< CTX: ContextTr, @@ -176,17 +190,30 @@ pub fn apply_eip7702_auth_list< >( context: &mut CTX, ) -> Result { - let tx = context.tx(); - // Return if there is no auth list. + let chain_id = context.cfg().chain_id(); + let (tx, journal) = context.tx_journal_mut(); + + // Return if not EIP-7702 transaction. if tx.tx_type() != TransactionType::Eip7702 { return Ok(0); } + apply_auth_list(chain_id, tx.authorization_list(), journal) +} - let chain_id = context.cfg().chain_id(); - let (tx, journal) = context.tx_journal_mut(); - +/// Apply EIP-7702 style auth list and return number gas refund on already created accounts. +/// +/// It is more granular function from [`apply_eip7702_auth_list`] function as it takes only the list, journal and chain id. +#[inline] +pub fn apply_auth_list< + JOURNAL: JournalTr, + ERROR: From + From<::Error>, +>( + chain_id: u64, + auth_list: impl Iterator, + journal: &mut JOURNAL, +) -> Result { let mut refunded_accounts = 0; - for authorization in tx.authorization_list() { + for authorization in auth_list { // 1. Verify the chain id is either 0 or the chain's current ID. let auth_chain_id = authorization.chain_id(); if !auth_chain_id.is_zero() && auth_chain_id != U256::from(chain_id) { @@ -206,10 +233,11 @@ pub fn apply_eip7702_auth_list< // warm authority account and check nonce. // 4. Add `authority` to `accessed_addresses` (as defined in [EIP-2929](./eip-2929.md).) - let mut authority_acc = journal.load_account_code(authority)?; + let mut authority_acc = journal.load_account_with_code_mut(authority)?; + let authority_acc_info = &authority_acc.account().info; // 5. Verify the code of `authority` is either empty or already delegated. - if let Some(bytecode) = &authority_acc.info.code { + if let Some(bytecode) = &authority_acc_info.code { // if it is not empty and it is not eip7702 if !bytecode.is_empty() && !bytecode.is_eip7702() { continue; @@ -217,32 +245,24 @@ pub fn apply_eip7702_auth_list< } // 6. Verify the nonce of `authority` is equal to `nonce`. In case `authority` does not exist in the trie, verify that `nonce` is equal to `0`. - if authorization.nonce() != authority_acc.info.nonce { + if authorization.nonce() != authority_acc_info.nonce { continue; } // 7. Add `PER_EMPTY_ACCOUNT_COST - PER_AUTH_BASE_COST` gas to the global refund counter if `authority` exists in the trie. - if !(authority_acc.is_empty() && authority_acc.is_loaded_as_not_existing_not_touched()) { + if !(authority_acc_info.is_empty() + && authority_acc + .account() + .is_loaded_as_not_existing_not_touched()) + { refunded_accounts += 1; } // 8. Set the code of `authority` to be `0xef0100 || address`. This is a delegation designation. // * As a special case, if `address` is `0x0000000000000000000000000000000000000000` do not write the designation. // Clear the accounts code and reset the account's code hash to the empty hash `0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470`. - let address = authorization.address(); - let (bytecode, hash) = if address.is_zero() { - (Bytecode::default(), KECCAK_EMPTY) - } else { - let bytecode = Bytecode::new_eip7702(address); - let hash = bytecode.hash_slow(); - (bytecode, hash) - }; - authority_acc.info.code_hash = hash; - authority_acc.info.code = Some(bytecode); - // 9. Increase the nonce of `authority` by one. - authority_acc.info.nonce = authority_acc.info.nonce.saturating_add(1); - authority_acc.mark_touch(); + authority_acc.delegate(authorization.address()); } let refunded_gas = diff --git a/crates/handler/src/precompile_provider.rs b/crates/handler/src/precompile_provider.rs index b6224c6a4e..dab165140f 100644 --- a/crates/handler/src/precompile_provider.rs +++ b/crates/handler/src/precompile_provider.rs @@ -2,11 +2,12 @@ use auto_impl::auto_impl; use context::{Cfg, LocalContextTr}; use context_interface::{ContextTr, JournalTr}; use interpreter::{CallInput, CallInputs, Gas, InstructionResult, InterpreterResult}; -use precompile::PrecompileError; -use precompile::{PrecompileSpecId, Precompiles}; +use precompile::{PrecompileError, PrecompileSpecId, Precompiles}; use primitives::{hardfork::SpecId, Address, Bytes}; -use std::boxed::Box; -use std::string::{String, ToString}; +use std::{ + boxed::Box, + string::{String, ToString}, +}; /// Provider for precompiled contracts in the EVM. #[auto_impl(&mut, Box)] @@ -43,6 +44,14 @@ pub struct EthPrecompiles { } impl EthPrecompiles { + /// Create a new precompile provider with the given spec. + pub fn new(spec: SpecId) -> Self { + Self { + precompiles: Precompiles::new(PrecompileSpecId::from_spec_id(spec)), + spec, + } + } + /// Returns addresses of the precompiles. pub fn warm_addresses(&self) -> Box> { Box::new(self.precompiles.addresses().cloned()) @@ -120,6 +129,7 @@ impl PrecompileProvider for EthPrecompiles { match exec_result { Ok(output) => { + result.gas.record_refund(output.gas_refunded); let underflow = result.gas.record_cost(output.gas_used); assert!(underflow, "Gas underflow is not possible"); result.result = if output.reverted { diff --git a/crates/handler/src/validation.rs b/crates/handler/src/validation.rs index 0f53144edd..e5d173181a 100644 --- a/crates/handler/src/validation.rs +++ b/crates/handler/src/validation.rs @@ -1,10 +1,11 @@ use context_interface::{ + cfg::GasParams, result::{InvalidHeader, InvalidTransaction}, - transaction::{Transaction, TransactionType}, + transaction::{AccessListItemTr as _, Transaction, TransactionType}, Block, Cfg, ContextTr, }; use core::cmp; -use interpreter::gas::{self, InitialAndFloorGas}; +use interpreter::InitialAndFloorGas; use primitives::{eip4844, hardfork::SpecId, B256}; /// Validates the execution environment including block and transaction parameters. @@ -61,6 +62,21 @@ pub fn validate_priority_fee_tx( Ok(()) } +/// Validate priority fee for transactions that support EIP-1559 (Eip1559, Eip4844, Eip7702). +#[inline] +fn validate_priority_fee_for_tx( + tx: TX, + base_fee: Option, + disable_priority_fee_check: bool, +) -> Result<(), InvalidTransaction> { + validate_priority_fee_tx( + tx.max_fee_per_gas(), + tx.max_priority_fee_per_gas().unwrap_or_default(), + base_fee, + disable_priority_fee_check, + ) +} + /// Validate EIP-4844 transaction. pub fn validate_eip4844_tx( blobs: &[B256], @@ -107,8 +123,8 @@ pub fn validate_tx_env( spec_id: SpecId, ) -> Result<(), InvalidTransaction> { // Check if the transaction's chain id is correct - let tx_type = context.tx().tx_type(); let tx = context.tx(); + let tx_type = tx.tx_type(); let base_fee = if context.cfg().is_base_fee_check_disabled() { None @@ -157,24 +173,14 @@ pub fn validate_tx_env( if !spec_id.is_enabled_in(SpecId::LONDON) { return Err(InvalidTransaction::Eip1559NotSupported); } - validate_priority_fee_tx( - tx.max_fee_per_gas(), - tx.max_priority_fee_per_gas().unwrap_or_default(), - base_fee, - disable_priority_fee_check, - )?; + validate_priority_fee_for_tx(tx, base_fee, disable_priority_fee_check)?; } TransactionType::Eip4844 => { if !spec_id.is_enabled_in(SpecId::CANCUN) { return Err(InvalidTransaction::Eip4844NotSupported); } - validate_priority_fee_tx( - tx.max_fee_per_gas(), - tx.max_priority_fee_per_gas().unwrap_or_default(), - base_fee, - disable_priority_fee_check, - )?; + validate_priority_fee_for_tx(tx, base_fee, disable_priority_fee_check)?; validate_eip4844_tx( tx.blob_versioned_hashes(), @@ -189,12 +195,7 @@ pub fn validate_tx_env( return Err(InvalidTransaction::Eip7702NotSupported); } - validate_priority_fee_tx( - tx.max_fee_per_gas(), - tx.max_priority_fee_per_gas().unwrap_or_default(), - base_fee, - disable_priority_fee_check, - )?; + validate_priority_fee_for_tx(tx, base_fee, disable_priority_fee_check)?; let auth_list_len = tx.authorization_list_len(); // The transaction is considered invalid if the length of authorization_list is zero. @@ -216,7 +217,7 @@ pub fn validate_tx_env( // EIP-3860: Limit and meter initcode. Still valid with EIP-7907 and increase of initcode size. if spec_id.is_enabled_in(SpecId::SHANGHAI) && tx.kind().is_create() - && context.tx().input().len() > context.cfg().max_initcode_size() + && tx.input().len() > context.cfg().max_initcode_size() { return Err(InvalidTransaction::CreateInitCodeSizeLimit); } @@ -227,13 +228,29 @@ pub fn validate_tx_env( /// Validate initial transaction gas. pub fn validate_initial_tx_gas( tx: impl Transaction, - spec: SpecId, + _spec: SpecId, + gas_params: &GasParams, is_eip7623_disabled: bool, - is_eip7702_enabled: bool, - is_eip7623_enabled: bool, ) -> Result { - let mut gas = - gas::calculate_initial_tx_gas_for_tx(&tx, spec, is_eip7702_enabled, is_eip7623_enabled); + let (accounts, storages) = if tx.tx_type() != TransactionType::Legacy as u8 { + tx.access_list() + .map(|al| { + al.fold((0u64, 0u64), |(a, s), item| { + (a + 1, s + item.storage_slots().count() as u64) + }) + }) + .unwrap_or_default() + } else { + (0, 0) + }; + + let mut gas = gas_params.initial_tx_gas( + tx.input(), + tx.kind().is_create(), + accounts, + storages, + tx.authorization_list_len() as u64, + ); if is_eip7623_disabled { gas.floor_gas = 0 @@ -247,15 +264,13 @@ pub fn validate_initial_tx_gas( }); } - // EIP-7623: Increase calldata cost - // floor gas should be less than gas limit. - if (spec.is_enabled_in(SpecId::PRAGUE) || is_eip7623_enabled) && gas.floor_gas > tx.gas_limit() - { + // EIP-7623: floor_gas > 0 means EIP-7623 is active (either via spec or custom GasParams) + if gas.floor_gas > 0 && gas.floor_gas > tx.gas_limit() { return Err(InvalidTransaction::GasFloorMoreThanGasLimit { gas_floor: gas.floor_gas, gas_limit: tx.gas_limit(), }); - }; + } Ok(gas) } @@ -279,7 +294,7 @@ mod tests { let ctx = Context::mainnet() .modify_cfg_chained(|c| { if let Some(spec_id) = spec_id { - c.spec = spec_id; + c.set_spec_and_mainnet_gas_params(spec_id); } }) .with_db(CacheDB::::default()); diff --git a/crates/inspector/CHANGELOG.md b/crates/inspector/CHANGELOG.md index 6b388ff89c..eb3996e0d9 100644 --- a/crates/inspector/CHANGELOG.md +++ b/crates/inspector/CHANGELOG.md @@ -7,6 +7,65 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [15.0.0](https://github.com/bluealloy/revm/compare/revm-inspector-v14.1.0...revm-inspector-v15.0.0) - 2026-01-15 + +### Added + +- *(inspector)* make TestInspector public for testing EVM variants ([#3282](https://github.com/bluealloy/revm/pull/3282)) +- move GasParams to Cfg ([#3229](https://github.com/bluealloy/revm/pull/3229)) +- BAL EIP-7928 ([#3070](https://github.com/bluealloy/revm/pull/3070)) +- Gas params ([#3132](https://github.com/bluealloy/revm/pull/3132)) + +### Other + +- fix typos, grammar errors, and improve documentation consistency ([#3294](https://github.com/bluealloy/revm/pull/3294)) +- happy new year, 2026 licence ([#3272](https://github.com/bluealloy/revm/pull/3272)) +- replace HashMap with fixed-size array for opcode counts in CountInspector ([#3203](https://github.com/bluealloy/revm/pull/3203)) +- *(fmt)* merge all imports ([#3184](https://github.com/bluealloy/revm/pull/3184)) + +## [14.1.0](https://github.com/bluealloy/revm/compare/revm-inspector-v14.0.0...revm-inspector-v14.1.0) - 2025-11-14 + +### Other + +- call Inspector::log_full ([#3164](https://github.com/bluealloy/revm/pull/3164)) + +## [14.0.0](https://github.com/bluealloy/revm/compare/revm-inspector-v12.0.2...revm-inspector-v14.0.0) - 2025-11-10 + +### Added + +- process precompile logs to inspector ([#3148](https://github.com/bluealloy/revm/pull/3148)) + +## [12.0.2](https://github.com/bluealloy/revm/compare/revm-inspector-v12.0.1...revm-inspector-v12.0.2) - 2025-11-10 + +### Other + +- updated the following local packages: revm-database, revm-context, revm-handler + +## [12.0.1](https://github.com/bluealloy/revm/compare/revm-inspector-v12.0.0...revm-inspector-v12.0.1) - 2025-11-07 + +### Other + +- updated the following local packages: revm-primitives, revm-context, revm-state, revm-database-interface, revm-database, revm-interpreter, revm-handler + +## [12.0.0](https://github.com/bluealloy/revm/compare/revm-inspector-v11.2.0...revm-inspector-v12.0.0) - 2025-10-30 + +### Other + +- remove redundant alloy-eip7702 from handler dev-dependencies ([#3105](https://github.com/bluealloy/revm/pull/3105)) +- *(inspector)* remove redundant EthInterpreter import in either.rs ([#3093](https://github.com/bluealloy/revm/pull/3093)) + +## [11.2.0](https://github.com/bluealloy/revm/compare/revm-inspector-v11.1.2...revm-inspector-v11.2.0) - 2025-10-17 + +### Other + +- updated the following local packages: revm-interpreter, revm-handler + +## [11.1.2](https://github.com/bluealloy/revm/compare/revm-inspector-v11.1.1...revm-inspector-v11.1.2) - 2025-10-15 + +### Other + +- updated the following local packages: revm-state, revm-database-interface, revm-context, revm-database, revm-interpreter, revm-handler + ## [11.1.1](https://github.com/bluealloy/revm/compare/revm-inspector-v11.1.0...revm-inspector-v11.1.1) - 2025-10-15 ### Other diff --git a/crates/inspector/Cargo.toml b/crates/inspector/Cargo.toml index 613f630877..190babcd3e 100644 --- a/crates/inspector/Cargo.toml +++ b/crates/inspector/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "revm-inspector" description = "Revm inspector interface" -version = "11.1.1" +version = "15.0.0" authors.workspace = true edition.workspace = true keywords.workspace = true @@ -57,8 +57,8 @@ std = [ ] serde = [ "dep:serde", - "database/serde", "context/serde", + "database/serde", "database-interface/serde", "handler/serde", "interpreter/serde", diff --git a/crates/inspector/LICENSE b/crates/inspector/LICENSE index be6d350ebe..7edb0405ff 100644 --- a/crates/inspector/LICENSE +++ b/crates/inspector/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021-2025 draganrakita +Copyright (c) 2021-2026 draganrakita Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/crates/inspector/src/count_inspector.rs b/crates/inspector/src/count_inspector.rs index 37a99832c5..2068cbc285 100644 --- a/crates/inspector/src/count_inspector.rs +++ b/crates/inspector/src/count_inspector.rs @@ -1,13 +1,13 @@ //! CountInspector - Inspector that counts all opcodes that were called. use crate::inspector::Inspector; use interpreter::{interpreter_types::Jumps, InterpreterTypes}; -use primitives::HashMap; +use primitives::Log; /// Inspector that counts all opcodes that were called during execution. -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug)] pub struct CountInspector { - /// Map from opcode value to count of times it was executed. - opcode_counts: HashMap, + /// Fixed array keyed by opcode value to count executions. + opcode_counts: [u64; 256], /// Count of initialize_interp calls. initialize_interp_count: u64, /// Count of step calls. @@ -28,6 +28,23 @@ pub struct CountInspector { selfdestruct_count: u64, } +impl Default for CountInspector { + fn default() -> Self { + Self { + opcode_counts: [0; 256], + initialize_interp_count: 0, + step_count: 0, + step_end_count: 0, + log_count: 0, + call_count: 0, + call_end_count: 0, + create_count: 0, + create_end_count: 0, + selfdestruct_count: 0, + } + } +} + impl CountInspector { /// Create a new CountInspector. pub fn new() -> Self { @@ -36,27 +53,30 @@ impl CountInspector { /// Get the count for a specific opcode. pub fn get_count(&self, opcode: u8) -> u64 { - self.opcode_counts.get(&opcode).copied().unwrap_or_default() + self.opcode_counts[opcode as usize] } /// Get a reference to all opcode counts. - pub fn opcode_counts(&self) -> &HashMap { + pub fn opcode_counts(&self) -> &[u64; 256] { &self.opcode_counts } /// Get the total number of opcodes executed. pub fn total_opcodes(&self) -> u64 { - self.opcode_counts.values().sum() + self.opcode_counts.iter().copied().sum() } /// Get the number of unique opcodes executed. pub fn unique_opcodes(&self) -> usize { - self.opcode_counts.len() + self.opcode_counts + .iter() + .filter(|&&count| count > 0) + .count() } /// Clear all counts. pub fn clear(&mut self) { - self.opcode_counts.clear(); + self.opcode_counts = [0; 256]; self.initialize_interp_count = 0; self.step_count = 0; self.step_end_count = 0; @@ -126,19 +146,14 @@ impl Inspector for CountInspector { fn step(&mut self, interp: &mut interpreter::Interpreter, _context: &mut CTX) { self.step_count += 1; let opcode = interp.bytecode.opcode(); - *self.opcode_counts.entry(opcode).or_insert(0) += 1; + self.opcode_counts[opcode as usize] += 1; } fn step_end(&mut self, _interp: &mut interpreter::Interpreter, _context: &mut CTX) { self.step_end_count += 1; } - fn log( - &mut self, - _interp: &mut interpreter::Interpreter, - _context: &mut CTX, - _log: primitives::Log, - ) { + fn log(&mut self, _context: &mut CTX, _log: Log) { self.log_count += 1; } @@ -258,8 +273,8 @@ mod tests { let mut inspector = CountInspector::new(); // Add some counts manually for testing - *inspector.opcode_counts.entry(opcode::PUSH1).or_insert(0) += 5; - *inspector.opcode_counts.entry(opcode::ADD).or_insert(0) += 3; + inspector.opcode_counts[opcode::PUSH1 as usize] += 5; + inspector.opcode_counts[opcode::ADD as usize] += 3; inspector.initialize_interp_count = 2; inspector.step_count = 10; inspector.step_end_count = 10; @@ -279,7 +294,6 @@ mod tests { inspector.clear(); assert_eq!(inspector.total_opcodes(), 0); assert_eq!(inspector.unique_opcodes(), 0); - assert!(inspector.opcode_counts().is_empty()); assert_eq!(inspector.initialize_interp_count(), 0); assert_eq!(inspector.step_count(), 0); assert_eq!(inspector.step_end_count(), 0); @@ -289,6 +303,7 @@ mod tests { assert_eq!(inspector.create_count(), 0); assert_eq!(inspector.create_end_count(), 0); assert_eq!(inspector.selfdestruct_count(), 0); + assert!(inspector.opcode_counts().iter().all(|&count| count == 0)); } #[test] diff --git a/crates/inspector/src/eip3155.rs b/crates/inspector/src/eip3155.rs index 841e12eede..3d9c9d86a1 100644 --- a/crates/inspector/src/eip3155.rs +++ b/crates/inspector/src/eip3155.rs @@ -1,5 +1,4 @@ -use crate::inspectors::GasInspector; -use crate::Inspector; +use crate::{inspectors::GasInspector, Inspector}; use context::{Cfg, ContextTr, JournalTr, Transaction}; use interpreter::{ interpreter_types::{Jumps, LoopControl, MemoryTr, StackTr}, diff --git a/crates/inspector/src/either.rs b/crates/inspector/src/either.rs index 7b4da7249b..8f3c8c5ca6 100644 --- a/crates/inspector/src/either.rs +++ b/crates/inspector/src/either.rs @@ -35,10 +35,18 @@ where } #[inline] - fn log(&mut self, interp: &mut Interpreter, context: &mut CTX, log: Log) { + fn log(&mut self, context: &mut CTX, log: Log) { match self { - Either::Left(inspector) => inspector.log(interp, context, log), - Either::Right(inspector) => inspector.log(interp, context, log), + Either::Left(inspector) => inspector.log(context, log), + Either::Right(inspector) => inspector.log(context, log), + } + } + + #[inline] + fn log_full(&mut self, interp: &mut Interpreter, context: &mut CTX, log: Log) { + match self { + Either::Left(inspector) => inspector.log_full(interp, context, log), + Either::Right(inspector) => inspector.log_full(interp, context, log), } } @@ -101,8 +109,6 @@ mod tests { #[test] fn test_either_inspector_type_check() { - use interpreter::interpreter::EthInterpreter; - // This test verifies that Either // implements the Inspector trait as required by the issue fn _requires_inspector>(inspector: T) -> T { diff --git a/crates/inspector/src/handler.rs b/crates/inspector/src/handler.rs index aad34460cc..53bdf436db 100644 --- a/crates/inspector/src/handler.rs +++ b/crates/inspector/src/handler.rs @@ -1,5 +1,5 @@ use crate::{Inspector, InspectorEvmTr, JournalExt}; -use context::{result::ExecutionResult, ContextTr, JournalEntry, Transaction}; +use context::{result::ExecutionResult, ContextTr, JournalEntry, JournalTr, Transaction}; use handler::{evm::FrameTr, EvmTr, FrameResult, Handler, ItemOrResult}; use interpreter::{ instructions::InstructionTable, @@ -260,7 +260,7 @@ fn inspect_log( } let log = context.journal_mut().logs().last().unwrap().clone(); - inspector.log(interpreter, context, log); + inspector.log_full(interpreter, context, log); } #[inline(never)] diff --git a/crates/inspector/src/inspector.rs b/crates/inspector/src/inspector.rs index 0df83be214..0c8dd19933 100644 --- a/crates/inspector/src/inspector.rs +++ b/crates/inspector/src/inspector.rs @@ -47,14 +47,23 @@ pub trait Inspector { let _ = context; } - /// Called when a log is emitted. + /// Called when a log is emitted, called on every new log. + /// If there is a needs for Interpreter context, use [`Inspector::log_full`] instead. #[inline] - fn log(&mut self, interp: &mut Interpreter, context: &mut CTX, log: Log) { - let _ = interp; + fn log(&mut self, context: &mut CTX, log: Log) { let _ = context; let _ = log; } + /// Called when a log is emitted with the interpreter context. + /// + /// This will not happen only if custom precompiles where logs will be + /// gethered after precompile call. + fn log_full(&mut self, interpreter: &mut Interpreter, context: &mut CTX, log: Log) { + let _ = interpreter; + self.log(context, log); + } + /// Called whenever a call to a contract is about to start. /// /// Returning `CallOutcome` will override the result of the call. @@ -133,9 +142,14 @@ where self.1.step_end(interp, context); } - fn log(&mut self, interp: &mut Interpreter, context: &mut CTX, log: Log) { - self.0.log(interp, context, log.clone()); - self.1.log(interp, context, log); + fn log(&mut self, context: &mut CTX, log: Log) { + self.0.log(context, log.clone()); + self.1.log(context, log); + } + + fn log_full(&mut self, interp: &mut Interpreter, context: &mut CTX, log: Log) { + self.0.log_full(interp, context, log.clone()); + self.1.log_full(interp, context, log); } fn call(&mut self, context: &mut CTX, inputs: &mut CallInputs) -> Option { @@ -174,9 +188,6 @@ where /// Extends the journal with additional methods that are used by the inspector. #[auto_impl(&mut, Box)] pub trait JournalExt { - /// Get all logs from the journal. - fn logs(&self) -> &[Log]; - /// Get the journal entries that are created from last checkpoint. /// new checkpoint is created when sub call is made. fn journal(&self) -> &[JournalEntry]; @@ -189,11 +200,6 @@ pub trait JournalExt { } impl JournalExt for Journal { - #[inline] - fn logs(&self) -> &[Log] { - &self.logs - } - #[inline] fn journal(&self) -> &[JournalEntry] { &self.journal diff --git a/crates/inspector/src/inspector_tests.rs b/crates/inspector/src/inspector_tests.rs index 5916a75c86..8d9d13ae50 100644 --- a/crates/inspector/src/inspector_tests.rs +++ b/crates/inspector/src/inspector_tests.rs @@ -1,186 +1,12 @@ #[cfg(test)] mod tests { - use crate::{InspectEvm, InspectSystemCallEvm, Inspector}; + use crate::{InspectEvm, InspectSystemCallEvm, InspectorEvent, TestInspector}; use context::{Context, TxEnv}; use database::{BenchmarkDB, BENCH_CALLER, BENCH_TARGET}; use handler::{MainBuilder, MainContext}; - use interpreter::{ - interpreter_types::{Jumps, MemoryTr, StackTr}, - CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter, InterpreterTypes, - }; - use primitives::{address, Address, Bytes, Log, TxKind, U256}; + use primitives::{address, Address, Bytes, TxKind, U256}; use state::{bytecode::opcode, AccountInfo, Bytecode}; - #[derive(Debug, Clone)] - struct InterpreterState { - pc: usize, - stack_len: usize, - memory_size: usize, - } - - #[derive(Debug, Clone)] - struct StepRecord { - before: InterpreterState, - after: Option, - opcode_name: String, - } - - #[derive(Debug, Clone)] - enum InspectorEvent { - Step(StepRecord), - Call { - inputs: CallInputs, - outcome: Option, - }, - Create { - inputs: CreateInputs, - outcome: Option, - }, - Log(Log), - Selfdestruct { - address: Address, - beneficiary: Address, - value: U256, - }, - } - - #[derive(Debug, Default)] - struct TestInspector { - events: Vec, - step_count: usize, - call_depth: usize, - } - - impl TestInspector { - fn new() -> Self { - Self { - events: Vec::new(), - step_count: 0, - call_depth: 0, - } - } - - fn capture_interpreter_state( - interp: &Interpreter, - ) -> InterpreterState - where - INTR::Bytecode: Jumps, - INTR::Stack: StackTr, - INTR::Memory: MemoryTr, - { - InterpreterState { - pc: interp.bytecode.pc(), - stack_len: interp.stack.len(), - memory_size: interp.memory.size(), - } - } - - fn get_events(&self) -> Vec { - self.events.clone() - } - - fn get_step_count(&self) -> usize { - self.step_count - } - } - - impl Inspector for TestInspector - where - INTR: InterpreterTypes, - INTR::Bytecode: Jumps, - INTR::Stack: StackTr, - INTR::Memory: MemoryTr, - { - fn step(&mut self, interp: &mut Interpreter, _context: &mut CTX) { - self.step_count += 1; - - let state = Self::capture_interpreter_state(interp); - let opcode = interp.bytecode.opcode(); - let opcode_name = if let Some(op) = state::bytecode::opcode::OpCode::new(opcode) { - format!("{op}") - } else { - format!("Unknown(0x{opcode:02x})") - }; - - self.events.push(InspectorEvent::Step(StepRecord { - before: state, - after: None, - opcode_name, - })); - } - - fn step_end(&mut self, interp: &mut Interpreter, _context: &mut CTX) { - let state = Self::capture_interpreter_state(interp); - - if let Some(InspectorEvent::Step(record)) = self.events.last_mut() { - record.after = Some(state); - } - } - - fn log(&mut self, _interp: &mut Interpreter, _ctx: &mut CTX, log: Log) { - self.events.push(InspectorEvent::Log(log)); - } - - fn call(&mut self, _ctx: &mut CTX, inputs: &mut CallInputs) -> Option { - self.call_depth += 1; - self.events.push(InspectorEvent::Call { - inputs: inputs.clone(), - outcome: None, - }); - None - } - - fn call_end(&mut self, _ctx: &mut CTX, _inputs: &CallInputs, outcome: &mut CallOutcome) { - self.call_depth -= 1; - if let Some(InspectorEvent::Call { - outcome: ref mut out, - .. - }) = self - .events - .iter_mut() - .rev() - .find(|e| matches!(e, InspectorEvent::Call { outcome: None, .. })) - { - *out = Some(outcome.clone()); - } - } - - fn create(&mut self, _ctx: &mut CTX, inputs: &mut CreateInputs) -> Option { - self.events.push(InspectorEvent::Create { - inputs: inputs.clone(), - outcome: None, - }); - None - } - - fn create_end( - &mut self, - _ctx: &mut CTX, - _inputs: &CreateInputs, - outcome: &mut CreateOutcome, - ) { - if let Some(InspectorEvent::Create { - outcome: ref mut out, - .. - }) = self - .events - .iter_mut() - .rev() - .find(|e| matches!(e, InspectorEvent::Create { outcome: None, .. })) - { - *out = Some(outcome.clone()); - } - } - - fn selfdestruct(&mut self, contract: Address, beneficiary: Address, value: U256) { - self.events.push(InspectorEvent::Selfdestruct { - address: contract, - beneficiary, - value, - }); - } - } - #[test] fn test_push_opcodes_and_stack_operations() { // PUSH1 0x42, PUSH2 0x1234, ADD, PUSH1 0x00, MSTORE, STOP @@ -384,6 +210,7 @@ mod tests { nonce: 0, code_hash: primitives::keccak256(&caller_code), code: Some(Bytecode::new_raw(caller_code)), + ..Default::default() }, ); @@ -399,6 +226,7 @@ mod tests { nonce: 0, code_hash: primitives::keccak256(&callee_code), code: Some(Bytecode::new_raw(callee_code)), + ..Default::default() }, ); diff --git a/crates/inspector/src/lib.rs b/crates/inspector/src/lib.rs index 35338857bb..e8df6ccb5b 100644 --- a/crates/inspector/src/lib.rs +++ b/crates/inspector/src/lib.rs @@ -16,6 +16,8 @@ mod inspect; mod inspector; mod mainnet_inspect; mod noop; +/// Test inspector for testing EVM execution. +pub mod test_inspector; mod traits; #[cfg(test)] @@ -33,6 +35,7 @@ pub use handler::{inspect_instructions, InspectorHandler}; pub use inspect::{InspectCommitEvm, InspectEvm, InspectSystemCallEvm}; pub use inspector::*; pub use noop::NoOpInspector; +pub use test_inspector::{InspectorEvent, InterpreterState, StepRecord, TestInspector}; pub use traits::*; #[cfg(test)] diff --git a/crates/inspector/src/test_inspector.rs b/crates/inspector/src/test_inspector.rs new file mode 100644 index 0000000000..4aaf47108b --- /dev/null +++ b/crates/inspector/src/test_inspector.rs @@ -0,0 +1,293 @@ +//! Test inspector for testing EVM execution. + +extern crate alloc; + +use crate::Inspector; +use alloc::{format, string::String, vec::Vec}; +use interpreter::{ + interpreter_types::{Jumps, MemoryTr, StackTr}, + CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter, InterpreterTypes, +}; +use primitives::{Address, Log, U256}; + +/// Interpreter state at a specific point in execution. +#[derive(Debug, Clone)] +pub struct InterpreterState { + /// Program counter. + pub pc: usize, + /// Stack length. + pub stack_len: usize, + /// Memory size. + pub memory_size: usize, +} + +/// Step execution record. +#[derive(Debug, Clone)] +pub struct StepRecord { + /// State before instruction execution. + pub before: InterpreterState, + /// State after instruction execution. + pub after: Option, + /// Opcode name. + pub opcode_name: String, +} + +/// Events captured during EVM execution. +#[derive(Debug, Clone)] +pub enum InspectorEvent { + /// Execution step. + Step(StepRecord), + /// Call operation. + Call { + /// Call inputs. + inputs: CallInputs, + /// Call outcome. + outcome: Option, + }, + /// Create operation. + Create { + /// Create inputs. + inputs: CreateInputs, + /// Create outcome. + outcome: Option, + }, + /// Log emission. + Log(Log), + /// Selfdestruct operation. + Selfdestruct { + /// Contract address. + address: Address, + /// Beneficiary address. + beneficiary: Address, + /// Value transferred. + value: U256, + }, +} + +/// Test inspector that records execution events. +#[derive(Debug, Default)] +pub struct TestInspector { + /// Captured events. + pub events: Vec, + /// Total step count. + pub step_count: usize, + /// Current call depth. + pub call_depth: usize, +} + +impl TestInspector { + /// Create a new TestInspector. + pub fn new() -> Self { + Self::default() + } + + fn capture_interpreter_state( + interp: &Interpreter, + ) -> InterpreterState + where + INTR::Bytecode: Jumps, + INTR::Stack: StackTr, + INTR::Memory: MemoryTr, + { + InterpreterState { + pc: interp.bytecode.pc(), + stack_len: interp.stack.len(), + memory_size: interp.memory.size(), + } + } + + /// Get all captured events. + pub fn get_events(&self) -> Vec { + self.events.clone() + } + + /// Get the total step count. + pub fn get_step_count(&self) -> usize { + self.step_count + } +} + +impl Inspector for TestInspector +where + INTR: InterpreterTypes, + INTR::Bytecode: Jumps, + INTR::Stack: StackTr, + INTR::Memory: MemoryTr, +{ + fn step(&mut self, interp: &mut Interpreter, _context: &mut CTX) { + self.step_count += 1; + + let state = Self::capture_interpreter_state(interp); + let opcode = interp.bytecode.opcode(); + let opcode_name = if let Some(op) = state::bytecode::opcode::OpCode::new(opcode) { + format!("{op}") + } else { + format!("Unknown(0x{opcode:02x})") + }; + + self.events.push(InspectorEvent::Step(StepRecord { + before: state, + after: None, + opcode_name, + })); + } + + fn step_end(&mut self, interp: &mut Interpreter, _context: &mut CTX) { + let state = Self::capture_interpreter_state(interp); + + if let Some(InspectorEvent::Step(record)) = self.events.last_mut() { + record.after = Some(state); + } + } + + fn log(&mut self, _ctx: &mut CTX, log: Log) { + self.events.push(InspectorEvent::Log(log)); + } + + fn call(&mut self, _ctx: &mut CTX, inputs: &mut CallInputs) -> Option { + self.call_depth += 1; + self.events.push(InspectorEvent::Call { + inputs: inputs.clone(), + outcome: None, + }); + None + } + + fn call_end(&mut self, _ctx: &mut CTX, _inputs: &CallInputs, outcome: &mut CallOutcome) { + self.call_depth -= 1; + if let Some(InspectorEvent::Call { + outcome: ref mut out, + .. + }) = self + .events + .iter_mut() + .rev() + .find(|e| matches!(e, InspectorEvent::Call { outcome: None, .. })) + { + *out = Some(outcome.clone()); + } + } + + fn create(&mut self, _ctx: &mut CTX, inputs: &mut CreateInputs) -> Option { + self.events.push(InspectorEvent::Create { + inputs: inputs.clone(), + outcome: None, + }); + None + } + + fn create_end(&mut self, _ctx: &mut CTX, _inputs: &CreateInputs, outcome: &mut CreateOutcome) { + if let Some(InspectorEvent::Create { + outcome: ref mut out, + .. + }) = self + .events + .iter_mut() + .rev() + .find(|e| matches!(e, InspectorEvent::Create { outcome: None, .. })) + { + *out = Some(outcome.clone()); + } + } + + fn selfdestruct(&mut self, contract: Address, beneficiary: Address, value: U256) { + self.events.push(InspectorEvent::Selfdestruct { + address: contract, + beneficiary, + value, + }); + } +} + +/// Default tests for EVM implementations. +#[cfg(feature = "std")] +pub mod default_tests { + use super::*; + use alloc::{string::ToString, vec, vec::Vec}; + use primitives::Bytes; + use state::bytecode::opcode; + + /// Run default test suite on an EVM implementation. + /// The execute function should set up the EVM, run the bytecode, and return the TestInspector. + pub fn run_tests(mut execute: F) -> Result<(), Vec<(&'static str, String)>> + where + F: FnMut(Bytes) -> Result, + { + let mut failures = Vec::new(); + + // Test basic stack operations: PUSH, ADD, MSTORE + let stack_test = Bytes::from(vec![ + opcode::PUSH1, + 0x42, + opcode::PUSH1, + 0x10, + opcode::ADD, + opcode::PUSH1, + 0x00, + opcode::MSTORE, + opcode::STOP, + ]); + + match execute(stack_test) { + Ok(inspector) => { + if inspector.step_count < 5 { + failures.push(("stack_operations", "Not enough steps recorded".to_string())); + } + } + Err(e) => failures.push(("stack_operations", e)), + } + + // Test JUMP control flow + let jump_test = Bytes::from(vec![ + opcode::PUSH1, + 0x05, + opcode::JUMP, + opcode::INVALID, + opcode::INVALID, + opcode::JUMPDEST, + opcode::STOP, + ]); + + match execute(jump_test) { + Ok(inspector) => { + let has_jump = inspector + .events + .iter() + .any(|e| matches!(e, InspectorEvent::Step(s) if s.opcode_name == "JUMP")); + if !has_jump { + failures.push(("jump", "JUMP not recorded".to_string())); + } + } + Err(e) => failures.push(("jump", e)), + } + + // Test LOG0 + let log_test = Bytes::from(vec![ + opcode::PUSH1, + 0x20, + opcode::PUSH1, + 0x00, + opcode::LOG0, + opcode::STOP, + ]); + + match execute(log_test) { + Ok(inspector) => { + let has_log = inspector + .events + .iter() + .any(|e| matches!(e, InspectorEvent::Log(_))); + if !has_log { + failures.push(("log", "LOG0 not recorded".to_string())); + } + } + Err(e) => failures.push(("log", e)), + } + + if failures.is_empty() { + Ok(()) + } else { + Err(failures) + } + } +} diff --git a/crates/inspector/src/traits.rs b/crates/inspector/src/traits.rs index bcd395acfc..b55f66b0be 100644 --- a/crates/inspector/src/traits.rs +++ b/crates/inspector/src/traits.rs @@ -1,10 +1,12 @@ -use context::{ContextTr, FrameStack}; +use context::{ContextTr, FrameStack, JournalTr}; use handler::{ evm::{ContextDbError, FrameInitResult, FrameTr}, instructions::InstructionProvider, EthFrame, EvmTr, FrameInitOrResult, FrameResult, ItemOrResult, }; -use interpreter::{interpreter::EthInterpreter, interpreter_action::FrameInit, InterpreterTypes}; +use interpreter::{ + interpreter::EthInterpreter, interpreter_action::FrameInit, CallOutcome, InterpreterTypes, +}; use crate::{ handler::{frame_end, frame_start}, @@ -104,8 +106,23 @@ pub trait InspectorEvmTr: } let frame_input = frame_init.frame_input.clone(); + let logs_i = ctx.journal().logs().len(); if let ItemOrResult::Result(mut output) = self.frame_init(frame_init)? { let (ctx, inspector) = self.ctx_inspector(); + // for precompiles send logs to inspector. + if let FrameResult::Call(CallOutcome { + was_precompile_called, + precompile_call_logs, + .. + }) = &mut output + { + if *was_precompile_called { + let logs = ctx.journal_mut().logs()[logs_i..].to_vec(); + for log in logs.iter().chain(precompile_call_logs.iter()).cloned() { + inspector.log(ctx, log); + } + } + } frame_end(ctx, inspector, &frame_input, &mut output); return Ok(ItemOrResult::Result(output)); } diff --git a/crates/interpreter/CHANGELOG.md b/crates/interpreter/CHANGELOG.md index 6e0fe840cf..2bedd96a79 100644 --- a/crates/interpreter/CHANGELOG.md +++ b/crates/interpreter/CHANGELOG.md @@ -7,49 +7,80 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -## [25.0.2](https://github.com/bluealloy/revm/compare/revm-interpreter-v25.0.1...revm-interpreter-v25.0.2) - 2025-08-23 +## [32.0.0](https://github.com/bluealloy/revm/compare/revm-interpreter-v31.1.0...revm-interpreter-v32.0.0) - 2026-01-15 + +### Added + +- add `new_oog` helpers to InterpreterResult, CallOutcome, CreateOutcome, and FrameResult ([#3309](https://github.com/bluealloy/revm/pull/3309)) +- new gas params, tx initial gas and codedeposit ([#3260](https://github.com/bluealloy/revm/pull/3260)) +- move GasParams to Cfg ([#3229](https://github.com/bluealloy/revm/pull/3229)) +- BAL EIP-7928 ([#3070](https://github.com/bluealloy/revm/pull/3070)) +- Gas params ([#3132](https://github.com/bluealloy/revm/pull/3132)) +- *(create)* Implement Cache for CreateInputs::created_address ([#3218](https://github.com/bluealloy/revm/pull/3218)) ### Fixed -- *(interpreter)* correct CreateContractStartingWithEF halt mapping ([#2890](https://github.com/bluealloy/revm/pull/2890)) +- *(create)* Fix CreateInputs::created_address Cache invalidation ([#3222](https://github.com/bluealloy/revm/pull/3222)) -## [25.0.1](https://github.com/bluealloy/revm/compare/revm-interpreter-v25.0.0...revm-interpreter-v25.0.1) - 2025-08-12 +### Other + +- fix typos, grammar errors, and improve documentation consistency ([#3294](https://github.com/bluealloy/revm/pull/3294)) +- add optimization for push and pop ([#3263](https://github.com/bluealloy/revm/pull/3263)) +- happy new year, 2026 licence ([#3272](https://github.com/bluealloy/revm/pull/3272)) +- avoid loading bytecode in extcodehash ([#3261](https://github.com/bluealloy/revm/pull/3261)) +- *(clippy)* remove unused imports ([#3227](https://github.com/bluealloy/revm/pull/3227)) +- *(fmt)* merge all imports ([#3184](https://github.com/bluealloy/revm/pull/3184)) + +## [31.1.0](https://github.com/bluealloy/revm/compare/revm-interpreter-v31.0.0...revm-interpreter-v31.1.0) - 2025-11-14 + +### Fixed + +- correctly handle selfdestruct cold load ([#3174](https://github.com/bluealloy/revm/pull/3174)) + +## [31.0.0](https://github.com/bluealloy/revm/compare/revm-interpreter-v29.0.1...revm-interpreter-v31.0.0) - 2025-11-10 + +### Added + +- process precompile logs to inspector ([#3148](https://github.com/bluealloy/revm/pull/3148)) +- selfdestruct oog on cold load ([#3140](https://github.com/bluealloy/revm/pull/3140)) ### Other -- updated the following local packages: revm-primitives, revm-bytecode, revm-context-interface +- merge v98 versions bumps ([#3155](https://github.com/bluealloy/revm/pull/3155)) +- add eq/serde for InitialAndFloorGas ([#3147](https://github.com/bluealloy/revm/pull/3147)) +- *(interpreter)* deprecate public otry! macro ([#3146](https://github.com/bluealloy/revm/pull/3146)) -## [25.0.0](https://github.com/bluealloy/revm/compare/revm-interpreter-v24.0.0...revm-interpreter-v25.0.0) - 2025-08-06 +## [29.0.1](https://github.com/bluealloy/revm/compare/revm-interpreter-v29.0.0...revm-interpreter-v29.0.1) - 2025-11-07 + +### Other + +- updated the following local packages: revm-primitives, revm-bytecode, revm-state, revm-context-interface + +## [28.0.1](https://github.com/bluealloy/revm/compare/revm-interpreter-v28.0.0...revm-interpreter-v28.0.1) - 2025-10-30 + +### Fixed + +- hook up Cfg::memory_limit ([#3129](https://github.com/bluealloy/revm/pull/3129)) + +## [28.0.0](https://github.com/bluealloy/revm/compare/revm-interpreter-v27.0.2...revm-interpreter-v28.0.0) - 2025-10-17 ### Added -- short address for journal cold/warm check ([#2849](https://github.com/bluealloy/revm/pull/2849)) -- gastable, record static gas in Interpreter loop ([#2822](https://github.com/bluealloy/revm/pull/2822)) +- Optional Bytecode in CallInput ([#3110](https://github.com/bluealloy/revm/pull/3110)) ### Fixed -- map new once and for all (+ci) ([#2852](https://github.com/bluealloy/revm/pull/2852)) +- return MemoryLimitOOG when memory_limit is enabled ([#3109](https://github.com/bluealloy/revm/pull/3109)) ### Other -- *(deps)* bump ruint ([#2811](https://github.com/bluealloy/revm/pull/2811)) -- specialize halt, making instruction code very slightly smaller ([#2840](https://github.com/bluealloy/revm/pull/2840)) -- update README.md ([#2842](https://github.com/bluealloy/revm/pull/2842)) -- add debug assertions to set_action ([#2832](https://github.com/bluealloy/revm/pull/2832)) -- improve ExtBytecode hash handling ([#2826](https://github.com/bluealloy/revm/pull/2826)) -- fix inspector, cleanup loop ([#2797](https://github.com/bluealloy/revm/pull/2797)) -- start InstructionResult at 1 ([#2802](https://github.com/bluealloy/revm/pull/2802)) -- fix typos ([#2800](https://github.com/bluealloy/revm/pull/2800)) -- improve inspector loop ([#2776](https://github.com/bluealloy/revm/pull/2776)) -- add rust-version and note about MSRV ([#2789](https://github.com/bluealloy/revm/pull/2789)) -- collapse debug info for interpreter macros ([#2780](https://github.com/bluealloy/revm/pull/2780)) -# Changelog -All notable changes to this project will be documented in this file. +- *(instructions)* fix typo in spurious_dragon variable ([#3106](https://github.com/bluealloy/revm/pull/3106)) -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [27.0.2](https://github.com/bluealloy/revm/compare/revm-interpreter-v27.0.1...revm-interpreter-v27.0.2) - 2025-10-15 -## [Unreleased] +### Other + +- updated the following local packages: revm-bytecode, revm-state, revm-context-interface ## [27.0.1](https://github.com/bluealloy/revm/compare/revm-interpreter-v27.0.0...revm-interpreter-v27.0.1) - 2025-10-15 @@ -101,6 +132,48 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - updated the following local packages: revm-context-interface +## [25.0.2](https://github.com/bluealloy/revm/compare/revm-interpreter-v25.0.1...revm-interpreter-v25.0.2) - 2025-08-23 + +### Fixed + +- *(interpreter)* correct CreateContractStartingWithEF halt mapping ([#2890](https://github.com/bluealloy/revm/pull/2890)) + +## [25.0.1](https://github.com/bluealloy/revm/compare/revm-interpreter-v25.0.0...revm-interpreter-v25.0.1) - 2025-08-12 + +### Other + +- updated the following local packages: revm-primitives, revm-bytecode, revm-context-interface + +## [25.0.0](https://github.com/bluealloy/revm/compare/revm-interpreter-v24.0.0...revm-interpreter-v25.0.0) - 2025-08-06 + +### Added + +- short address for journal cold/warm check ([#2849](https://github.com/bluealloy/revm/pull/2849)) +- gastable, record static gas in Interpreter loop ([#2822](https://github.com/bluealloy/revm/pull/2822)) + +### Fixed + +- map new once and for all (+ci) ([#2852](https://github.com/bluealloy/revm/pull/2852)) + +### Other + +- *(deps)* bump ruint ([#2811](https://github.com/bluealloy/revm/pull/2811)) +- specialize halt, making instruction code very slightly smaller ([#2840](https://github.com/bluealloy/revm/pull/2840)) +- update README.md ([#2842](https://github.com/bluealloy/revm/pull/2842)) +- add debug assertions to set_action ([#2832](https://github.com/bluealloy/revm/pull/2832)) +- improve ExtBytecode hash handling ([#2826](https://github.com/bluealloy/revm/pull/2826)) +- fix inspector, cleanup loop ([#2797](https://github.com/bluealloy/revm/pull/2797)) +- start InstructionResult at 1 ([#2802](https://github.com/bluealloy/revm/pull/2802)) +- fix typos ([#2800](https://github.com/bluealloy/revm/pull/2800)) +- improve inspector loop ([#2776](https://github.com/bluealloy/revm/pull/2776)) +- add rust-version and note about MSRV ([#2789](https://github.com/bluealloy/revm/pull/2789)) +- collapse debug info for interpreter macros ([#2780](https://github.com/bluealloy/revm/pull/2780)) +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + ## [24.0.0](https://github.com/bluealloy/revm/compare/revm-interpreter-v23.0.2...revm-interpreter-v24.0.0) - 2025-07-23 ### Added diff --git a/crates/interpreter/Cargo.toml b/crates/interpreter/Cargo.toml index ba7e15eb87..7ddfc962e4 100644 --- a/crates/interpreter/Cargo.toml +++ b/crates/interpreter/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "revm-interpreter" description = "Revm Interpreter that executes bytecode." -version = "27.0.1" +version = "32.0.0" authors.workspace = true edition.workspace = true keywords.workspace = true diff --git a/crates/interpreter/LICENSE b/crates/interpreter/LICENSE index be6d350ebe..7edb0405ff 100644 --- a/crates/interpreter/LICENSE +++ b/crates/interpreter/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021-2025 draganrakita +Copyright (c) 2021-2026 draganrakita Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/crates/interpreter/src/gas.rs b/crates/interpreter/src/gas.rs index 37a0421835..d862ab9499 100644 --- a/crates/interpreter/src/gas.rs +++ b/crates/interpreter/src/gas.rs @@ -1,10 +1,6 @@ //! EVM gas calculation utilities. -mod calc; -mod constants; - -pub use calc::*; -pub use constants::*; +pub use context_interface::cfg::gas::*; /// Represents the state of gas during execution. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] @@ -91,11 +87,6 @@ impl Gas { self.remaining } - /// Return remaining gas after subtracting 63/64 parts. - pub const fn remaining_63_of_64_parts(&self) -> u64 { - self.remaining - self.remaining / 64 - } - /// Erases a gas cost from the totals. #[inline] pub fn erase_cost(&mut self, returned: u64) { @@ -199,18 +190,42 @@ impl MemoryGas { } } + /// Sets the number of words and the expansion cost. + /// + /// Returns the difference between the new and old expansion cost. + #[inline] + pub fn set_words_num(&mut self, words_num: usize, mut expansion_cost: u64) -> Option { + self.words_num = words_num; + core::mem::swap(&mut self.expansion_cost, &mut expansion_cost); + self.expansion_cost.checked_sub(expansion_cost) + } + /// Records a new memory length and calculates additional cost if memory is expanded. /// Returns the additional gas cost required, or None if no expansion is needed. #[inline] - pub fn record_new_len(&mut self, new_num: usize) -> Option { + pub fn record_new_len( + &mut self, + new_num: usize, + linear_cost: u64, + quadratic_cost: u64, + ) -> Option { if new_num <= self.words_num { return None; } self.words_num = new_num; - let mut cost = crate::gas::calc::memory_gas(new_num); + let mut cost = memory_gas(new_num, linear_cost, quadratic_cost); core::mem::swap(&mut self.expansion_cost, &mut cost); // Safe to subtract because we know that new_len > length // Notice the swap above. Some(self.expansion_cost - cost) } } + +/// Memory expansion cost calculation for a given number of words. +#[inline] +pub const fn memory_gas(num_words: usize, linear_cost: u64, quadratic_cost: u64) -> u64 { + let num_words = num_words as u64; + linear_cost + .saturating_mul(num_words) + .saturating_add(num_words.saturating_mul(num_words) / quadratic_cost) +} diff --git a/crates/interpreter/src/gas/calc.rs b/crates/interpreter/src/gas/calc.rs deleted file mode 100644 index ba6fe0aebc..0000000000 --- a/crates/interpreter/src/gas/calc.rs +++ /dev/null @@ -1,504 +0,0 @@ -use super::constants::*; -use crate::{num_words, tri, SStoreResult, SelfDestructResult, StateLoad}; -use context_interface::{ - journaled_state::AccountLoad, transaction::AccessListItemTr as _, Transaction, TransactionType, -}; -use primitives::{eip7702, hardfork::SpecId, U256}; - -/// `SSTORE` opcode refund calculation. -#[allow(clippy::collapsible_else_if)] -#[inline] -pub fn sstore_refund(spec_id: SpecId, vals: &SStoreResult) -> i64 { - if spec_id.is_enabled_in(SpecId::ISTANBUL) { - // EIP-3529: Reduction in refunds - let sstore_clears_schedule = if spec_id.is_enabled_in(SpecId::LONDON) { - (SSTORE_RESET - COLD_SLOAD_COST + ACCESS_LIST_STORAGE_KEY) as i64 - } else { - REFUND_SSTORE_CLEARS - }; - if vals.is_new_eq_present() { - 0 - } else { - if vals.is_original_eq_present() && vals.is_new_zero() { - sstore_clears_schedule - } else { - let mut refund = 0; - - if !vals.is_original_zero() { - if vals.is_present_zero() { - refund -= sstore_clears_schedule; - } else if vals.is_new_zero() { - refund += sstore_clears_schedule; - } - } - - if vals.is_original_eq_new() { - let (gas_sstore_reset, gas_sload) = if spec_id.is_enabled_in(SpecId::BERLIN) { - (SSTORE_RESET - COLD_SLOAD_COST, WARM_STORAGE_READ_COST) - } else { - (SSTORE_RESET, sload_cost(spec_id, false)) - }; - if vals.is_original_zero() { - refund += (SSTORE_SET - gas_sload) as i64; - } else { - refund += (gas_sstore_reset - gas_sload) as i64; - } - } - - refund - } - } - } else { - if !vals.is_present_zero() && vals.is_new_zero() { - REFUND_SSTORE_CLEARS - } else { - 0 - } - } -} - -/// `CREATE2` opcode cost calculation. -#[inline] -pub const fn create2_cost(len: usize) -> Option { - CREATE.checked_add(tri!(cost_per_word(len, KECCAK256WORD))) -} - -#[inline] -const fn log2floor(value: U256) -> u64 { - let mut l: u64 = 256; - let mut i = 3; - loop { - if value.as_limbs()[i] == 0u64 { - l -= 64; - } else { - l -= value.as_limbs()[i].leading_zeros() as u64; - if l == 0 { - return l; - } else { - return l - 1; - } - } - if i == 0 { - break; - } - i -= 1; - } - l -} - -/// `EXP` opcode cost calculation. -#[inline] -pub fn exp_cost(spec_id: SpecId, power: U256) -> Option { - if power.is_zero() { - Some(EXP) - } else { - // EIP-160: EXP cost increase - let gas_byte = U256::from(if spec_id.is_enabled_in(SpecId::SPURIOUS_DRAGON) { - 50 - } else { - 10 - }); - let gas = U256::from(EXP) - .checked_add(gas_byte.checked_mul(U256::from(log2floor(power) / 8 + 1))?)?; - - u64::try_from(gas).ok() - } -} - -/// `*COPY` opcodes cost calculation. -#[inline] -pub const fn copy_cost_verylow(len: usize) -> Option { - copy_cost(VERYLOW, len) -} - -/// `EXTCODECOPY` opcode cost calculation. -#[inline] -pub const fn extcodecopy_cost(spec_id: SpecId, len: usize, is_cold: bool) -> Option { - let base_gas = if spec_id.is_enabled_in(SpecId::BERLIN) { - warm_cold_cost(is_cold) - } else if spec_id.is_enabled_in(SpecId::TANGERINE) { - 700 - } else { - 20 - }; - copy_cost(base_gas, len) -} - -#[inline] -/// Calculates the gas cost for copy operations based on data length. -pub const fn copy_cost(base_cost: u64, len: usize) -> Option { - base_cost.checked_add(tri!(cost_per_word(len, COPY))) -} - -/// `LOG` opcode cost calculation. -#[inline] -pub const fn log_cost(n: u8, len: u64) -> Option { - tri!(LOG.checked_add(tri!(LOGDATA.checked_mul(len)))).checked_add(LOGTOPIC * n as u64) -} - -/// `KECCAK256` opcode cost calculation. -#[inline] -pub const fn keccak256_cost(len: usize) -> Option { - KECCAK256.checked_add(tri!(cost_per_word(len, KECCAK256WORD))) -} - -/// Calculate the cost of buffer per word. -#[inline] -pub const fn cost_per_word(len: usize, multiple: u64) -> Option { - multiple.checked_mul(num_words(len) as u64) -} - -/// EIP-3860: Limit and meter initcode -/// -/// Apply extra gas cost of 2 for every 32-byte chunk of initcode. -/// -/// This cannot overflow as the initcode length is assumed to be checked. -#[inline] -pub const fn initcode_cost(len: usize) -> u64 { - let Some(cost) = cost_per_word(len, INITCODE_WORD_COST) else { - panic!("initcode cost overflow") - }; - cost -} - -/// `SLOAD` opcode cost calculation. -#[inline] -pub const fn sload_cost(spec_id: SpecId, is_cold: bool) -> u64 { - if spec_id.is_enabled_in(SpecId::BERLIN) { - if is_cold { - COLD_SLOAD_COST - } else { - WARM_STORAGE_READ_COST - } - } else if spec_id.is_enabled_in(SpecId::ISTANBUL) { - // EIP-1884: Repricing for trie-size-dependent opcodes - ISTANBUL_SLOAD_GAS - } else if spec_id.is_enabled_in(SpecId::TANGERINE) { - // EIP-150: Gas cost changes for IO-heavy operations - 200 - } else { - 50 - } -} - -/// Static gas cost for sstore. -#[inline] -pub const fn sstore_cost_static(spec_id: SpecId) -> u64 { - if spec_id.is_enabled_in(SpecId::BERLIN) { - WARM_STORAGE_READ_COST - } else if spec_id.is_enabled_in(SpecId::ISTANBUL) { - ISTANBUL_SLOAD_GAS - } else { - SSTORE_RESET - } -} - -/// Dynamic gas cost for sstore. -#[inline] -pub const fn sstore_cost_dynamic(spec_id: SpecId, vals: &SStoreResult, is_cold: bool) -> u64 { - sstore_cost(spec_id, vals, is_cold) - sstore_cost_static(spec_id) -} - -/// Static gas cost for sstore. -#[inline] -pub const fn static_sstore_cost(spec_id: SpecId) -> u64 { - if spec_id.is_enabled_in(SpecId::BERLIN) { - WARM_STORAGE_READ_COST - } else if spec_id.is_enabled_in(SpecId::ISTANBUL) { - ISTANBUL_SLOAD_GAS - } else { - SSTORE_RESET - } -} - -/// Dynamic gas cost for sstore. -#[inline] -pub const fn dyn_sstore_cost(spec_id: SpecId, vals: &SStoreResult, is_cold: bool) -> u64 { - sstore_cost(spec_id, vals, is_cold) - static_sstore_cost(spec_id) -} - -/// `SSTORE` opcode cost calculation. -#[inline] -pub const fn sstore_cost(spec_id: SpecId, vals: &SStoreResult, is_cold: bool) -> u64 { - if spec_id.is_enabled_in(SpecId::BERLIN) { - // Berlin specification logic - let mut gas_cost = istanbul_sstore_cost::(vals); - - if is_cold { - gas_cost += COLD_SLOAD_COST; - } - gas_cost - } else if spec_id.is_enabled_in(SpecId::ISTANBUL) { - // Istanbul logic - istanbul_sstore_cost::(vals) - } else { - // Frontier logic - frontier_sstore_cost(vals) - } -} - -/// EIP-2200: Structured Definitions for Net Gas Metering -#[inline] -const fn istanbul_sstore_cost( - vals: &SStoreResult, -) -> u64 { - if vals.is_new_eq_present() { - SLOAD_GAS - } else if vals.is_original_eq_present() && vals.is_original_zero() { - SSTORE_SET - } else if vals.is_original_eq_present() { - SSTORE_RESET_GAS - } else { - SLOAD_GAS - } -} - -/// Frontier sstore cost just had two cases set and reset values. -#[inline] -const fn frontier_sstore_cost(vals: &SStoreResult) -> u64 { - if vals.is_present_zero() && !vals.is_new_zero() { - SSTORE_SET - } else { - SSTORE_RESET - } -} - -/// Static gas cost for selfdestruct. -#[inline] -pub const fn static_selfdestruct_cost(spec_id: SpecId) -> u64 { - // EIP-150: Gas cost changes for IO-heavy operations - if spec_id.is_enabled_in(SpecId::TANGERINE) { - 5000 - } else { - 0 - } -} - -/// `SELFDESTRUCT` opcode cost calculation. -#[inline] -pub const fn dyn_selfdestruct_cost(spec_id: SpecId, res: &StateLoad) -> u64 { - let is_tangerine = spec_id.is_enabled_in(SpecId::TANGERINE); - let mut gas = 0; - - // EIP-161: State trie clearing (invariant-preserving alternative) - let should_charge_topup = if spec_id.is_enabled_in(SpecId::SPURIOUS_DRAGON) { - res.data.had_value && !res.data.target_exists - } else { - !res.data.target_exists - }; - - // EIP-150: Gas cost changes for IO-heavy operations - if is_tangerine && should_charge_topup { - gas += NEWACCOUNT - } - - if spec_id.is_enabled_in(SpecId::BERLIN) && res.is_cold { - gas += COLD_ACCOUNT_ACCESS_COST - } - gas -} - -/// `SELFDESTRUCT` opcode cost calculation. -#[inline] -pub const fn selfdestruct_cost(spec_id: SpecId, res: StateLoad) -> u64 { - static_selfdestruct_cost(spec_id) + dyn_selfdestruct_cost(spec_id, &res) -} - -/// Calculate static gas for the call -/// -/// Gas depends on: -/// * Spec. For berlin hardfork only warm gas [`WARM_STORAGE_READ_COST`] is calculated. -/// * If there is transfer value. additional gas of [`CALLVALUE`] is added. -#[inline] -pub fn calc_call_static_gas(spec_id: SpecId, has_transfer: bool) -> u64 { - // Account access. - let mut gas = if spec_id.is_enabled_in(SpecId::BERLIN) { - WARM_STORAGE_READ_COST - } else if spec_id.is_enabled_in(SpecId::TANGERINE) { - // EIP-150: Gas cost changes for IO-heavy operations - 700 - } else { - 40 - }; - - // Transfer value cost - if has_transfer { - gas += CALLVALUE; - } - - gas -} - -/// Berlin warm and cold storage access cost for account access. -#[inline] -pub const fn warm_cold_cost(is_cold: bool) -> u64 { - if is_cold { - COLD_ACCOUNT_ACCESS_COST - } else { - WARM_STORAGE_READ_COST - } -} - -/// Berlin warm and cold storage access cost for account access. -/// -/// If delegation is Some, add additional cost for delegation account load. -#[inline] -pub const fn warm_cold_cost_with_delegation(load: StateLoad) -> u64 { - let mut gas = warm_cold_cost(load.is_cold); - if let Some(is_cold) = load.data.is_delegate_account_cold { - gas += warm_cold_cost(is_cold); - } - gas -} - -/// Memory expansion cost calculation for a given number of words. -#[inline] -pub const fn memory_gas(num_words: usize) -> u64 { - let num_words = num_words as u64; - MEMORY - .saturating_mul(num_words) - .saturating_add(num_words.saturating_mul(num_words) / 512) -} - -/// Init and floor gas from transaction -#[derive(Clone, Copy, Debug, Default)] -pub struct InitialAndFloorGas { - /// Initial gas for transaction. - pub initial_gas: u64, - /// If transaction is a Call and Prague is enabled - /// floor_gas is at least amount of gas that is going to be spent. - pub floor_gas: u64, -} - -impl InitialAndFloorGas { - /// Create a new InitialAndFloorGas instance. - #[inline] - pub const fn new(initial_gas: u64, floor_gas: u64) -> Self { - Self { - initial_gas, - floor_gas, - } - } -} - -/// Initial gas that is deducted for transaction to be included. -/// Initial gas contains initial stipend gas, gas for access list and input data. -/// -/// # Returns -/// -/// - Intrinsic gas -/// - Number of tokens in calldata -#[allow(clippy::too_many_arguments)] -pub fn calculate_initial_tx_gas( - spec_id: SpecId, - input: &[u8], - is_create: bool, - is_eip7702_enabled: bool, - is_eip7623_enabled: bool, - access_list_accounts: u64, - access_list_storages: u64, - authorization_list_num: u64, -) -> InitialAndFloorGas { - let mut gas = InitialAndFloorGas::default(); - - // Initdate stipend - let tokens_in_calldata = get_tokens_in_calldata(input, spec_id.is_enabled_in(SpecId::ISTANBUL)); - - gas.initial_gas += tokens_in_calldata * STANDARD_TOKEN_COST; - - // Get number of access list account and storages. - gas.initial_gas += access_list_accounts * ACCESS_LIST_ADDRESS; - gas.initial_gas += access_list_storages * ACCESS_LIST_STORAGE_KEY; - - // Base stipend - gas.initial_gas += if is_create { - if spec_id.is_enabled_in(SpecId::HOMESTEAD) { - // EIP-2: Homestead Hard-fork Changes - 53000 - } else { - 21000 - } - } else { - 21000 - }; - - // EIP-3860: Limit and meter initcode - // Init code stipend for bytecode analysis - if spec_id.is_enabled_in(SpecId::SHANGHAI) && is_create { - gas.initial_gas += initcode_cost(input.len()) - } - - // EIP-7702 - if spec_id.is_enabled_in(SpecId::PRAGUE) || is_eip7702_enabled { - gas.initial_gas += authorization_list_num * eip7702::PER_EMPTY_ACCOUNT_COST; - } - - // EIP-7623 - if spec_id.is_enabled_in(SpecId::PRAGUE) || is_eip7623_enabled { - gas.floor_gas = calc_tx_floor_cost(tokens_in_calldata); - } - - gas -} - -/// Initial gas that is deducted for transaction to be included. -/// Initial gas contains initial stipend gas, gas for access list and input data. -/// -/// # Returns -/// -/// - Intrinsic gas -/// - Number of tokens in calldata -pub fn calculate_initial_tx_gas_for_tx( - tx: impl Transaction, - spec: SpecId, - is_eip7702_enabled: bool, - is_eip7623_enabled: bool, -) -> InitialAndFloorGas { - let mut accounts = 0; - let mut storages = 0; - // legacy is only tx type that does not have access list. - if tx.tx_type() != TransactionType::Legacy { - (accounts, storages) = tx - .access_list() - .map(|al| { - al.fold((0, 0), |(mut num_accounts, mut num_storage_slots), item| { - num_accounts += 1; - num_storage_slots += item.storage_slots().count(); - - (num_accounts, num_storage_slots) - }) - }) - .unwrap_or_default(); - } - - calculate_initial_tx_gas( - spec, - tx.input(), - tx.kind().is_create(), - is_eip7702_enabled, - is_eip7623_enabled, - accounts as u64, - storages as u64, - tx.authorization_list_len() as u64, - ) -} - -/// Retrieve the total number of tokens in calldata. -#[inline] -pub fn get_tokens_in_calldata(input: &[u8], is_istanbul: bool) -> u64 { - let zero_data_len = input.iter().filter(|v| **v == 0).count() as u64; - let non_zero_data_len = input.len() as u64 - zero_data_len; - let non_zero_data_multiplier = if is_istanbul { - // EIP-2028: Transaction data gas cost reduction - NON_ZERO_BYTE_MULTIPLIER_ISTANBUL - } else { - NON_ZERO_BYTE_MULTIPLIER - }; - zero_data_len + non_zero_data_len * non_zero_data_multiplier -} - -/// Calculate the transaction cost floor as specified in EIP-7623. -#[inline] -pub fn calc_tx_floor_cost(tokens_in_calldata: u64) -> u64 { - tokens_in_calldata * TOTAL_COST_FLOOR_PER_TOKEN + 21_000 -} diff --git a/crates/interpreter/src/instructions.rs b/crates/interpreter/src/instructions.rs index 185b11b6e1..969805b583 100644 --- a/crates/interpreter/src/instructions.rs +++ b/crates/interpreter/src/instructions.rs @@ -27,7 +27,10 @@ pub mod tx_info; /// Utility functions and helpers for instruction implementation. pub mod utility; +pub use context_interface::cfg::gas::{self, *}; + use crate::{interpreter_types::InterpreterTypes, Host, InstructionContext}; +use primitives::hardfork::SpecId; /// EVM opcode function signature. #[derive(Debug)] @@ -81,6 +84,51 @@ pub const fn instruction_table() -> [Instructio const { instruction_table_impl::() } } +/// Create a instruction table with applied spec changes to static gas cost. +#[inline] +pub fn instruction_table_gas_changes_spec( + spec: SpecId, +) -> [Instruction; 256] { + use bytecode::opcode::*; + use SpecId::*; + let mut table = instruction_table(); + + if spec.is_enabled_in(TANGERINE) { + // EIP-150: Gas cost changes for IO-heavy operations + table[SLOAD as usize].static_gas = 200; + table[BALANCE as usize].static_gas = 400; + table[EXTCODESIZE as usize].static_gas = 700; + table[EXTCODECOPY as usize].static_gas = 700; + table[CALL as usize].static_gas = 700; + table[CALLCODE as usize].static_gas = 700; + table[DELEGATECALL as usize].static_gas = 700; + table[STATICCALL as usize].static_gas = 700; + table[SELFDESTRUCT as usize].static_gas = 5000; + } + + if spec.is_enabled_in(ISTANBUL) { + // EIP-1884: Repricing for trie-size-dependent opcodes + table[SLOAD as usize].static_gas = gas::ISTANBUL_SLOAD_GAS; + table[BALANCE as usize].static_gas = 700; + table[EXTCODEHASH as usize].static_gas = 700; + } + + if spec.is_enabled_in(BERLIN) { + // warm account cost is base gas that is spend. Additional gas depends if account is cold loaded. + table[SLOAD as usize].static_gas = gas::WARM_STORAGE_READ_COST; + table[BALANCE as usize].static_gas = gas::WARM_STORAGE_READ_COST; + table[EXTCODESIZE as usize].static_gas = gas::WARM_STORAGE_READ_COST; + table[EXTCODEHASH as usize].static_gas = gas::WARM_STORAGE_READ_COST; + table[EXTCODECOPY as usize].static_gas = gas::WARM_STORAGE_READ_COST; + table[CALL as usize].static_gas = gas::WARM_STORAGE_READ_COST; + table[CALLCODE as usize].static_gas = gas::WARM_STORAGE_READ_COST; + table[DELEGATECALL as usize].static_gas = gas::WARM_STORAGE_READ_COST; + table[STATICCALL as usize].static_gas = gas::WARM_STORAGE_READ_COST; + } + + table +} + const fn instruction_table_impl() -> [Instruction; 256] { use bytecode::opcode::*; let mut table = [Instruction::unknown(); 256]; @@ -95,7 +143,7 @@ const fn instruction_table_impl() -> [Instructi table[SMOD as usize] = Instruction::new(arithmetic::smod, 5); table[ADDMOD as usize] = Instruction::new(arithmetic::addmod, 8); table[MULMOD as usize] = Instruction::new(arithmetic::mulmod, 8); - table[EXP as usize] = Instruction::new(arithmetic::exp, 0); // dynamic + table[EXP as usize] = Instruction::new(arithmetic::exp, gas::EXP); // base table[SIGNEXTEND as usize] = Instruction::new(arithmetic::signextend, 5); table[LT as usize] = Instruction::new(bitwise::lt, 3); @@ -114,25 +162,25 @@ const fn instruction_table_impl() -> [Instructi table[SAR as usize] = Instruction::new(bitwise::sar, 3); table[CLZ as usize] = Instruction::new(bitwise::clz, 5); - table[KECCAK256 as usize] = Instruction::new(system::keccak256, 0); // dynamic + table[KECCAK256 as usize] = Instruction::new(system::keccak256, gas::KECCAK256); table[ADDRESS as usize] = Instruction::new(system::address, 2); - table[BALANCE as usize] = Instruction::new(host::balance, 0); // dynamic + table[BALANCE as usize] = Instruction::new(host::balance, 20); table[ORIGIN as usize] = Instruction::new(tx_info::origin, 2); table[CALLER as usize] = Instruction::new(system::caller, 2); table[CALLVALUE as usize] = Instruction::new(system::callvalue, 2); table[CALLDATALOAD as usize] = Instruction::new(system::calldataload, 3); table[CALLDATASIZE as usize] = Instruction::new(system::calldatasize, 2); - table[CALLDATACOPY as usize] = Instruction::new(system::calldatacopy, 0); // static 2, mostly dynamic + table[CALLDATACOPY as usize] = Instruction::new(system::calldatacopy, 3); table[CODESIZE as usize] = Instruction::new(system::codesize, 2); - table[CODECOPY as usize] = Instruction::new(system::codecopy, 0); // static 2, mostly dynamic + table[CODECOPY as usize] = Instruction::new(system::codecopy, 3); table[GASPRICE as usize] = Instruction::new(tx_info::gasprice, 2); - table[EXTCODESIZE as usize] = Instruction::new(host::extcodesize, 0); // dynamic - table[EXTCODECOPY as usize] = Instruction::new(host::extcodecopy, 0); // dynamic + table[EXTCODESIZE as usize] = Instruction::new(host::extcodesize, 20); + table[EXTCODECOPY as usize] = Instruction::new(host::extcodecopy, 20); table[RETURNDATASIZE as usize] = Instruction::new(system::returndatasize, 2); - table[RETURNDATACOPY as usize] = Instruction::new(system::returndatacopy, 0); // static 2, mostly dynamic - table[EXTCODEHASH as usize] = Instruction::new(host::extcodehash, 0); // dynamic + table[RETURNDATACOPY as usize] = Instruction::new(system::returndatacopy, 3); + table[EXTCODEHASH as usize] = Instruction::new(host::extcodehash, 400); table[BLOCKHASH as usize] = Instruction::new(host::blockhash, 20); table[COINBASE as usize] = Instruction::new(block_info::coinbase, 2); table[TIMESTAMP as usize] = Instruction::new(block_info::timestamp, 2); @@ -149,8 +197,10 @@ const fn instruction_table_impl() -> [Instructi table[MLOAD as usize] = Instruction::new(memory::mload, 3); table[MSTORE as usize] = Instruction::new(memory::mstore, 3); table[MSTORE8 as usize] = Instruction::new(memory::mstore8, 3); - table[SLOAD as usize] = Instruction::new(host::sload, 0); // dynamic - table[SSTORE as usize] = Instruction::new(host::sstore, 0); // dynamic + table[SLOAD as usize] = Instruction::new(host::sload, 50); + // SSTORE static gas can be found in GasParams as check for minimal stipend + // needs to be done before deduction of static gas. + table[SSTORE as usize] = Instruction::new(host::sstore, 0); table[JUMP as usize] = Instruction::new(control::jump, 8); table[JUMPI as usize] = Instruction::new(control::jumpi, 10); table[PC as usize] = Instruction::new(control::pc, 2); @@ -159,7 +209,7 @@ const fn instruction_table_impl() -> [Instructi table[JUMPDEST as usize] = Instruction::new(control::jumpdest, 1); table[TLOAD as usize] = Instruction::new(host::tload, 100); table[TSTORE as usize] = Instruction::new(host::tstore, 100); - table[MCOPY as usize] = Instruction::new(memory::mcopy, 0); // static 2, mostly dynamic + table[MCOPY as usize] = Instruction::new(memory::mcopy, 3); // static 2, mostly dynamic table[PUSH0 as usize] = Instruction::new(stack::push0, 2); table[PUSH1 as usize] = Instruction::new(stack::push::<1, _, _>, 3); @@ -229,23 +279,23 @@ const fn instruction_table_impl() -> [Instructi table[SWAP15 as usize] = Instruction::new(stack::swap::<15, _, _>, 3); table[SWAP16 as usize] = Instruction::new(stack::swap::<16, _, _>, 3); - table[LOG0 as usize] = Instruction::new(host::log::<0, _>, 0); // dynamic - table[LOG1 as usize] = Instruction::new(host::log::<1, _>, 0); // dynamic - table[LOG2 as usize] = Instruction::new(host::log::<2, _>, 0); // dynamic - table[LOG3 as usize] = Instruction::new(host::log::<3, _>, 0); // dynamic - table[LOG4 as usize] = Instruction::new(host::log::<4, _>, 0); // dynamic + table[LOG0 as usize] = Instruction::new(host::log::<0, _>, gas::LOG); + table[LOG1 as usize] = Instruction::new(host::log::<1, _>, gas::LOG); + table[LOG2 as usize] = Instruction::new(host::log::<2, _>, gas::LOG); + table[LOG3 as usize] = Instruction::new(host::log::<3, _>, gas::LOG); + table[LOG4 as usize] = Instruction::new(host::log::<4, _>, gas::LOG); - table[CREATE as usize] = Instruction::new(contract::create::<_, false, _>, 0); // dynamic - table[CALL as usize] = Instruction::new(contract::call, 0); // dynamic - table[CALLCODE as usize] = Instruction::new(contract::call_code, 0); // dynamic + table[CREATE as usize] = Instruction::new(contract::create::<_, false, _>, 0); + table[CALL as usize] = Instruction::new(contract::call, 40); + table[CALLCODE as usize] = Instruction::new(contract::call_code, 40); table[RETURN as usize] = Instruction::new(control::ret, 0); - table[DELEGATECALL as usize] = Instruction::new(contract::delegate_call, 0); // dynamic - table[CREATE2 as usize] = Instruction::new(contract::create::<_, true, _>, 0); // dynamic + table[DELEGATECALL as usize] = Instruction::new(contract::delegate_call, 40); + table[CREATE2 as usize] = Instruction::new(contract::create::<_, true, _>, 0); - table[STATICCALL as usize] = Instruction::new(contract::static_call, 0); // dynamic + table[STATICCALL as usize] = Instruction::new(contract::static_call, 40); table[REVERT as usize] = Instruction::new(control::revert, 0); table[INVALID as usize] = Instruction::new(control::invalid, 0); - table[SELFDESTRUCT as usize] = Instruction::new(host::selfdestruct, 0); // dynamic + table[SELFDESTRUCT as usize] = Instruction::new(host::selfdestruct, 0); table } diff --git a/crates/interpreter/src/instructions/arithmetic.rs b/crates/interpreter/src/instructions/arithmetic.rs index 68333c9385..9692adce74 100644 --- a/crates/interpreter/src/instructions/arithmetic.rs +++ b/crates/interpreter/src/instructions/arithmetic.rs @@ -1,35 +1,31 @@ use super::i256::{i256_div, i256_mod}; use crate::{ - gas, - interpreter_types::{InterpreterTypes, RuntimeFlag, StackTr}, + interpreter_types::{InterpreterTypes, StackTr}, InstructionContext, }; +use context_interface::Host; use primitives::U256; /// Implements the ADD instruction - adds two values from stack. pub fn add(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::VERYLOW); popn_top!([op1], op2, context.interpreter); *op2 = op1.wrapping_add(*op2); } /// Implements the MUL instruction - multiplies two values from stack. pub fn mul(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::LOW); popn_top!([op1], op2, context.interpreter); *op2 = op1.wrapping_mul(*op2); } /// Implements the SUB instruction - subtracts two values from stack. pub fn sub(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::VERYLOW); popn_top!([op1], op2, context.interpreter); *op2 = op1.wrapping_sub(*op2); } /// Implements the DIV instruction - divides two values from stack. pub fn div(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::LOW); popn_top!([op1], op2, context.interpreter); if !op2.is_zero() { *op2 = op1.wrapping_div(*op2); @@ -40,7 +36,6 @@ pub fn div(context: InstructionContext<'_, H, /// /// Performs signed division of two values from stack. pub fn sdiv(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::LOW); popn_top!([op1], op2, context.interpreter); *op2 = i256_div(op1, *op2); } @@ -49,7 +44,6 @@ pub fn sdiv(context: InstructionContext<'_, H /// /// Pops two values from stack and pushes the remainder of their division. pub fn rem(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::LOW); popn_top!([op1], op2, context.interpreter); if !op2.is_zero() { *op2 = op1.wrapping_rem(*op2); @@ -60,7 +54,6 @@ pub fn rem(context: InstructionContext<'_, H, /// /// Performs signed modulo of two values from stack. pub fn smod(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::LOW); popn_top!([op1], op2, context.interpreter); *op2 = i256_mod(op1, *op2) } @@ -69,7 +62,6 @@ pub fn smod(context: InstructionContext<'_, H /// /// Pops three values from stack and pushes (a + b) % n. pub fn addmod(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::MID); popn_top!([op1, op2], op3, context.interpreter); *op3 = op1.add_mod(op2, *op3) } @@ -78,16 +70,17 @@ pub fn addmod(context: InstructionContext<'_, /// /// Pops three values from stack and pushes (a * b) % n. pub fn mulmod(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::MID); popn_top!([op1, op2], op3, context.interpreter); *op3 = op1.mul_mod(op2, *op3) } /// Implements the EXP instruction - exponentiates two values from stack. -pub fn exp(context: InstructionContext<'_, H, WIRE>) { - let spec_id = context.interpreter.runtime_flag.spec_id(); +pub fn exp(context: InstructionContext<'_, H, WIRE>) { popn_top!([op1], op2, context.interpreter); - gas_or_fail!(context.interpreter, gas::exp_cost(spec_id, *op2)); + gas!( + context.interpreter, + context.host.gas_params().exp_cost(*op2) + ); *op2 = op1.pow(*op2); } @@ -121,7 +114,6 @@ pub fn exp(context: InstructionContext<'_, H, /// Similarly, if `b == 0` then the yellow paper says the output should start with all zeros, /// then end with bits from `b`; this is equal to `y & mask` where `&` is bitwise `AND`. pub fn signextend(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::LOW); popn_top!([ext], x, context.interpreter); // For 31 we also don't need to do anything. if ext < U256::from(31) { diff --git a/crates/interpreter/src/instructions/bitwise.rs b/crates/interpreter/src/instructions/bitwise.rs index d07f7638e1..57972a2de2 100644 --- a/crates/interpreter/src/instructions/bitwise.rs +++ b/crates/interpreter/src/instructions/bitwise.rs @@ -8,25 +8,20 @@ use primitives::U256; /// Implements the LT instruction - less than comparison. pub fn lt(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::VERYLOW); popn_top!([op1], op2, context.interpreter); *op2 = U256::from(op1 < *op2); } /// Implements the GT instruction - greater than comparison. pub fn gt(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::VERYLOW); popn_top!([op1], op2, context.interpreter); - *op2 = U256::from(op1 > *op2); } /// Implements the CLZ instruction - count leading zeros. pub fn clz(context: InstructionContext<'_, H, WIRE>) { check!(context.interpreter, OSAKA); - //gas!(context.interpreter, gas::LOW); popn_top!([], op1, context.interpreter); - let leading_zeros = op1.leading_zeros(); *op1 = U256::from(leading_zeros); } @@ -35,9 +30,7 @@ pub fn clz(context: InstructionContext<'_, H, /// /// Signed less than comparison of two values from stack. pub fn slt(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::VERYLOW); popn_top!([op1], op2, context.interpreter); - *op2 = U256::from(i256_cmp(&op1, op2) == Ordering::Less); } @@ -45,9 +38,7 @@ pub fn slt(context: InstructionContext<'_, H, /// /// Signed greater than comparison of two values from stack. pub fn sgt(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::VERYLOW); popn_top!([op1], op2, context.interpreter); - *op2 = U256::from(i256_cmp(&op1, op2) == Ordering::Greater); } @@ -55,9 +46,7 @@ pub fn sgt(context: InstructionContext<'_, H, /// /// Equality comparison of two values from stack. pub fn eq(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::VERYLOW); popn_top!([op1], op2, context.interpreter); - *op2 = U256::from(op1 == *op2); } @@ -65,7 +54,6 @@ pub fn eq(context: InstructionContext<'_, H, /// /// Checks if the top stack value is zero. pub fn iszero(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::VERYLOW); popn_top!([], op1, context.interpreter); *op1 = U256::from(op1.is_zero()); } @@ -74,7 +62,6 @@ pub fn iszero(context: InstructionContext<'_, /// /// Bitwise AND of two values from stack. pub fn bitand(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::VERYLOW); popn_top!([op1], op2, context.interpreter); *op2 = op1 & *op2; } @@ -83,9 +70,7 @@ pub fn bitand(context: InstructionContext<'_, /// /// Bitwise OR of two values from stack. pub fn bitor(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::VERYLOW); popn_top!([op1], op2, context.interpreter); - *op2 = op1 | *op2; } @@ -93,9 +78,7 @@ pub fn bitor(context: InstructionContext<'_, /// /// Bitwise XOR of two values from stack. pub fn bitxor(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::VERYLOW); popn_top!([op1], op2, context.interpreter); - *op2 = op1 ^ *op2; } @@ -103,9 +86,7 @@ pub fn bitxor(context: InstructionContext<'_, /// /// Bitwise NOT (negation) of the top stack value. pub fn not(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::VERYLOW); popn_top!([], op1, context.interpreter); - *op1 = !*op1; } @@ -113,9 +94,7 @@ pub fn not(context: InstructionContext<'_, H, /// /// Extracts a single byte from a word at a given index. pub fn byte(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::VERYLOW); popn_top!([op1], op2, context.interpreter); - let o1 = as_usize_saturated!(op1); *op2 = if o1 < 32 { // `31 - o1` because `byte` returns LE, while we want BE @@ -128,9 +107,7 @@ pub fn byte(context: InstructionContext<'_, H /// EIP-145: Bitwise shifting instructions in EVM pub fn shl(context: InstructionContext<'_, H, WIRE>) { check!(context.interpreter, CONSTANTINOPLE); - //gas!(context.interpreter, gas::VERYLOW); popn_top!([op1], op2, context.interpreter); - let shift = as_usize_saturated!(op1); *op2 = if shift < 256 { *op2 << shift @@ -142,9 +119,7 @@ pub fn shl(context: InstructionContext<'_, H, /// EIP-145: Bitwise shifting instructions in EVM pub fn shr(context: InstructionContext<'_, H, WIRE>) { check!(context.interpreter, CONSTANTINOPLE); - //gas!(context.interpreter, gas::VERYLOW); popn_top!([op1], op2, context.interpreter); - let shift = as_usize_saturated!(op1); *op2 = if shift < 256 { *op2 >> shift @@ -156,9 +131,7 @@ pub fn shr(context: InstructionContext<'_, H, /// EIP-145: Bitwise shifting instructions in EVM pub fn sar(context: InstructionContext<'_, H, WIRE>) { check!(context.interpreter, CONSTANTINOPLE); - //gas!(context.interpreter, gas::VERYLOW); popn_top!([op1], op2, context.interpreter); - let shift = as_usize_saturated!(op1); *op2 = if shift < 256 { op2.arithmetic_shr(shift) @@ -252,7 +225,7 @@ mod tests { push!(interpreter, test.value); push!(interpreter, test.shift); let context = InstructionContext { - host: &mut DummyHost, + host: &mut DummyHost::default(), interpreter: &mut interpreter, }; shl(context); @@ -335,7 +308,7 @@ mod tests { push!(interpreter, test.value); push!(interpreter, test.shift); let context = InstructionContext { - host: &mut DummyHost, + host: &mut DummyHost::default(), interpreter: &mut interpreter, }; shr(context); @@ -443,7 +416,7 @@ mod tests { push!(interpreter, test.value); push!(interpreter, test.shift); let context = InstructionContext { - host: &mut DummyHost, + host: &mut DummyHost::default(), interpreter: &mut interpreter, }; sar(context); @@ -481,7 +454,7 @@ mod tests { push!(interpreter, test.input); push!(interpreter, U256::from(test.index)); let context = InstructionContext { - host: &mut DummyHost, + host: &mut DummyHost::default(), interpreter: &mut interpreter, }; byte(context); @@ -493,7 +466,8 @@ mod tests { #[test] fn test_clz() { let mut interpreter = Interpreter::default(); - interpreter.set_spec_id(SpecId::OSAKA); + interpreter.runtime_flag.spec_id = SpecId::OSAKA; + let mut host = DummyHost::new(SpecId::OSAKA); struct TestCase { value: U256, @@ -534,7 +508,7 @@ mod tests { for test in test_cases { push!(interpreter, test.value); let context = InstructionContext { - host: &mut DummyHost, + host: &mut host, interpreter: &mut interpreter, }; clz(context); diff --git a/crates/interpreter/src/instructions/block_info.rs b/crates/interpreter/src/instructions/block_info.rs index fa2c118c7a..8956d55378 100644 --- a/crates/interpreter/src/instructions/block_info.rs +++ b/crates/interpreter/src/instructions/block_info.rs @@ -9,7 +9,6 @@ use crate::InstructionContext; /// EIP-1344: ChainID opcode pub fn chainid(context: InstructionContext<'_, H, WIRE>) { check!(context.interpreter, ISTANBUL); - //gas!(context.interpreter, gas::BASE); push!(context.interpreter, context.host.chain_id()); } @@ -19,7 +18,6 @@ pub fn chainid(context: InstructionCon pub fn coinbase( context: InstructionContext<'_, H, WIRE>, ) { - //gas!(context.interpreter, gas::BASE); push!( context.interpreter, context.host.beneficiary().into_word().into() @@ -32,7 +30,6 @@ pub fn coinbase( pub fn timestamp( context: InstructionContext<'_, H, WIRE>, ) { - //gas!(context.interpreter, gas::BASE); push!(context.interpreter, context.host.timestamp()); } @@ -42,7 +39,6 @@ pub fn timestamp( pub fn block_number( context: InstructionContext<'_, H, WIRE>, ) { - //gas!(context.interpreter, gas::BASE); push!(context.interpreter, context.host.block_number()); } @@ -52,7 +48,6 @@ pub fn block_number( pub fn difficulty( context: InstructionContext<'_, H, WIRE>, ) { - //gas!(context.interpreter, gas::BASE); if context .interpreter .runtime_flag @@ -72,14 +67,12 @@ pub fn difficulty( pub fn gaslimit( context: InstructionContext<'_, H, WIRE>, ) { - //gas!(context.interpreter, gas::BASE); push!(context.interpreter, context.host.gas_limit()); } /// EIP-3198: BASEFEE opcode pub fn basefee(context: InstructionContext<'_, H, WIRE>) { check!(context.interpreter, LONDON); - //gas!(context.interpreter, gas::BASE); push!(context.interpreter, context.host.basefee()); } @@ -88,6 +81,5 @@ pub fn blob_basefee( context: InstructionContext<'_, H, WIRE>, ) { check!(context.interpreter, CANCUN); - //gas!(context.interpreter, gas::BASE); push!(context.interpreter, context.host.blob_gasprice()); } diff --git a/crates/interpreter/src/instructions/contract.rs b/crates/interpreter/src/instructions/contract.rs index e3137ff04b..b7a7cc9927 100644 --- a/crates/interpreter/src/instructions/contract.rs +++ b/crates/interpreter/src/instructions/contract.rs @@ -2,11 +2,10 @@ mod call_helpers; pub use call_helpers::{ get_memory_input_and_out_ranges, load_acc_and_calc_gas, load_account_delegated, - load_account_delegated_handle_error, new_account_cost, resize_memory, + load_account_delegated_handle_error, resize_memory, }; use crate::{ - gas, instructions::utility::IntoAddress, interpreter_action::FrameInput, interpreter_types::{InputsTr, InterpreterTypes, LoopControl, MemoryTr, RuntimeFlag, StackTr}, @@ -51,11 +50,20 @@ pub fn create( .halt(InstructionResult::CreateInitCodeSizeLimit); return; } - gas!(context.interpreter, gas::initcode_cost(len)); + gas!( + context.interpreter, + context.host.gas_params().initcode_cost(len) + ); } let code_offset = as_usize_or_fail!(context.interpreter, code_offset); - resize_memory!(context.interpreter, code_offset, len); + resize_memory!( + context.interpreter, + context.host.gas_params(), + code_offset, + len + ); + code = Bytes::copy_from_slice( context .interpreter @@ -69,10 +77,13 @@ pub fn create( let scheme = if IS_CREATE2 { popn!([salt], context.interpreter); // SAFETY: `len` is reasonable in size as gas for it is already deducted. - gas_or_fail!(context.interpreter, gas::create2_cost(len)); + gas!( + context.interpreter, + context.host.gas_params().create2_cost(len) + ); CreateScheme::Create2 { salt } } else { - gas!(context.interpreter, gas::CREATE); + gas!(context.interpreter, context.host.gas_params().create_cost()); CreateScheme::Create }; @@ -86,7 +97,7 @@ pub fn create( .is_enabled_in(SpecId::TANGERINE) { // Take remaining gas and deduce l64 part of it. - gas_limit -= gas_limit / 64 + gas_limit = context.host.gas_params().call_stipend_reduction(gas_limit); } gas!(context.interpreter, gas_limit); @@ -95,13 +106,13 @@ pub fn create( .interpreter .bytecode .set_action(InterpreterAction::NewFrame(FrameInput::Create(Box::new( - CreateInputs { - caller: context.interpreter.input.target_address(), + CreateInputs::new( + context.interpreter.input.target_address(), scheme, value, - init_code: code, + code, gas_limit, - }, + ), )))); } @@ -123,10 +134,13 @@ pub fn call( .halt(InstructionResult::CallNotAllowedInsideStatic); return; } - let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(context.interpreter) + + let Some((input, return_memory_offset)) = + get_memory_input_and_out_ranges(context.interpreter, context.host.gas_params()) else { return; }; + let Some((gas_limit, bytecode, bytecode_hash)) = load_acc_and_calc_gas(&mut context, to, has_transfer, true, local_gas_limit) else { @@ -144,8 +158,7 @@ pub fn call( target_address: to, caller: context.interpreter.input.target_address(), bytecode_address: to, - bytecode, - bytecode_hash, + known_bytecode: Some((bytecode_hash, bytecode)), value: CallValue::Transfer(value), scheme: CallScheme::Call, is_static: context.interpreter.runtime_flag.is_static(), @@ -166,7 +179,8 @@ pub fn call_code( let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX); let has_transfer = !value.is_zero(); - let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(context.interpreter) + let Some((input, return_memory_offset)) = + get_memory_input_and_out_ranges(context.interpreter, context.host.gas_params()) else { return; }; @@ -188,8 +202,7 @@ pub fn call_code( target_address: context.interpreter.input.target_address(), caller: context.interpreter.input.target_address(), bytecode_address: to, - bytecode, - bytecode_hash, + known_bytecode: Some((bytecode_hash, bytecode)), value: CallValue::Transfer(value), scheme: CallScheme::CallCode, is_static: context.interpreter.runtime_flag.is_static(), @@ -210,7 +223,8 @@ pub fn delegate_call( // Max gas limit is not possible in real ethereum situation. let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX); - let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(context.interpreter) + let Some((input, return_memory_offset)) = + get_memory_input_and_out_ranges(context.interpreter, context.host.gas_params()) else { return; }; @@ -232,8 +246,7 @@ pub fn delegate_call( target_address: context.interpreter.input.target_address(), caller: context.interpreter.input.caller_address(), bytecode_address: to, - bytecode, - bytecode_hash, + known_bytecode: Some((bytecode_hash, bytecode)), value: CallValue::Apparent(context.interpreter.input.call_value()), scheme: CallScheme::DelegateCall, is_static: context.interpreter.runtime_flag.is_static(), @@ -254,7 +267,8 @@ pub fn static_call( // Max gas limit is not possible in real ethereum situation. let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX); - let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(context.interpreter) + let Some((input, return_memory_offset)) = + get_memory_input_and_out_ranges(context.interpreter, context.host.gas_params()) else { return; }; @@ -276,8 +290,7 @@ pub fn static_call( target_address: to, caller: context.interpreter.input.target_address(), bytecode_address: to, - bytecode, - bytecode_hash, + known_bytecode: Some((bytecode_hash, bytecode)), value: CallValue::Transfer(U256::ZERO), scheme: CallScheme::StaticCall, is_static: true, diff --git a/crates/interpreter/src/instructions/contract/call_helpers.rs b/crates/interpreter/src/instructions/contract/call_helpers.rs index 4d2ee93a9d..47d49305fc 100644 --- a/crates/interpreter/src/instructions/contract/call_helpers.rs +++ b/crates/interpreter/src/instructions/contract/call_helpers.rs @@ -1,13 +1,9 @@ use crate::{ - gas::{ - self, calc_call_static_gas, COLD_ACCOUNT_ACCESS_COST_ADDITIONAL, NEWACCOUNT, - WARM_STORAGE_READ_COST, - }, interpreter::Interpreter, interpreter_types::{InterpreterTypes, MemoryTr, RuntimeFlag, StackTr}, InstructionContext, }; -use context_interface::{host::LoadError, Host}; +use context_interface::{cfg::GasParams, host::LoadError, Host}; use core::{cmp::min, ops::Range}; use primitives::{ hardfork::SpecId::{self, *}, @@ -19,17 +15,18 @@ use state::Bytecode; #[inline] pub fn get_memory_input_and_out_ranges( interpreter: &mut Interpreter, + gas_params: &GasParams, ) -> Option<(Range, Range)> { popn!([in_offset, in_len, out_offset, out_len], interpreter, None); - let mut in_range = resize_memory(interpreter, in_offset, in_len)?; + let mut in_range = resize_memory(interpreter, gas_params, in_offset, in_len)?; if !in_range.is_empty() { let offset = interpreter.memory.local_memory_offset(); in_range = in_range.start.saturating_add(offset)..in_range.end.saturating_add(offset); } - let ret_range = resize_memory(interpreter, out_offset, out_len)?; + let ret_range = resize_memory(interpreter, gas_params, out_offset, out_len)?; Some((in_range, ret_range)) } @@ -38,13 +35,14 @@ pub fn get_memory_input_and_out_ranges( #[inline] pub fn resize_memory( interpreter: &mut Interpreter, + gas_params: &GasParams, offset: U256, len: U256, ) -> Option> { let len = as_usize_or_fail_ret!(interpreter, len, None); let offset = if len != 0 { let offset = as_usize_or_fail_ret!(interpreter, offset, None); - resize_memory!(interpreter, offset, len, None); + resize_memory!(interpreter, gas_params, offset, len, None); offset } else { usize::MAX //unrealistic value so we are sure it is not used @@ -61,10 +59,14 @@ pub fn load_acc_and_calc_gas( create_empty_account: bool, stack_gas_limit: u64, ) -> Option<(u64, Bytecode, B256)> { - let spec = context.interpreter.runtime_flag.spec_id(); - // calculate static gas first. For berlin hardfork it will take warm gas. - let static_gas = calc_call_static_gas(spec, transfers_value); - gas!(context.interpreter, static_gas, None); + // Transfer value cost + if transfers_value { + gas!( + context.interpreter, + context.host.gas_params().transfer_value_cost(), + None + ); + } // load account delegated and deduct dynamic gas. let (gas, bytecode, code_hash) = @@ -74,18 +76,24 @@ pub fn load_acc_and_calc_gas( // deduct dynamic gas. gas!(interpreter, gas, None); + let interpreter = &mut context.interpreter; + let host = &mut context.host; + // EIP-150: Gas cost changes for IO-heavy operations let mut gas_limit = if interpreter.runtime_flag.spec_id().is_enabled_in(TANGERINE) { - // Take l64 part of gas_limit - min(interpreter.gas.remaining_63_of_64_parts(), stack_gas_limit) + // On mainnet this will take return 63/64 of gas_limit. + let reduced_gas_limit = host + .gas_params() + .call_stipend_reduction(interpreter.gas.remaining()); + min(reduced_gas_limit, stack_gas_limit) } else { stack_gas_limit }; - gas!(interpreter, gas_limit, None); + // Add call stipend if there is value to be transferred. if transfers_value { - gas_limit = gas_limit.saturating_add(gas::CALL_STIPEND); + gas_limit = gas_limit.saturating_add(host.gas_params().call_stipend()); } Some((gas_limit, bytecode, code_hash)) @@ -134,37 +142,42 @@ pub fn load_account_delegated( ) -> Result<(u64, Bytecode, B256), LoadError> { let mut cost = 0; let is_berlin = spec.is_enabled_in(SpecId::BERLIN); - let is_spurioud_dragon = spec.is_enabled_in(SpecId::SPURIOUS_DRAGON); + let is_spurious_dragon = spec.is_enabled_in(SpecId::SPURIOUS_DRAGON); - let skip_cold_load = is_berlin && remaining_gas < COLD_ACCOUNT_ACCESS_COST_ADDITIONAL; + let additional_cold_cost = host.gas_params().cold_account_additional_cost(); + let warm_storage_read_cost = host.gas_params().warm_storage_read_cost(); + + let skip_cold_load = is_berlin && remaining_gas < additional_cold_cost; let account = host.load_account_info_skip_cold_load(address, true, skip_cold_load)?; if is_berlin && account.is_cold { - cost += COLD_ACCOUNT_ACCESS_COST_ADDITIONAL; + cost += additional_cold_cost; } let mut bytecode = account.code.clone().unwrap_or_default(); let mut code_hash = account.code_hash(); // New account cost, as account is empty there is no delegated account and we can return early. if create_empty_account && account.is_empty { - cost += new_account_cost(is_spurioud_dragon, transfers_value); + cost += host + .gas_params() + .new_account_cost(is_spurious_dragon, transfers_value); return Ok((cost, bytecode, code_hash)); } // load delegate code if account is EIP-7702 if let Some(Bytecode::Eip7702(code)) = &account.code { // EIP-7702 is enabled after berlin hardfork. - cost += WARM_STORAGE_READ_COST; + cost += warm_storage_read_cost; if cost > remaining_gas { return Err(LoadError::ColdLoadSkipped); } let address = code.address(); // skip cold load if there is enough gas to cover the cost. - let skip_cold_load = remaining_gas < cost + COLD_ACCOUNT_ACCESS_COST_ADDITIONAL; + let skip_cold_load = remaining_gas < cost + additional_cold_cost; let delegate_account = host.load_account_info_skip_cold_load(address, true, skip_cold_load)?; if delegate_account.is_cold { - cost += COLD_ACCOUNT_ACCESS_COST_ADDITIONAL; + cost += additional_cold_cost; } bytecode = delegate_account.code.clone().unwrap_or_default(); code_hash = delegate_account.code_hash(); @@ -172,15 +185,3 @@ pub fn load_account_delegated( Ok((cost, bytecode, code_hash)) } - -/// Returns new account cost. -#[inline] -pub fn new_account_cost(is_spurioud_dragon: bool, transfers_value: bool) -> u64 { - // EIP-161: State trie clearing (invariant-preserving alternative) - // Pre-Spurious Dragon: always charge for new account - // Post-Spurious Dragon: only charge if value is transferred - if !is_spurioud_dragon || transfers_value { - return NEWACCOUNT; - } - 0 -} diff --git a/crates/interpreter/src/instructions/control.rs b/crates/interpreter/src/instructions/control.rs index 66224360c4..bb85393b6b 100644 --- a/crates/interpreter/src/instructions/control.rs +++ b/crates/interpreter/src/instructions/control.rs @@ -3,6 +3,7 @@ use crate::{ interpreter_types::{InterpreterTypes, Jumps, LoopControl, MemoryTr, RuntimeFlag, StackTr}, InstructionResult, InterpreterAction, }; +use context_interface::{cfg::GasParams, Host}; use primitives::{Bytes, U256}; use crate::InstructionContext; @@ -11,7 +12,6 @@ use crate::InstructionContext; /// /// Unconditional jump to a valid destination. pub fn jump(context: InstructionContext<'_, H, ITy>) { - //gas!(context.interpreter, gas::MID); popn!([target], context.interpreter); jump_inner(context.interpreter, target); } @@ -20,9 +20,7 @@ pub fn jump(context: InstructionContext<'_, H, /// /// Conditional jump to a valid destination if condition is true. pub fn jumpi(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::HIGH); popn!([target, cond], context.interpreter); - if !cond.is_zero() { jump_inner(context.interpreter, target); } @@ -45,15 +43,12 @@ fn jump_inner(interpreter: &mut Interpreter, targe /// Implements the JUMPDEST instruction. /// /// Marks a valid destination for jump operations. -pub fn jumpdest(_context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::JUMPDEST); -} +pub fn jumpdest(_context: InstructionContext<'_, H, WIRE>) {} /// Implements the PC instruction. /// /// Pushes the current program counter onto the stack. pub fn pc(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::BASE); // - 1 because we have already advanced the instruction pointer in `Interpreter::step` push!( context.interpreter, @@ -67,17 +62,18 @@ pub fn pc(context: InstructionContext<'_, H, /// Handles memory data retrieval and sets the return action. fn return_inner( interpreter: &mut Interpreter, + gas_params: &GasParams, instruction_result: InstructionResult, ) { - // Zero gas cost - // //gas!(interpreter, gas::ZERO) popn!([offset, len], interpreter); let len = as_usize_or_fail!(interpreter, len); // Important: Offset must be ignored if len is zeros let mut output = Bytes::default(); if len != 0 { let offset = as_usize_or_fail!(interpreter, offset); - resize_memory!(interpreter, offset, len); + if !interpreter.resize_memory(gas_params, offset, len) { + return; + } output = interpreter.memory.slice_len(offset, len).to_vec().into() } @@ -93,14 +89,22 @@ fn return_inner( /// Implements the RETURN instruction. /// /// Halts execution and returns data from memory. -pub fn ret(context: InstructionContext<'_, H, WIRE>) { - return_inner(context.interpreter, InstructionResult::Return); +pub fn ret(context: InstructionContext<'_, H, WIRE>) { + return_inner( + context.interpreter, + context.host.gas_params(), + InstructionResult::Return, + ); } /// EIP-140: REVERT instruction -pub fn revert(context: InstructionContext<'_, H, WIRE>) { +pub fn revert(context: InstructionContext<'_, H, WIRE>) { check!(context.interpreter, BYZANTIUM); - return_inner(context.interpreter, InstructionResult::Revert); + return_inner( + context.interpreter, + context.host.gas_params(), + InstructionResult::Revert, + ); } /// Stop opcode. This opcode halts the execution. diff --git a/crates/interpreter/src/instructions/host.rs b/crates/interpreter/src/instructions/host.rs index b68f284e2e..99f5c007bd 100644 --- a/crates/interpreter/src/instructions/host.rs +++ b/crates/interpreter/src/instructions/host.rs @@ -1,15 +1,14 @@ use crate::{ - gas::{ - self, CALL_STIPEND, COLD_ACCOUNT_ACCESS_COST_ADDITIONAL, COLD_SLOAD_COST_ADDITIONAL, - ISTANBUL_SLOAD_GAS, WARM_STORAGE_READ_COST, - }, instructions::utility::{IntoAddress, IntoU256}, interpreter_types::{InputsTr, InterpreterTypes, MemoryTr, RuntimeFlag, StackTr}, Host, InstructionResult, }; use context_interface::host::LoadError; use core::cmp::min; -use primitives::{hardfork::SpecId::*, Bytes, Log, LogData, B256, BLOCK_HASH_HISTORY, U256}; +use primitives::{ + hardfork::SpecId::{self, *}, + Bytes, Log, LogData, B256, BLOCK_HASH_HISTORY, U256, +}; use crate::InstructionContext; @@ -24,15 +23,6 @@ pub fn balance(context: InstructionCon let account = berlin_load_account!(context, address, false); *top = account.balance; } else { - let gas = if spec_id.is_enabled_in(ISTANBUL) { - // EIP-1884: Repricing for trie-size-dependent opcodes - 700 - } else if spec_id.is_enabled_in(TANGERINE) { - 400 - } else { - 20 - }; - gas!(context.interpreter, gas); let Ok(account) = context .host .load_account_info_skip_cold_load(address, false, false) @@ -48,7 +38,6 @@ pub fn selfbalance( context: InstructionContext<'_, H, WIRE>, ) { check!(context.interpreter, ISTANBUL); - //gas!(context.interpreter, gas::LOW); let Some(balance) = context .host @@ -67,18 +56,13 @@ pub fn extcodesize( ) { popn_top!([], top, context.interpreter); let address = top.into_address(); + let spec_id = context.interpreter.runtime_flag.spec_id(); if spec_id.is_enabled_in(BERLIN) { let account = berlin_load_account!(context, address, true); // safe to unwrap because we are loading code *top = U256::from(account.code.as_ref().unwrap().len()); } else { - let gas = if spec_id.is_enabled_in(TANGERINE) { - 700 - } else { - 20 - }; - gas!(context.interpreter, gas); let Ok(account) = context .host .load_account_info_skip_cold_load(address, true, false) @@ -100,17 +84,11 @@ pub fn extcodehash( let spec_id = context.interpreter.runtime_flag.spec_id(); let account = if spec_id.is_enabled_in(BERLIN) { - berlin_load_account!(context, address, true) + berlin_load_account!(context, address, false) } else { - let gas = if spec_id.is_enabled_in(ISTANBUL) { - 700 - } else { - 400 - }; - gas!(context.interpreter, gas); let Ok(account) = context .host - .load_account_info_skip_cold_load(address, true, false) + .load_account_info_skip_cold_load(address, false, false) else { return context.interpreter.halt_fatal(); }; @@ -142,7 +120,7 @@ pub fn extcodecopy( let len = as_usize_or_fail!(context.interpreter, len_u256); gas!( context.interpreter, - gas::copy_cost(0, len).unwrap_or(u64::MAX) + context.host.gas_params().extcodecopy(len) ); let mut memory_offset_usize = 0; @@ -150,20 +128,19 @@ pub fn extcodecopy( if len != 0 { // fail on casting of memory_offset only if len is not zero. memory_offset_usize = as_usize_or_fail!(context.interpreter, memory_offset); - resize_memory!(context.interpreter, memory_offset_usize, len); + // Resize memory to fit the code + resize_memory!( + context.interpreter, + context.host.gas_params(), + memory_offset_usize, + len + ); } let code = if spec_id.is_enabled_in(BERLIN) { let account = berlin_load_account!(context, address, true); account.code.as_ref().unwrap().original_bytes() } else { - let gas = if spec_id.is_enabled_in(TANGERINE) { - 700 - } else { - 20 - }; - gas!(context.interpreter, gas); - let Some(code) = context.host.load_account_code(address) else { return context.interpreter.halt_fatal(); }; @@ -186,7 +163,6 @@ pub fn extcodecopy( pub fn blockhash( context: InstructionContext<'_, H, WIRE>, ) { - //gas!(context.interpreter, gas::BLOCKHASH); popn_top!([], number, context.interpreter); let requested_number = *number; @@ -223,26 +199,14 @@ pub fn sload(context: InstructionConte let spec_id = context.interpreter.runtime_flag.spec_id(); let target = context.interpreter.input.target_address(); - // `SLOAD` opcode cost calculation. - let gas = if spec_id.is_enabled_in(BERLIN) { - WARM_STORAGE_READ_COST - } else if spec_id.is_enabled_in(ISTANBUL) { - // EIP-1884: Repricing for trie-size-dependent opcodes - ISTANBUL_SLOAD_GAS - } else if spec_id.is_enabled_in(TANGERINE) { - // EIP-150: Gas cost changes for IO-heavy operations - 200 - } else { - 50 - }; - gas!(context.interpreter, gas); if spec_id.is_enabled_in(BERLIN) { - let skip_cold = context.interpreter.gas.remaining() < COLD_SLOAD_COST_ADDITIONAL; + let additional_cold_cost = context.host.gas_params().cold_storage_additional_cost(); + let skip_cold = context.interpreter.gas.remaining() < additional_cold_cost; let res = context.host.sload_skip_cold_load(target, *index, skip_cold); match res { Ok(storage) => { if storage.is_cold { - gas!(context.interpreter, COLD_SLOAD_COST_ADDITIONAL); + gas!(context.interpreter, additional_cold_cost); } *index = storage.data; @@ -268,13 +232,10 @@ pub fn sstore(context: InstructionCont let target = context.interpreter.input.target_address(); let spec_id = context.interpreter.runtime_flag.spec_id(); - // EIP-1706 Disable SSTORE with gasleft lower than call stipend - if context - .interpreter - .runtime_flag - .spec_id() - .is_enabled_in(ISTANBUL) - && context.interpreter.gas.remaining() <= CALL_STIPEND + // EIP-2200: Structured Definitions for Net Gas Metering + // If gasleft is less than or equal to gas stipend, fail the current call frame with ‘out of gas’ exception. + if spec_id.is_enabled_in(ISTANBUL) + && context.interpreter.gas.remaining() <= context.host.gas_params().call_stipend() { context .interpreter @@ -282,14 +243,14 @@ pub fn sstore(context: InstructionCont return; } - // static gas gas!( context.interpreter, - gas::static_sstore_cost(context.interpreter.runtime_flag.spec_id()) + context.host.gas_params().sstore_static_gas() ); let state_load = if spec_id.is_enabled_in(BERLIN) { - let skip_cold = context.interpreter.gas.remaining() < COLD_SLOAD_COST_ADDITIONAL; + let additional_cold_cost = context.host.gas_params().cold_storage_additional_cost(); + let skip_cold = context.interpreter.gas.remaining() < additional_cold_cost; let res = context .host .sstore_skip_cold_load(target, index, value, skip_cold); @@ -305,21 +266,25 @@ pub fn sstore(context: InstructionCont load }; + let is_istanbul = spec_id.is_enabled_in(ISTANBUL); + // dynamic gas gas!( context.interpreter, - gas::dyn_sstore_cost( - context.interpreter.runtime_flag.spec_id(), + context.host.gas_params().sstore_dynamic_gas( + is_istanbul, &state_load.data, state_load.is_cold ) ); // refund - context.interpreter.gas.record_refund(gas::sstore_refund( - context.interpreter.runtime_flag.spec_id(), - &state_load.data, - )); + context.interpreter.gas.record_refund( + context + .host + .gas_params() + .sstore_refund(is_istanbul, &state_load.data), + ); } /// EIP-1153: Transient storage opcodes @@ -327,8 +292,6 @@ pub fn sstore(context: InstructionCont pub fn tstore(context: InstructionContext<'_, H, WIRE>) { check!(context.interpreter, CANCUN); require_non_staticcall!(context.interpreter); - //gas!(context.interpreter, gas::WARM_STORAGE_READ_COST); - popn!([index, value], context.interpreter); context @@ -340,8 +303,6 @@ pub fn tstore(context: InstructionCont /// Load value from transient storage pub fn tload(context: InstructionContext<'_, H, WIRE>) { check!(context.interpreter, CANCUN); - //gas!(context.interpreter, gas::WARM_STORAGE_READ_COST); - popn_top!([], index, context.interpreter); *index = context @@ -359,12 +320,16 @@ pub fn log( popn!([offset, len], context.interpreter); let len = as_usize_or_fail!(context.interpreter, len); - gas_or_fail!(context.interpreter, gas::log_cost(N as u8, len as u64)); + gas!( + context.interpreter, + context.host.gas_params().log_cost(N as u8, len as u64) + ); let data = if len == 0 { Bytes::new() } else { let offset = as_usize_or_fail!(context.interpreter, offset); - resize_memory!(context.interpreter, offset, len); + // Resize memory to fit the data + resize_memory!(context.interpreter, context.host.gas_params(), offset, len); Bytes::copy_from_slice(context.interpreter.memory.slice_len(offset, len).as_ref()) }; let Some(topics) = context.interpreter.stack.popn::() else { @@ -392,33 +357,39 @@ pub fn selfdestruct( let target = target.into_address(); let spec = context.interpreter.runtime_flag.spec_id(); - // static gas - gas!(context.interpreter, gas::static_selfdestruct_cost(spec)); + let cold_load_gas = context.host.gas_params().selfdestruct_cold_cost(); + + let skip_cold_load = context.interpreter.gas.remaining() < cold_load_gas; + let res = match context.host.selfdestruct( + context.interpreter.input.target_address(), + target, + skip_cold_load, + ) { + Ok(res) => res, + Err(LoadError::ColdLoadSkipped) => return context.interpreter.halt_oog(), + Err(LoadError::DBError) => return context.interpreter.halt_fatal(), + }; - let Some(res) = context - .host - .selfdestruct(context.interpreter.input.target_address(), target) - else { - context - .interpreter - .halt(InstructionResult::FatalExternalError); - return; + // EIP-161: State trie clearing (invariant-preserving alternative) + let should_charge_topup = if spec.is_enabled_in(SpecId::SPURIOUS_DRAGON) { + res.had_value && !res.target_exists + } else { + !res.target_exists }; - gas!(context.interpreter, gas::dyn_selfdestruct_cost(spec, &res)); + gas!( + context.interpreter, + context + .host + .gas_params() + .selfdestruct_cost(should_charge_topup, res.is_cold) + ); - // EIP-3529: Reduction in refunds - if !context - .interpreter - .runtime_flag - .spec_id() - .is_enabled_in(LONDON) - && !res.previously_destroyed - { + if !res.previously_destroyed { context .interpreter .gas - .record_refund(gas::SELFDESTRUCT_REFUND); + .record_refund(context.host.gas_params().selfdestruct_refund()); } context.interpreter.halt(InstructionResult::SelfDestruct); diff --git a/crates/interpreter/src/instructions/macros.rs b/crates/interpreter/src/instructions/macros.rs index a37e3b7d34..a45648a52f 100644 --- a/crates/interpreter/src/instructions/macros.rs +++ b/crates/interpreter/src/instructions/macros.rs @@ -1,17 +1,5 @@ //! Utility macros to help implementing opcode instruction functions. -/// `const` Option `?`. -#[macro_export] -#[collapse_debuginfo(yes)] -macro_rules! tri { - ($e:expr) => { - match $e { - Some(v) => v, - None => return None, - } - }; -} - /// Fails the instruction if the current call is static. #[macro_export] #[collapse_debuginfo(yes)] @@ -24,19 +12,6 @@ macro_rules! require_non_staticcall { }; } -/// Macro for optional try - returns early if the expression evaluates to None. -/// Similar to the `?` operator but for use in instruction implementations. -#[macro_export] -#[collapse_debuginfo(yes)] -macro_rules! otry { - ($expression: expr) => {{ - let Some(value) = $expression else { - return; - }; - value - }}; -} - /// Check if the `SPEC` is enabled, and fail the instruction if it is not. #[macro_export] #[collapse_debuginfo(yes)] @@ -76,20 +51,15 @@ macro_rules! berlin_load_account { $crate::berlin_load_account!($context, $address, $load_code, ()) }; ($context:expr, $address:expr, $load_code:expr, $ret:expr) => {{ - $crate::gas!($context.interpreter, WARM_STORAGE_READ_COST, $ret); - let skip_cold_load = - $context.interpreter.gas.remaining() < COLD_ACCOUNT_ACCESS_COST_ADDITIONAL; + let cold_load_gas = $context.host.gas_params().cold_account_additional_cost(); + let skip_cold_load = $context.interpreter.gas.remaining() < cold_load_gas; match $context .host .load_account_info_skip_cold_load($address, $load_code, skip_cold_load) { Ok(account) => { if account.is_cold { - $crate::gas!( - $context.interpreter, - COLD_ACCOUNT_ACCESS_COST_ADDITIONAL, - $ret - ); + $crate::gas!($context.interpreter, cold_load_gas, $ret); } account } @@ -105,40 +75,23 @@ macro_rules! berlin_load_account { }}; } -/// Same as [`gas!`], but with `gas` as an option. -#[macro_export] -#[collapse_debuginfo(yes)] -macro_rules! gas_or_fail { - ($interpreter:expr, $gas:expr) => { - $crate::gas_or_fail!($interpreter, $gas, ()) - }; - ($interpreter:expr, $gas:expr, $ret:expr) => { - match $gas { - Some(gas_used) => $crate::gas!($interpreter, gas_used, $ret), - None => { - $interpreter.halt_oog(); - return $ret; - } - } - }; -} - /// Resizes the interpreter memory if necessary. Fails the instruction if the memory or gas limit /// is exceeded. #[macro_export] #[collapse_debuginfo(yes)] macro_rules! resize_memory { - ($interpreter:expr, $offset:expr, $len:expr) => { - $crate::resize_memory!($interpreter, $offset, $len, ()) + ($interpreter:expr, $gas_params:expr, $offset:expr, $len:expr) => { + $crate::resize_memory!($interpreter, $gas_params, $offset, $len, ()) }; - ($interpreter:expr, $offset:expr, $len:expr, $ret:expr) => { - if !$crate::interpreter::resize_memory( + ($interpreter:expr, $gas_params:expr, $offset:expr, $len:expr, $ret:expr) => { + if let Err(result) = $crate::interpreter::resize_memory( &mut $interpreter.gas, &mut $interpreter.memory, + $gas_params, $offset, $len, ) { - $interpreter.halt_memory_oog(); + $interpreter.halt(result); return $ret; } }; diff --git a/crates/interpreter/src/instructions/memory.rs b/crates/interpreter/src/instructions/memory.rs index 9a48d88f3d..f6856fc634 100644 --- a/crates/interpreter/src/instructions/memory.rs +++ b/crates/interpreter/src/instructions/memory.rs @@ -1,7 +1,5 @@ -use crate::{ - gas, - interpreter_types::{InterpreterTypes, MemoryTr, RuntimeFlag, StackTr}, -}; +use crate::interpreter_types::{InterpreterTypes, MemoryTr, RuntimeFlag, StackTr}; +use context_interface::Host; use core::cmp::max; use primitives::U256; @@ -10,11 +8,10 @@ use crate::InstructionContext; /// Implements the MLOAD instruction. /// /// Loads a 32-byte word from memory. -pub fn mload(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::VERYLOW); +pub fn mload(context: InstructionContext<'_, H, WIRE>) { popn_top!([], top, context.interpreter); let offset = as_usize_or_fail!(context.interpreter, top); - resize_memory!(context.interpreter, offset, 32); + resize_memory!(context.interpreter, context.host.gas_params(), offset, 32); *top = U256::try_from_be_slice(context.interpreter.memory.slice_len(offset, 32).as_ref()).unwrap() } @@ -22,11 +19,10 @@ pub fn mload(context: InstructionContext<'_, /// Implements the MSTORE instruction. /// /// Stores a 32-byte word to memory. -pub fn mstore(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::VERYLOW); +pub fn mstore(context: InstructionContext<'_, H, WIRE>) { popn!([offset, value], context.interpreter); let offset = as_usize_or_fail!(context.interpreter, offset); - resize_memory!(context.interpreter, offset, 32); + resize_memory!(context.interpreter, context.host.gas_params(), offset, 32); context .interpreter .memory @@ -36,11 +32,10 @@ pub fn mstore(context: InstructionContext<'_, /// Implements the MSTORE8 instruction. /// /// Stores a single byte to memory. -pub fn mstore8(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::VERYLOW); +pub fn mstore8(context: InstructionContext<'_, H, WIRE>) { popn!([offset, value], context.interpreter); let offset = as_usize_or_fail!(context.interpreter, offset); - resize_memory!(context.interpreter, offset, 1); + resize_memory!(context.interpreter, context.host.gas_params(), offset, 1); context.interpreter.memory.set(offset, &[value.byte(0)]); } @@ -48,7 +43,6 @@ pub fn mstore8(context: InstructionContext<'_ /// /// Gets the size of active memory in bytes. pub fn msize(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::BASE); push!( context.interpreter, U256::from(context.interpreter.memory.size()) @@ -58,14 +52,18 @@ pub fn msize(context: InstructionContext<'_, /// Implements the MCOPY instruction. /// /// EIP-5656: Memory copying instruction that copies memory from one location to another. -pub fn mcopy(context: InstructionContext<'_, H, WIRE>) { +pub fn mcopy(context: InstructionContext<'_, H, WIRE>) { check!(context.interpreter, CANCUN); popn!([dst, src, len], context.interpreter); // Into usize or fail let len = as_usize_or_fail!(context.interpreter, len); // Deduce gas - gas_or_fail!(context.interpreter, gas::copy_cost_verylow(len)); + gas!( + context.interpreter, + context.host.gas_params().mcopy_cost(len) + ); + if len == 0 { return; } @@ -73,7 +71,12 @@ pub fn mcopy(context: InstructionContext<'_, let dst = as_usize_or_fail!(context.interpreter, dst); let src = as_usize_or_fail!(context.interpreter, src); // Resize memory - resize_memory!(context.interpreter, max(dst, src), len); + resize_memory!( + context.interpreter, + context.host.gas_params(), + max(dst, src), + len + ); // Copy memory in place context.interpreter.memory.copy(dst, src, len); } diff --git a/crates/interpreter/src/instructions/stack.rs b/crates/interpreter/src/instructions/stack.rs index 822e6240d7..f2fb7f4cba 100644 --- a/crates/interpreter/src/instructions/stack.rs +++ b/crates/interpreter/src/instructions/stack.rs @@ -10,7 +10,6 @@ use crate::InstructionContext; /// /// Removes the top item from the stack. pub fn pop(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::BASE); // Can ignore return. as relative N jump is safe operation. popn!([_i], context.interpreter); } @@ -20,7 +19,6 @@ pub fn pop(context: InstructionContext<'_, H, /// Introduce a new instruction which pushes the constant value 0 onto the stack. pub fn push0(context: InstructionContext<'_, H, WIRE>) { check!(context.interpreter, SHANGHAI); - //gas!(context.interpreter, gas::BASE); push!(context.interpreter, U256::ZERO); } @@ -30,8 +28,6 @@ pub fn push0(context: InstructionContext<'_, pub fn push( context: InstructionContext<'_, H, WIRE>, ) { - //gas!(context.interpreter, gas::VERYLOW); - let slice = context.interpreter.bytecode.read_slice(N); if !context.interpreter.stack.push_slice(slice) { context.interpreter.halt(InstructionResult::StackOverflow); @@ -48,7 +44,6 @@ pub fn push( pub fn dup( context: InstructionContext<'_, H, WIRE>, ) { - //gas!(context.interpreter, gas::VERYLOW); if !context.interpreter.stack.dup(N) { context.interpreter.halt(InstructionResult::StackOverflow); } @@ -60,7 +55,6 @@ pub fn dup( pub fn swap( context: InstructionContext<'_, H, WIRE>, ) { - //gas!(context.interpreter, gas::VERYLOW); assert!(N != 0); if !context.interpreter.stack.exchange(0, N) { context.interpreter.halt(InstructionResult::StackOverflow); diff --git a/crates/interpreter/src/instructions/system.rs b/crates/interpreter/src/instructions/system.rs index 0bea684e7f..1ad3bf86cc 100644 --- a/crates/interpreter/src/instructions/system.rs +++ b/crates/interpreter/src/instructions/system.rs @@ -1,11 +1,11 @@ use crate::{ - gas, interpreter::Interpreter, interpreter_types::{ InputsTr, InterpreterTypes, LegacyBytecode, MemoryTr, ReturnData, RuntimeFlag, StackTr, }, CallInput, InstructionResult, }; +use context_interface::{cfg::GasParams, Host}; use core::ptr; use primitives::{B256, KECCAK_EMPTY, U256}; @@ -14,15 +14,20 @@ use crate::InstructionContext; /// Implements the KECCAK256 instruction. /// /// Computes Keccak-256 hash of memory data. -pub fn keccak256(context: InstructionContext<'_, H, WIRE>) { +pub fn keccak256( + context: InstructionContext<'_, H, WIRE>, +) { popn_top!([offset], top, context.interpreter); let len = as_usize_or_fail!(context.interpreter, top); - gas_or_fail!(context.interpreter, gas::keccak256_cost(len)); + gas!( + context.interpreter, + context.host.gas_params().keccak256_cost(len) + ); let hash = if len == 0 { KECCAK_EMPTY } else { let from = as_usize_or_fail!(context.interpreter, offset); - resize_memory!(context.interpreter, from, len); + resize_memory!(context.interpreter, context.host.gas_params(), from, len); primitives::keccak256(context.interpreter.memory.slice_len(from, len).as_ref()) }; *top = hash.into(); @@ -32,7 +37,6 @@ pub fn keccak256(context: InstructionContext< /// /// Pushes the current contract's address onto the stack. pub fn address(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::BASE); push!( context.interpreter, context @@ -48,7 +52,6 @@ pub fn address(context: InstructionContext<'_ /// /// Pushes the caller's address onto the stack. pub fn caller(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::BASE); push!( context.interpreter, context @@ -64,7 +67,6 @@ pub fn caller(context: InstructionContext<'_, /// /// Pushes the size of running contract's bytecode onto the stack. pub fn codesize(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::BASE); push!( context.interpreter, U256::from(context.interpreter.bytecode.bytecode_len()) @@ -74,10 +76,17 @@ pub fn codesize(context: InstructionContext<' /// Implements the CODECOPY instruction. /// /// Copies running contract's bytecode to memory. -pub fn codecopy(context: InstructionContext<'_, H, WIRE>) { +pub fn codecopy( + context: InstructionContext<'_, H, WIRE>, +) { popn!([memory_offset, code_offset, len], context.interpreter); let len = as_usize_or_fail!(context.interpreter, len); - let Some(memory_offset) = memory_resize(context.interpreter, memory_offset, len) else { + let Some(memory_offset) = copy_cost_and_memory_resize( + context.interpreter, + context.host.gas_params(), + memory_offset, + len, + ) else { return; }; let code_offset = as_usize_saturated!(code_offset); @@ -95,7 +104,6 @@ pub fn codecopy(context: InstructionContext<' /// /// Loads 32 bytes of input data from the specified offset. pub fn calldataload(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::VERYLOW); popn_top!([], offset_ptr, context.interpreter); let mut word = B256::ZERO; let offset = as_usize_saturated!(offset_ptr); @@ -133,7 +141,6 @@ pub fn calldataload(context: InstructionConte /// /// Pushes the size of input data onto the stack. pub fn calldatasize(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::BASE); push!( context.interpreter, U256::from(context.interpreter.input.input().len()) @@ -144,17 +151,23 @@ pub fn calldatasize(context: InstructionConte /// /// Pushes the value sent with the current call onto the stack. pub fn callvalue(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::BASE); push!(context.interpreter, context.interpreter.input.call_value()); } /// Implements the CALLDATACOPY instruction. /// /// Copies input data to memory. -pub fn calldatacopy(context: InstructionContext<'_, H, WIRE>) { +pub fn calldatacopy( + context: InstructionContext<'_, H, WIRE>, +) { popn!([memory_offset, data_offset, len], context.interpreter); let len = as_usize_or_fail!(context.interpreter, len); - let Some(memory_offset) = memory_resize(context.interpreter, memory_offset, len) else { + let Some(memory_offset) = copy_cost_and_memory_resize( + context.interpreter, + context.host.gas_params(), + memory_offset, + len, + ) else { return; }; @@ -180,7 +193,6 @@ pub fn calldatacopy(context: InstructionConte /// EIP-211: New opcodes: RETURNDATASIZE and RETURNDATACOPY pub fn returndatasize(context: InstructionContext<'_, H, WIRE>) { check!(context.interpreter, BYZANTIUM); - //gas!(context.interpreter, gas::BASE); push!( context.interpreter, U256::from(context.interpreter.return_data.buffer().len()) @@ -188,7 +200,9 @@ pub fn returndatasize(context: InstructionCon } /// EIP-211: New opcodes: RETURNDATASIZE and RETURNDATACOPY -pub fn returndatacopy(context: InstructionContext<'_, H, WIRE>) { +pub fn returndatacopy( + context: InstructionContext<'_, H, WIRE>, +) { check!(context.interpreter, BYZANTIUM); popn!([memory_offset, offset, len], context.interpreter); @@ -202,7 +216,12 @@ pub fn returndatacopy(context: InstructionCon return; } - let Some(memory_offset) = memory_resize(context.interpreter, memory_offset, len) else { + let Some(memory_offset) = copy_cost_and_memory_resize( + context.interpreter, + context.host.gas_params(), + memory_offset, + len, + ) else { return; }; @@ -219,7 +238,6 @@ pub fn returndatacopy(context: InstructionCon /// /// Pushes the amount of remaining gas onto the stack. pub fn gas(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::BASE); push!( context.interpreter, U256::from(context.interpreter.gas.remaining()) @@ -229,18 +247,19 @@ pub fn gas(context: InstructionContext<'_, H, /// Common logic for copying data from a source buffer to the EVM's memory. /// /// Handles memory expansion and gas calculation for data copy operations. -pub fn memory_resize( +pub fn copy_cost_and_memory_resize( interpreter: &mut Interpreter, + gas_params: &GasParams, memory_offset: U256, len: usize, ) -> Option { // Safe to cast usize to u64 - gas_or_fail!(interpreter, gas::copy_cost_verylow(len), None); + gas!(interpreter, gas_params.copy_cost(len), None); if len == 0 { return None; } let memory_offset = as_usize_or_fail_ret!(interpreter, memory_offset, None); - resize_memory!(interpreter, memory_offset, len, None); + resize_memory!(interpreter, gas_params, memory_offset, len, None); Some(memory_offset) } diff --git a/crates/interpreter/src/instructions/tx_info.rs b/crates/interpreter/src/instructions/tx_info.rs index d710b1d24f..90395ee54f 100644 --- a/crates/interpreter/src/instructions/tx_info.rs +++ b/crates/interpreter/src/instructions/tx_info.rs @@ -11,7 +11,6 @@ use crate::InstructionContext; pub fn gasprice( context: InstructionContext<'_, H, WIRE>, ) { - //gas!(context.interpreter, gas::BASE); push!(context.interpreter, context.host.effective_gas_price()); } @@ -19,7 +18,6 @@ pub fn gasprice( /// /// Gets the execution origination address. pub fn origin(context: InstructionContext<'_, H, WIRE>) { - //gas!(context.interpreter, gas::BASE); push!( context.interpreter, context.host.caller().into_word().into() @@ -33,7 +31,6 @@ pub fn blob_hash( context: InstructionContext<'_, H, WIRE>, ) { check!(context.interpreter, CANCUN); - //gas!(context.interpreter, gas::VERYLOW); popn_top!([], index, context.interpreter); let i = as_usize_saturated!(index); *index = context.host.blob_hash(i).unwrap_or_default(); diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index efa72aa9f7..4f648ad036 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -9,6 +9,7 @@ mod runtime_flags; mod shared_memory; mod stack; +use context_interface::cfg::GasParams; // re-exports pub use ext_bytecode::ExtBytecode; pub use input::InputsImpl; @@ -114,6 +115,7 @@ impl Interpreter> { /// Clears and reinitializes the interpreter with new parameters. #[allow(clippy::too_many_arguments)] + #[inline(always)] pub fn clear( &mut self, memory: SharedMemory, @@ -152,11 +154,6 @@ impl Interpreter> { self.bytecode = ExtBytecode::new(bytecode); self } - - /// Sets the specid for the interpreter. - pub fn set_spec_id(&mut self, spec_id: SpecId) { - self.runtime_flag.spec_id = spec_id; - } } impl Default for Interpreter { @@ -186,8 +183,13 @@ impl Interpreter { /// Performs EVM memory resize. #[inline] #[must_use] - pub fn resize_memory(&mut self, offset: usize, len: usize) -> bool { - resize_memory(&mut self.gas, &mut self.memory, offset, len) + pub fn resize_memory(&mut self, gas_params: &GasParams, offset: usize, len: usize) -> bool { + if let Err(result) = resize_memory(&mut self.gas, &mut self.memory, gas_params, offset, len) + { + self.halt(result); + return false; + } + true } /// Takes the next action from the control and returns it. @@ -233,6 +235,13 @@ impl Interpreter { #[cold] #[inline(never)] pub fn halt_memory_oog(&mut self) { + self.halt(InstructionResult::MemoryOOG); + } + + /// Halt the interpreter with an out-of-gas error. + #[cold] + #[inline(never)] + pub fn halt_memory_limit_oog(&mut self) { self.halt(InstructionResult::MemoryLimitOOG); } @@ -304,7 +313,7 @@ impl Interpreter { /// This uses dummy Host. #[inline] pub fn step_dummy(&mut self, instruction_table: &InstructionTable) { - self.step(instruction_table, &mut DummyHost); + self.step(instruction_table, &mut DummyHost::default()); } /// Executes the interpreter until it returns or stops. @@ -361,6 +370,15 @@ impl InterpreterResult { } } + /// Returns a new `InterpreterResult` for an out-of-gas error with the given gas limit. + pub fn new_oog(gas_limit: u64) -> Self { + Self { + result: InstructionResult::OutOfGas, + output: Bytes::default(), + gas: Gas::new_spent(gas_limit), + } + } + /// Returns whether the instruction result is a success. #[inline] pub const fn is_ok(&self) -> bool { @@ -431,3 +449,78 @@ mod tests { ); } } + +#[test] +fn test_mstore_big_offset_memory_oog() { + use super::*; + use crate::{host::DummyHost, instructions::instruction_table}; + use bytecode::Bytecode; + use primitives::Bytes; + + let code = Bytes::from( + &[ + 0x60, 0x00, // PUSH1 0x00 + 0x61, 0x27, 0x10, // PUSH2 0x2710 (10,000) + 0x52, // MSTORE + 0x00, // STOP + ][..], + ); + let bytecode = Bytecode::new_raw(code); + + let mut interpreter = Interpreter::::new( + SharedMemory::new(), + ExtBytecode::new(bytecode), + InputsImpl::default(), + false, + SpecId::default(), + 1000, + ); + + let table = instruction_table::(); + let mut host = DummyHost::default(); + let action = interpreter.run_plain(&table, &mut host); + + assert!(action.is_return()); + assert_eq!( + action.instruction_result(), + Some(InstructionResult::MemoryOOG) + ); +} + +#[test] +#[cfg(feature = "memory_limit")] +fn test_mstore_big_offset_memory_limit_oog() { + use super::*; + use crate::{host::DummyHost, instructions::instruction_table}; + use bytecode::Bytecode; + use primitives::Bytes; + + let code = Bytes::from( + &[ + 0x60, 0x00, // PUSH1 0x00 + 0x61, 0x27, 0x10, // PUSH2 0x2710 (10,000) + 0x52, // MSTORE + 0x00, // STOP + ][..], + ); + let bytecode = Bytecode::new_raw(code); + + let mut interpreter = Interpreter::::new( + SharedMemory::new_with_memory_limit(1000), + ExtBytecode::new(bytecode), + InputsImpl::default(), + false, + SpecId::default(), + 100000, + ); + + let table = instruction_table::(); + let mut host = DummyHost::default(); + let action = interpreter.run_plain(&table, &mut host); + + assert!(action.is_return()); + assert_eq!( + action.instruction_result(), + Some(InstructionResult::MemoryLimitOOG) + ); +} diff --git a/crates/interpreter/src/interpreter/ext_bytecode/serde.rs b/crates/interpreter/src/interpreter/ext_bytecode/serde.rs index 8c5457e361..0635f29a0d 100644 --- a/crates/interpreter/src/interpreter/ext_bytecode/serde.rs +++ b/crates/interpreter/src/interpreter/ext_bytecode/serde.rs @@ -2,8 +2,7 @@ use super::ExtBytecode; use crate::interpreter::Jumps; use primitives::B256; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use std::borrow::Cow; -use std::format; +use std::{borrow::Cow, format}; #[derive(Serialize, Deserialize)] struct ExtBytecodeSerde<'a> { diff --git a/crates/interpreter/src/interpreter/shared_memory.rs b/crates/interpreter/src/interpreter/shared_memory.rs index 6eb28741a4..f7a7598a8c 100644 --- a/crates/interpreter/src/interpreter/shared_memory.rs +++ b/crates/interpreter/src/interpreter/shared_memory.rs @@ -1,4 +1,6 @@ use super::MemoryTr; +use crate::InstructionResult; +use context_interface::cfg::GasParams; use core::{ cell::{Ref, RefCell, RefMut}, cmp::min, @@ -125,6 +127,17 @@ impl MemoryTr for SharedMemory { self.resize(new_size); true } + + /// Returns `true` if the `new_size` for the current context memory will + /// make the shared buffer length exceed the `memory_limit`. + #[cfg(feature = "memory_limit")] + #[inline] + fn limit_reached(&self, offset: usize, len: usize) -> bool { + self.my_checkpoint + .saturating_add(offset) + .saturating_add(len) as u64 + > self.memory_limit + } } impl SharedMemory { @@ -184,6 +197,17 @@ impl SharedMemory { } } + /// Sets the memory limit in bytes. + #[inline] + pub fn set_memory_limit(&mut self, limit: u64) { + #[cfg(feature = "memory_limit")] + { + self.memory_limit = limit; + } + // for clippy. + let _ = limit; + } + #[inline] fn buffer(&self) -> &Rc>> { debug_assert!(self.buffer.is_some(), "cannot use SharedMemory::empty"); @@ -200,14 +224,6 @@ impl SharedMemory { self.buffer().dbg_borrow_mut() } - /// Returns `true` if the `new_size` for the current context memory will - /// make the shared buffer length exceed the `memory_limit`. - #[cfg(feature = "memory_limit")] - #[inline] - pub fn limit_reached(&self, new_size: usize) -> bool { - self.my_checkpoint.saturating_add(new_size) as u64 > self.memory_limit - } - /// Prepares the shared memory for a new child context. /// /// # Panics @@ -542,24 +558,29 @@ unsafe fn set_data(dst: &mut [u8], src: &[u8], dst_offset: usize, src_offset: us /// i.e. it rounds up the number bytes to number of words. #[inline] pub const fn num_words(len: usize) -> usize { - len.saturating_add(31) / 32 + len.div_ceil(32) } /// Performs EVM memory resize. #[inline] -#[must_use] pub fn resize_memory( gas: &mut crate::Gas, memory: &mut Memory, + gas_table: &GasParams, offset: usize, len: usize, -) -> bool { +) -> Result<(), InstructionResult> { + #[cfg(feature = "memory_limit")] + if memory.limit_reached(offset, len) { + return Err(InstructionResult::MemoryLimitOOG); + } + let new_num_words = num_words(offset.saturating_add(len)); if new_num_words > gas.memory().words_num { - resize_memory_cold(gas, memory, new_num_words) - } else { - true + return resize_memory_cold(gas, memory, gas_table, new_num_words); } + + Ok(()) } #[cold] @@ -567,18 +588,21 @@ pub fn resize_memory( fn resize_memory_cold( gas: &mut crate::Gas, memory: &mut Memory, + gas_table: &GasParams, new_num_words: usize, -) -> bool { +) -> Result<(), InstructionResult> { + let cost = gas_table.memory_cost(new_num_words); let cost = unsafe { gas.memory_mut() - .record_new_len(new_num_words) + .set_words_num(new_num_words, cost) .unwrap_unchecked() }; + if !gas.record_cost(cost) { - return false; + return Err(InstructionResult::MemoryOOG); } memory.resize(new_num_words * 32); - true + Ok(()) } #[cfg(test)] @@ -595,7 +619,9 @@ mod tests { assert_eq!(num_words(63), 2); assert_eq!(num_words(64), 2); assert_eq!(num_words(65), 3); - assert_eq!(num_words(usize::MAX), usize::MAX / 32); + assert_eq!(num_words(usize::MAX - 31), usize::MAX / 32); + assert_eq!(num_words(usize::MAX - 30), (usize::MAX / 32) + 1); + assert_eq!(num_words(usize::MAX), (usize::MAX / 32) + 1); } #[test] diff --git a/crates/interpreter/src/interpreter/stack.rs b/crates/interpreter/src/interpreter/stack.rs index 761ec7da6c..62c4a7bf0c 100644 --- a/crates/interpreter/src/interpreter/stack.rs +++ b/crates/interpreter/src/interpreter/stack.rs @@ -153,7 +153,16 @@ impl Stack { #[inline] #[cfg_attr(debug_assertions, track_caller)] pub fn pop(&mut self) -> Result { - self.data.pop().ok_or(InstructionResult::StackUnderflow) + //Todo: can you likely instrincts to show the else branch is more likely to be hit + let len = self.data.len(); + if primitives::hints_util::unlikely(len == 0) { + Err(InstructionResult::StackUnderflow) + } else { + unsafe { + self.data.set_len(len - 1); + Ok(core::ptr::read(self.data.as_ptr().add(len - 1))) + } + } } /// Removes the topmost element from the stack and returns it. @@ -215,10 +224,15 @@ impl Stack { pub fn push(&mut self, value: U256) -> bool { // In debug builds, verify we have sufficient capacity provisioned. debug_assert!(self.data.capacity() >= STACK_LIMIT); - if self.data.len() == STACK_LIMIT { + let len = self.data.len(); + if len == STACK_LIMIT { return false; } - self.data.push(value); + unsafe { + let end = self.data.as_mut_ptr().add(len); + core::ptr::write(end, value); + self.data.set_len(len + 1); + } true } diff --git a/crates/interpreter/src/interpreter_action/call_inputs.rs b/crates/interpreter/src/interpreter_action/call_inputs.rs index 581bc15c95..e7224607cf 100644 --- a/crates/interpreter/src/interpreter_action/call_inputs.rs +++ b/crates/interpreter/src/interpreter_action/call_inputs.rs @@ -79,10 +79,10 @@ pub struct CallInputs { /// /// Previously `context.code_address`. pub bytecode_address: Address, - /// Bytecode that is going to be executed. - pub bytecode: Bytecode, - /// Bytecode hash, - pub bytecode_hash: B256, + /// Known bytecode and its hash. + /// If None, bytecode will be loaded from the account at `bytecode_address`. + /// If Some((hash, bytecode)), the provided bytecode and hash will be used. + pub known_bytecode: Option<(B256, Bytecode)>, /// Target address, this account storage is going to be modified. /// /// Previously `context.address`. diff --git a/crates/interpreter/src/interpreter_action/call_outcome.rs b/crates/interpreter/src/interpreter_action/call_outcome.rs index 55a69989f1..d8e851c302 100644 --- a/crates/interpreter/src/interpreter_action/call_outcome.rs +++ b/crates/interpreter/src/interpreter_action/call_outcome.rs @@ -1,6 +1,7 @@ use crate::{Gas, InstructionResult, InterpreterResult}; use core::ops::Range; -use primitives::Bytes; +use primitives::{Bytes, Log}; +use std::vec::Vec; /// Represents the outcome of a call operation in a virtual machine. /// @@ -18,6 +19,12 @@ pub struct CallOutcome { pub result: InterpreterResult, /// The range in memory where the output data is located pub memory_offset: Range, + /// Flag to indicate if the call is precompile call. + /// Used by inspector so it can copy the logs for Inspector::logs call. + pub was_precompile_called: bool, + /// Precompile call logs. Needs as revert/halt would delete them from Journal. + /// So they can't be accessed by inspector. + pub precompile_call_logs: Vec, } impl CallOutcome { @@ -33,9 +40,21 @@ impl CallOutcome { Self { result, memory_offset, + was_precompile_called: false, + precompile_call_logs: Vec::new(), } } + /// Constructs a new [`CallOutcome`] for an out-of-gas error. + /// + /// # Arguments + /// + /// * `gas_limit` - The gas limit that was exceeded. + /// * `memory_offset` - The range in memory indicating where the output data is stored. + pub fn new_oog(gas_limit: u64, memory_offset: Range) -> Self { + Self::new(InterpreterResult::new_oog(gas_limit), memory_offset) + } + /// Returns a reference to the instruction result. /// /// Provides access to the result of the executed instruction. diff --git a/crates/interpreter/src/interpreter_action/create_inputs.rs b/crates/interpreter/src/interpreter_action/create_inputs.rs index d44ed3eac8..1085c3dab8 100644 --- a/crates/interpreter/src/interpreter_action/create_inputs.rs +++ b/crates/interpreter/src/interpreter_action/create_inputs.rs @@ -1,31 +1,109 @@ use context_interface::CreateScheme; +use core::cell::OnceCell; use primitives::{Address, Bytes, U256}; /// Inputs for a create call -#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] +#[derive(Clone, Debug, Default, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct CreateInputs { /// Caller address of the EVM - pub caller: Address, + caller: Address, /// The create scheme - pub scheme: CreateScheme, + scheme: CreateScheme, /// The value to transfer - pub value: U256, + value: U256, /// The init code of the contract - pub init_code: Bytes, + init_code: Bytes, /// The gas limit of the call - pub gas_limit: u64, + gas_limit: u64, + /// Cached created address. This is computed lazily and cached to avoid + /// redundant keccak computations when inspectors call `created_address`. + #[cfg_attr(feature = "serde", serde(skip))] + cached_address: OnceCell
, } impl CreateInputs { + /// Creates a new `CreateInputs` instance. + pub fn new( + caller: Address, + scheme: CreateScheme, + value: U256, + init_code: Bytes, + gas_limit: u64, + ) -> Self { + Self { + caller, + scheme, + value, + init_code, + gas_limit, + cached_address: OnceCell::new(), + } + } + /// Returns the address that this create call will create. + /// + /// The result is cached to avoid redundant keccak computations. pub fn created_address(&self, nonce: u64) -> Address { - match self.scheme { + *self.cached_address.get_or_init(|| match self.scheme { CreateScheme::Create => self.caller.create(nonce), CreateScheme::Create2 { salt } => self .caller .create2_from_code(salt.to_be_bytes(), &self.init_code), CreateScheme::Custom { address } => address, - } + }) + } + + /// Returns the caller address of the EVM. + pub fn caller(&self) -> Address { + self.caller + } + + /// Returns the create scheme of the EVM. + pub fn scheme(&self) -> CreateScheme { + self.scheme + } + + /// Returns the value to transfer. + pub fn value(&self) -> U256 { + self.value + } + + /// Returns the init code of the contract. + pub fn init_code(&self) -> &Bytes { + &self.init_code + } + + /// Returns the gas limit of the call. + pub fn gas_limit(&self) -> u64 { + self.gas_limit + } + + /// Set call + pub fn set_call(&mut self, caller: Address) { + self.caller = caller; + self.cached_address = OnceCell::new(); + } + + /// Set scheme + pub fn set_scheme(&mut self, scheme: CreateScheme) { + self.scheme = scheme; + self.cached_address = OnceCell::new(); + } + + /// Set value + pub fn set_value(&mut self, value: U256) { + self.value = value; + } + + /// Set init code + pub fn set_init_code(&mut self, init_code: Bytes) { + self.init_code = init_code; + self.cached_address = OnceCell::new(); + } + + /// Set gas limit + pub fn set_gas_limit(&mut self, gas_limit: u64) { + self.gas_limit = gas_limit; } } diff --git a/crates/interpreter/src/interpreter_action/create_outcome.rs b/crates/interpreter/src/interpreter_action/create_outcome.rs index 11d7204e66..d10e517cda 100644 --- a/crates/interpreter/src/interpreter_action/create_outcome.rs +++ b/crates/interpreter/src/interpreter_action/create_outcome.rs @@ -30,6 +30,19 @@ impl CreateOutcome { Self { result, address } } + /// Constructs a new [`CreateOutcome`] for an out-of-gas error. + /// + /// # Arguments + /// + /// * `gas_limit` - The gas limit that was exceeded. + /// + /// # Returns + /// + /// A new [`CreateOutcome`] instance with no address. + pub fn new_oog(gas_limit: u64) -> Self { + Self::new(InterpreterResult::new_oog(gas_limit), None) + } + /// Retrieves a reference to the [`InstructionResult`] from the [`InterpreterResult`]. /// /// This method provides access to the [`InstructionResult`] which represents the diff --git a/crates/interpreter/src/interpreter_types.rs b/crates/interpreter/src/interpreter_types.rs index 0b1540a26e..0195ee93fa 100644 --- a/crates/interpreter/src/interpreter_types.rs +++ b/crates/interpreter/src/interpreter_types.rs @@ -1,6 +1,8 @@ use crate::{CallInput, InstructionResult, InterpreterAction}; -use core::cell::Ref; -use core::ops::{Deref, Range}; +use core::{ + cell::Ref, + ops::{Deref, Range}, +}; use primitives::{hardfork::SpecId, Address, Bytes, B256, U256}; /// Helper function to read immediates data from the bytecode @@ -144,8 +146,13 @@ pub trait MemoryTr { /// /// # Note /// - /// It checks memory limits. + /// It checks if the memory allocation fits under gas cap. fn resize(&mut self, new_size: usize) -> bool; + + /// Returns `true` if the `new_size` for the current context memory will + /// make the shared buffer length exceed the `memory_limit`. + #[cfg(feature = "memory_limit")] + fn limit_reached(&self, offset: usize, len: usize) -> bool; } /// Functions needed for Interpreter Stack operations. diff --git a/crates/interpreter/src/lib.rs b/crates/interpreter/src/lib.rs index 8c0a6b87b1..dc4f546424 100644 --- a/crates/interpreter/src/lib.rs +++ b/crates/interpreter/src/lib.rs @@ -27,11 +27,11 @@ pub mod interpreter_types; // Reexport primary types. pub use context_interface::{ + cfg::gas::InitialAndFloorGas, context::{SStoreResult, SelfDestructResult, StateLoad}, - CreateScheme, + host, CreateScheme, Host, }; -pub use context_interface::{host, Host}; -pub use gas::{Gas, InitialAndFloorGas}; +pub use gas::Gas; pub use instruction_context::InstructionContext; pub use instruction_result::*; pub use instructions::{instruction_table, Instruction, InstructionTable}; diff --git a/crates/op-revm/CHANGELOG.md b/crates/op-revm/CHANGELOG.md index 1e2b533343..e7731b8828 100644 --- a/crates/op-revm/CHANGELOG.md +++ b/crates/op-revm/CHANGELOG.md @@ -7,6 +7,83 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [15.0.0](https://github.com/bluealloy/revm/compare/op-revm-v14.1.0...op-revm-v15.0.0) - 2026-01-15 + +### Added + +- new gas params, tx initial gas and codedeposit ([#3260](https://github.com/bluealloy/revm/pull/3260)) +- move GasParams to Cfg ([#3229](https://github.com/bluealloy/revm/pull/3229)) +- BAL EIP-7928 ([#3070](https://github.com/bluealloy/revm/pull/3070)) +- early return if the l1 fee scalar is zero ([#3213](https://github.com/bluealloy/revm/pull/3213)) +- Restrict Database::Error. JournaledAccountTr ([#3199](https://github.com/bluealloy/revm/pull/3199)) + +### Other + +- fix typos, grammar errors, and improve documentation consistency ([#3294](https://github.com/bluealloy/revm/pull/3294)) +- happy new year, 2026 licence ([#3272](https://github.com/bluealloy/revm/pull/3272)) +- Remove redundant tx fetch in Optimism handler gas accounting ([#3220](https://github.com/bluealloy/revm/pull/3220)) +- *(fmt)* merge all imports ([#3184](https://github.com/bluealloy/revm/pull/3184)) + +## [14.1.0](https://github.com/bluealloy/revm/compare/op-revm-v14.0.0...op-revm-v14.1.0) - 2025-11-14 + +### Fixed + +- *(op-revm)* return error when enveloped_tx is missing ([#3143](https://github.com/bluealloy/revm/pull/3143)) + +## [14.0.0](https://github.com/bluealloy/revm/compare/op-revm-v12.0.2...op-revm-v14.0.0) - 2025-11-10 + +### Added + +- *(precompiles)* add performant PrecompileError::OtherCowStr variant ([#3144](https://github.com/bluealloy/revm/pull/3144)) +- process precompile logs to inspector ([#3148](https://github.com/bluealloy/revm/pull/3148)) + +## [12.0.2](https://github.com/bluealloy/revm/compare/op-revm-v12.0.1...op-revm-v12.0.2) - 2025-11-10 + +### Fixed + +- *(op)* Ensure L1Block account is always loaded ([#3150](https://github.com/bluealloy/revm/pull/3150)) + +## [12.0.1](https://github.com/bluealloy/revm/compare/op-revm-v12.0.0...op-revm-v12.0.1) - 2025-11-07 + +### Other + +- updated the following local packages: revm + +## [12.0.0](https://github.com/bluealloy/revm/compare/op-revm-v11.2.0...op-revm-v12.0.0) - 2025-10-30 + +### Added + +- JournaledAccount, a nice way to update and track changes ([#3086](https://github.com/bluealloy/revm/pull/3086)) + +### Fixed + +- *(jovian)* fixes the DA footprint update storage slot. fix l1 fork associated with Jovian. ([#3120](https://github.com/bluealloy/revm/pull/3120)) +- *(op-revm)* add missing enveloped_tx validation in validate_env ([#3094](https://github.com/bluealloy/revm/pull/3094)) + +### Other + +- *(op)* use helper function in validate against state ([#3069](https://github.com/bluealloy/revm/pull/3069)) + + +## [11.3.0](https://github.com/bluealloy/revm/compare/op-revm-v11.2.0...op-revm-v11.3.0) - 2025-10-28 + +### Added + +- *(precompiles/jovian)* add jovian precompiles to revm ([#3128](https://github.com/bluealloy/revm/pull/3128)) + + +## [11.2.0](https://github.com/bluealloy/revm/compare/op-revm-v11.1.2...op-revm-v11.2.0) - 2025-10-17 + +### Other + +- updated the following local packages: revm + +## [11.1.2](https://github.com/bluealloy/revm/compare/op-revm-v11.1.1...op-revm-v11.1.2) - 2025-10-15 + +### Other + +- updated the following local packages: revm + ## [11.1.1](https://github.com/bluealloy/revm/compare/op-revm-v11.1.0...op-revm-v11.1.1) - 2025-10-15 ### Other diff --git a/crates/op-revm/Cargo.toml b/crates/op-revm/Cargo.toml index 90b3b6ae91..59e1c7d093 100644 --- a/crates/op-revm/Cargo.toml +++ b/crates/op-revm/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "op-revm" description = "Optimism variant of Revm" -version = "11.1.1" +version = "15.0.0" authors.workspace = true edition.workspace = true keywords.workspace = true diff --git a/crates/op-revm/LICENSE b/crates/op-revm/LICENSE index be6d350ebe..7edb0405ff 100644 --- a/crates/op-revm/LICENSE +++ b/crates/op-revm/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021-2025 draganrakita +Copyright (c) 2021-2026 draganrakita Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/crates/op-revm/src/constants.rs b/crates/op-revm/src/constants.rs index 243343e804..2c93bff13b 100644 --- a/crates/op-revm/src/constants.rs +++ b/crates/op-revm/src/constants.rs @@ -20,7 +20,7 @@ pub const OPERATOR_FEE_CONSTANT_OFFSET: usize = 24; /// The Jovian daFootprintGasScalar value is packed into a single storage slot. Byte offset within /// the storage slot of the 16-byte daFootprintGasScalar attribute. -pub const DA_FOOTPRINT_GAS_SCALAR_OFFSET: usize = 0; +pub const DA_FOOTPRINT_GAS_SCALAR_OFFSET: usize = 18; /// The fixed point decimal scaling factor associated with the operator fee scalar. /// @@ -48,9 +48,9 @@ pub const ECOTONE_L1_FEE_SCALARS_SLOT: U256 = U256::from_limbs([3u64, 0, 0, 0]); /// offsets [OPERATOR_FEE_SCALAR_OFFSET] and [OPERATOR_FEE_CONSTANT_OFFSET] respectively. pub const OPERATOR_FEE_SCALARS_SLOT: U256 = U256::from_limbs([8u64, 0, 0, 0]); -/// As of the Jovian upgrade, this storage slot stores the 32-bit daFootprintGasScalar attribute at +/// As of the Jovian upgrade, this storage slot stores the 16-bit daFootprintGasScalar attribute at /// offset [DA_FOOTPRINT_GAS_SCALAR_OFFSET]. -pub const DA_FOOTPRINT_GAS_SCALAR_SLOT: U256 = U256::from_limbs([9u64, 0, 0, 0]); +pub const DA_FOOTPRINT_GAS_SCALAR_SLOT: U256 = U256::from_limbs([8u64, 0, 0, 0]); /// An empty 64-bit set of scalar values. pub const EMPTY_SCALARS: [u8; 8] = [0u8; 8]; diff --git a/crates/op-revm/src/evm.rs b/crates/op-revm/src/evm.rs index a93a172756..64f28a3e35 100644 --- a/crates/op-revm/src/evm.rs +++ b/crates/op-revm/src/evm.rs @@ -1,7 +1,7 @@ //! Contains the `[OpEvm]` type and its implementation of the execution EVM traits. -use crate::precompiles::OpPrecompiles; +use crate::{precompiles::OpPrecompiles, OpSpecId}; use revm::{ - context::{ContextError, ContextSetters, Evm, FrameStack}, + context::{Cfg, ContextError, ContextSetters, Evm, FrameStack}, context_interface::ContextTr, handler::{ evm::FrameTr, @@ -26,17 +26,25 @@ pub struct OpEvm< pub Evm, ); -impl OpEvm, OpPrecompiles> { +impl + Clone>>, INSP> + OpEvm, OpPrecompiles> +{ /// Create a new Optimism EVM. pub fn new(ctx: CTX, inspector: INSP) -> Self { + let spec: OpSpecId = ctx.cfg().spec().into(); Self(Evm { ctx, inspector, - instruction: EthInstructions::new_mainnet(), - precompiles: OpPrecompiles::default(), + instruction: EthInstructions::new_mainnet_with_spec(spec.into()), + precompiles: OpPrecompiles::new_with_spec(spec), frame_stack: FrameStack::new_prealloc(8), }) } + + /// Consumes self and returns the inner context. + pub fn into_context(self) -> CTX { + self.0.ctx + } } impl OpEvm { diff --git a/crates/op-revm/src/fast_lz.rs b/crates/op-revm/src/fast_lz.rs index a1a262a079..7d1f1c57a6 100644 --- a/crates/op-revm/src/fast_lz.rs +++ b/crates/op-revm/src/fast_lz.rs @@ -112,14 +112,13 @@ fn u24(input: &[u8], idx: u32) -> u32 { mod tests { use super::*; use crate::api::{builder::OpBuilder, default_ctx::DefaultOp}; - use alloy_sol_types::sol; - use alloy_sol_types::SolCall; + use alloy_sol_types::{sol, SolCall}; use revm::{ bytecode::Bytecode, database::{BenchmarkDB, EEADDRESS, FFADDRESS}, primitives::{bytes, Bytes, TxKind, U256}, + Context, ExecuteEvm, }; - use revm::{Context, ExecuteEvm}; use rstest::rstest; use std::vec::Vec; diff --git a/crates/op-revm/src/handler.rs b/crates/op-revm/src/handler.rs index 98c054f467..ada5a49a0f 100644 --- a/crates/op-revm/src/handler.rs +++ b/crates/op-revm/src/handler.rs @@ -6,7 +6,11 @@ use crate::{ L1BlockInfo, OpHaltReason, OpSpecId, }; use revm::{ - context::{result::InvalidTransaction, LocalContextTr}, + context::{ + journaled_state::{account::JournaledAccountTr, JournalCheckpoint}, + result::InvalidTransaction, + LocalContextTr, + }, context_interface::{ context::ContextError, result::{EVMError, ExecutionResult, FromStringError}, @@ -16,7 +20,7 @@ use revm::{ evm::FrameTr, handler::EvmTrError, post_execution::{self, reimburse_caller}, - pre_execution::validate_account_nonce_and_code, + pre_execution::{calculate_caller_fee, validate_account_nonce_and_code_with_components}, EthFrame, EvmTr, FrameResult, Handler, MainnetHandler, }, inspector::{Inspector, InspectorEvmTr, InspectorHandler}, @@ -88,6 +92,12 @@ where } return Ok(()); } + + // Check that non-deposit transactions have enveloped_tx set + if tx.enveloped_tx().is_none() { + return Err(OpTransactionError::MissingEnvelopedTx.into()); + } + self.mainnet.validate_env(evm) } @@ -95,24 +105,15 @@ where &self, evm: &mut Self::Evm, ) -> Result<(), Self::Error> { - let ctx = evm.ctx(); - - let basefee = ctx.block().basefee() as u128; - let blob_price = ctx.block().blob_gasprice().unwrap_or_default(); - let is_deposit = ctx.tx().tx_type() == DEPOSIT_TRANSACTION_TYPE; - let spec = ctx.cfg().spec(); - let block_number = ctx.block().number(); - let is_balance_check_disabled = ctx.cfg().is_balance_check_disabled(); - let is_eip3607_disabled = ctx.cfg().is_eip3607_disabled(); - let is_nonce_check_disabled = ctx.cfg().is_nonce_check_disabled(); + let (block, tx, cfg, journal, chain, _) = evm.ctx().all_mut(); + let spec = cfg.spec(); - if is_deposit { - let (block, tx, cfg, journal, _, _) = evm.ctx().all_mut(); + if tx.tx_type() == DEPOSIT_TRANSACTION_TYPE { let basefee = block.basefee() as u128; let blob_price = block.blob_gasprice().unwrap_or_default(); // deposit skips max fee check and just deducts the effective balance spending. - let caller_account = journal.load_account_code(tx.caller())?.data; + let mut caller = journal.load_account_with_code_mut(tx.caller())?.data; let effective_balance_spending = tx .effective_balance_spending(basefee, blob_price) @@ -120,9 +121,8 @@ where - tx.value(); // Mind value should be added first before subtracting the effective balance spending. - let mut new_balance = caller_account - .info - .balance + let mut new_balance = caller + .balance() .saturating_add(U256::from(tx.mint().unwrap_or_default())) .saturating_sub(effective_balance_spending); @@ -132,101 +132,53 @@ where new_balance = new_balance.max(tx.value()); } - let old_balance = - caller_account.caller_initial_modification(new_balance, tx.kind().is_call()); - - // NOTE: all changes to the caller account should journaled so in case of error - // we can revert the changes. - journal.caller_accounting_journal_entry(tx.caller(), old_balance, tx.kind().is_call()); + // set the new balance and bump the nonce if it is a call + caller.set_balance(new_balance); + if tx.kind().is_call() { + caller.bump_nonce(); + } return Ok(()); } - let mut additional_cost = U256::ZERO; - // L1 block info is stored in the context for later use. // and it will be reloaded from the database if it is not for the current block. - if ctx.chain().l2_block != Some(block_number) { - *ctx.chain_mut() = L1BlockInfo::try_fetch(ctx.db_mut(), block_number, spec)?; + if chain.l2_block != Some(block.number()) { + *chain = L1BlockInfo::try_fetch(journal.db_mut(), block.number(), spec)?; } - if !ctx.cfg().is_fee_charge_disabled() { - // account for additional cost of l1 fee and operator fee - // account for additional cost of l1 fee and operator fee - let Some(enveloped_tx) = ctx.tx().enveloped_tx().cloned() else { + let mut caller_account = journal.load_account_with_code_mut(tx.caller())?.data; + + // validates account nonce and code + validate_account_nonce_and_code_with_components(&caller_account.account().info, tx, cfg)?; + + // check additional cost and deduct it from the caller's balances + let mut balance = caller_account.account().info.balance; + + if !cfg.is_fee_charge_disabled() { + let Some(additional_cost) = chain.tx_cost_with_tx(tx, spec) else { return Err(ERROR::from_string( "[OPTIMISM] Failed to load enveloped transaction.".into(), )); }; - - // compute L1 cost - additional_cost = ctx.chain_mut().calculate_tx_l1_cost(&enveloped_tx, spec); - - // compute operator fee - if spec.is_enabled_in(OpSpecId::ISTHMUS) { - let gas_limit = U256::from(ctx.tx().gas_limit()); - let operator_fee_charge = - ctx.chain() - .operator_fee_charge(&enveloped_tx, gas_limit, spec); - additional_cost = additional_cost.saturating_add(operator_fee_charge); - } - } - - let (tx, journal) = ctx.tx_journal_mut(); - - let caller_account = journal.load_account_code(tx.caller())?.data; - - // validates account nonce and code - validate_account_nonce_and_code( - &mut caller_account.info, - tx.nonce(), - is_eip3607_disabled, - is_nonce_check_disabled, - )?; - - let mut new_balance = caller_account.info.balance; - - // Check if account has enough balance for `gas_limit * max_fee`` and value transfer. - // Transfer will be done inside `*_inner` functions. - if !is_balance_check_disabled { - // check additional cost and deduct it from the caller's balances - let Some(balance) = new_balance.checked_sub(additional_cost) else { + let Some(new_balance) = balance.checked_sub(additional_cost) else { return Err(InvalidTransaction::LackOfFundForMaxFee { fee: Box::new(additional_cost), - balance: Box::new(new_balance), + balance: Box::new(balance), } .into()); }; - tx.ensure_enough_balance(balance)?; + balance = new_balance } - // subtracting max balance spending with value that is going to be deducted later in the call. - let gas_balance_spending = tx - .gas_balance_spending(basefee, blob_price) - .expect("effective balance is always smaller than max balance so it can't overflow"); - - // If the transaction is not a deposit transaction, subtract the L1 data fee from the - // caller's balance directly after minting the requested amount of ETH. - // Additionally deduct the operator fee from the caller's account. - // - // In case of deposit additional cost will be zero. - let op_gas_balance_spending = gas_balance_spending.saturating_add(additional_cost); + let balance = calculate_caller_fee(balance, tx, block, cfg)?; - new_balance = new_balance.saturating_sub(op_gas_balance_spending); - - if is_balance_check_disabled { - // Make sure the caller's balance is at least the value of the transaction. - // this is not consensus critical, and it is used in testing. - new_balance = new_balance.max(tx.value()); + // make changes to the account + caller_account.set_balance(balance); + if tx.kind().is_call() { + caller_account.bump_nonce(); } - let old_balance = - caller_account.caller_initial_modification(new_balance, tx.kind().is_call()); - - // NOTE: all changes to the caller account should journaled so in case of error - // we can revert the changes. - journal.caller_accounting_journal_entry(tx.caller(), old_balance, tx.kind().is_call()); - Ok(()) } @@ -268,13 +220,10 @@ where // Regolith, gas is reported as normal. gas.erase_cost(remaining); gas.record_refund(refunded); - } else if is_deposit { - let tx = ctx.tx(); - if tx.is_system_transaction() { - // System transactions were a special type of deposit transaction in - // the Bedrock hardfork that did not incur any gas costs. - gas.erase_cost(tx_gas_limit); - } + } else if is_deposit && tx.is_system_transaction() { + // System transactions were a special type of deposit transaction in + // the Bedrock hardfork that did not incur any gas costs. + gas.erase_cost(tx_gas_limit); } } else if instruction_result.is_revert() { // On Optimism, deposit transactions report gas usage uniquely to other @@ -429,7 +378,11 @@ where error: Self::Error, ) -> Result, Self::Error> { let is_deposit = evm.ctx().tx().tx_type() == DEPOSIT_TRANSACTION_TYPE; - let output = if error.is_tx_error() && is_deposit { + let is_tx_error = error.is_tx_error(); + let mut output = Err(error); + + // Deposit transaction can't fail so we manually handle it here. + if is_tx_error && is_deposit { let ctx = evm.ctx(); let spec = ctx.cfg().spec(); let tx = ctx.tx(); @@ -437,9 +390,11 @@ where let mint = tx.mint(); let is_system_tx = tx.is_system_transaction(); let gas_limit = tx.gas_limit(); + let journal = evm.ctx().journal_mut(); // discard all changes of this transaction - evm.ctx().journal_mut().discard_tx(); + // Default JournalCheckpoint is the first checkpoint and will wipe all changes. + journal.checkpoint_revert(JournalCheckpoint::default()); // If the transaction is a deposit transaction and it failed // for any reason, the caller nonce must be bumped, and the @@ -450,23 +405,14 @@ where // Increment sender nonce and account balance for the mint amount. Deposits // always persist the mint amount, even if the transaction fails. - let acc: &mut revm::state::Account = evm.ctx().journal_mut().load_account(caller)?.data; + let mut acc = journal.load_account_mut(caller)?; + acc.bump_nonce(); + acc.incr_balance(U256::from(mint.unwrap_or_default())); - let old_balance = acc.info.balance; + drop(acc); // Drop acc to avoid borrow checker issues. - // decrement transaction id as it was incremented when we discarded the tx. - acc.transaction_id -= 1; - acc.info.nonce = acc.info.nonce.saturating_add(1); - acc.info.balance = acc - .info - .balance - .saturating_add(U256::from(mint.unwrap_or_default())); - acc.mark_touch(); - - // add journal entry for accounts - evm.ctx() - .journal_mut() - .caller_accounting_journal_entry(caller, old_balance, true); + // We can now commit the changes. + journal.commit_tx(); // The gas used of a failed deposit post-regolith is the gas // limit of the transaction. pre-regolith, it is the gas limit @@ -478,13 +424,12 @@ where 0 }; // clear the journal - Ok(ExecutionResult::Halt { + output = Ok(ExecutionResult::Halt { reason: OpHaltReason::FailedDeposit, gas_used, }) - } else { - Err(error) - }; + } + // do the cleanup evm.ctx().chain_mut().clear_tx_l1_cost(); evm.ctx().local_mut().clear(); @@ -512,15 +457,14 @@ mod tests { use crate::{ api::default_ctx::OpContext, constants::{ - BASE_FEE_SCALAR_OFFSET, DA_FOOTPRINT_GAS_SCALAR_SLOT, ECOTONE_L1_BLOB_BASE_FEE_SLOT, - ECOTONE_L1_FEE_SCALARS_SLOT, L1_BASE_FEE_SLOT, L1_BLOCK_CONTRACT, - OPERATOR_FEE_SCALARS_SLOT, + BASE_FEE_SCALAR_OFFSET, ECOTONE_L1_BLOB_BASE_FEE_SLOT, ECOTONE_L1_FEE_SCALARS_SLOT, + L1_BASE_FEE_SLOT, L1_BLOCK_CONTRACT, OPERATOR_FEE_SCALARS_SLOT, }, DefaultOp, OpBuilder, OpTransaction, }; use alloy_primitives::uint; use revm::{ - context::{BlockEnv, Context, TxEnv}, + context::{BlockEnv, CfgEnv, Context, TxEnv}, context_interface::result::InvalidTransaction, database::InMemoryDB, database_interface::EmptyDB, @@ -567,7 +511,7 @@ mod tests { .base(TxEnv::builder().gas_limit(100)) .build_fill(), ) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::BEDROCK); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::BEDROCK)); let gas = call_last_frame_return(ctx, InstructionResult::Revert, Gas::new(90)); assert_eq!(gas.remaining(), 90); @@ -583,7 +527,7 @@ mod tests { .base(TxEnv::builder().gas_limit(100)) .build_fill(), ) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::REGOLITH)); let gas = call_last_frame_return(ctx, InstructionResult::Stop, Gas::new(90)); assert_eq!(gas.remaining(), 90); @@ -600,7 +544,7 @@ mod tests { .source_hash(B256::from([1u8; 32])) .build_fill(), ) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::REGOLITH)); let mut ret_gas = Gas::new(90); ret_gas.record_refund(20); @@ -625,7 +569,7 @@ mod tests { .source_hash(B256::from([1u8; 32])) .build_fill(), ) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::BEDROCK); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::BEDROCK)); let gas = call_last_frame_return(ctx, InstructionResult::Stop, Gas::new(90)); assert_eq!(gas.remaining(), 0); assert_eq!(gas.spent(), 100); @@ -642,7 +586,7 @@ mod tests { .is_system_transaction() .build_fill(), ) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::BEDROCK); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::BEDROCK)); let gas = call_last_frame_return(ctx, InstructionResult::Stop, Gas::new(90)); assert_eq!(gas.remaining(), 100); assert_eq!(gas.spent(), 0); @@ -669,7 +613,7 @@ mod tests { l1_base_fee_scalar: U256::from(1_000), ..Default::default() }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::REGOLITH)); ctx.modify_tx(|tx| { tx.deposit.source_hash = B256::from([1u8; 32]); tx.deposit.mint = Some(10); @@ -708,7 +652,7 @@ mod tests { l2_block: Some(U256::from(0)), ..Default::default() }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH) + .with_cfg(CfgEnv::new_with_spec(OpSpecId::REGOLITH)) .with_tx( OpTransaction::builder() .base(TxEnv::builder().gas_limit(100)) @@ -781,7 +725,7 @@ mod tests { number: BLOCK_NUM, ..Default::default() }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::ISTHMUS); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::ISTHMUS)); let mut evm = ctx.build_op(); @@ -824,15 +768,14 @@ mod tests { 0, 0, ]); - const OPERATOR_FEE_SCALAR: u64 = 5; - const OPERATOR_FEE_CONST: u64 = 6; - const OPERATOR_FEE: U256 = - U256::from_limbs([OPERATOR_FEE_CONST, OPERATOR_FEE_SCALAR, 0, 0]); - const DA_FOOTPRINT_GAS_SCALAR: u16 = 7; - const DA_FOOTPRINT_GAS_SCALAR_U64: u64 = - u64::from_be_bytes([0, DA_FOOTPRINT_GAS_SCALAR as u8, 0, 0, 0, 0, 0, 0]); - const DA_FOOTPRINT_GAS_SCALAR_SLOT_VALUE: U256 = - U256::from_limbs([0, 0, 0, DA_FOOTPRINT_GAS_SCALAR_U64]); + const OPERATOR_FEE_SCALAR: u8 = 5; + const OPERATOR_FEE_CONST: u8 = 6; + const DA_FOOTPRINT_GAS_SCALAR: u8 = 7; + let mut operator_fee_and_da_footprint = [0u8; 32]; + operator_fee_and_da_footprint[31] = OPERATOR_FEE_CONST; + operator_fee_and_da_footprint[23] = OPERATOR_FEE_SCALAR; + operator_fee_and_da_footprint[19] = DA_FOOTPRINT_GAS_SCALAR; + let operator_fee_and_da_footprint_u256 = U256::from_be_bytes(operator_fee_and_da_footprint); let mut db = InMemoryDB::default(); let l1_block_contract = db.load_account(L1_BLOCK_CONTRACT).unwrap(); @@ -845,12 +788,9 @@ mod tests { l1_block_contract .storage .insert(ECOTONE_L1_FEE_SCALARS_SLOT, L1_FEE_SCALARS); - l1_block_contract - .storage - .insert(OPERATOR_FEE_SCALARS_SLOT, OPERATOR_FEE); l1_block_contract.storage.insert( - DA_FOOTPRINT_GAS_SCALAR_SLOT, - DA_FOOTPRINT_GAS_SCALAR_SLOT_VALUE, + OPERATOR_FEE_SCALARS_SLOT, + operator_fee_and_da_footprint_u256, ); db.insert_account_info( Address::ZERO, @@ -872,7 +812,7 @@ mod tests { number: BLOCK_NUM, ..Default::default() }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::JOVIAN) + .with_cfg(CfgEnv::new_with_spec(OpSpecId::JOVIAN)) // set the operator fee to a low value .with_tx( OpTransaction::builder() @@ -904,7 +844,7 @@ mod tests { operator_fee_scalar: Some(U256::from(OPERATOR_FEE_SCALAR)), operator_fee_constant: Some(U256::from(OPERATOR_FEE_CONST)), tx_l1_cost: Some(U256::ZERO), - da_footprint_gas_scalar: Some(DA_FOOTPRINT_GAS_SCALAR), + da_footprint_gas_scalar: Some(DA_FOOTPRINT_GAS_SCALAR as u16), } ); } @@ -940,7 +880,7 @@ mod tests { number: BLOCK_NUM, ..Default::default() }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::REGOLITH)); let mut evm = ctx.build_op(); assert_ne!(evm.ctx().chain().l2_block, Some(BLOCK_NUM)); @@ -1000,7 +940,7 @@ mod tests { number: BLOCK_NUM, ..Default::default() }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::ECOTONE); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::ECOTONE)); let mut evm = ctx.build_op(); assert_ne!(evm.ctx().chain().l2_block, Some(BLOCK_NUM)); @@ -1073,7 +1013,7 @@ mod tests { number: BLOCK_NUM, ..Default::default() }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::ISTHMUS); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::ISTHMUS)); let mut evm = ctx.build_op(); @@ -1123,7 +1063,7 @@ mod tests { l2_block: Some(U256::from(0)), ..Default::default() }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH) + .with_cfg(CfgEnv::new_with_spec(OpSpecId::REGOLITH)) .with_tx( OpTransaction::builder() .base(TxEnv::builder().gas_limit(100)) @@ -1166,7 +1106,7 @@ mod tests { l2_block: Some(U256::from(0)), ..Default::default() }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::ISTHMUS) + .with_cfg(CfgEnv::new_with_spec(OpSpecId::ISTHMUS)) .with_tx( OpTransaction::builder() .base(TxEnv::builder().gas_limit(10)) @@ -1208,7 +1148,7 @@ mod tests { l2_block: Some(U256::from(0)), ..Default::default() }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::JOVIAN) + .with_cfg(CfgEnv::new_with_spec(OpSpecId::JOVIAN)) .with_tx( OpTransaction::builder() .base(TxEnv::builder().gas_limit(10)) @@ -1250,7 +1190,7 @@ mod tests { l2_block: Some(U256::from(0)), ..Default::default() }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH) + .with_cfg(CfgEnv::new_with_spec(OpSpecId::REGOLITH)) .modify_tx_chained(|tx| { tx.enveloped_tx = Some(bytes!("FACADE")); }); @@ -1281,7 +1221,7 @@ mod tests { tx.deposit.source_hash = B256::from([1u8; 32]); tx.deposit.is_system_transaction = true; }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::REGOLITH)); let mut evm = ctx.build_op(); let handler = @@ -1294,7 +1234,11 @@ mod tests { )) ); - evm.ctx().modify_cfg(|cfg| cfg.spec = OpSpecId::BEDROCK); + // With BEDROCK spec. + let ctx = evm.into_context(); + let mut evm = ctx + .with_cfg(CfgEnv::new_with_spec(OpSpecId::BEDROCK)) + .build_op(); // Pre-regolith system transactions should be allowed. assert!(handler.validate_env(&mut evm).is_ok()); @@ -1307,7 +1251,7 @@ mod tests { .modify_tx_chained(|tx| { tx.deposit.source_hash = B256::from([1u8; 32]); }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::REGOLITH)); let mut evm = ctx.build_op(); let handler = @@ -1323,7 +1267,7 @@ mod tests { .modify_tx_chained(|tx| { tx.deposit.source_hash = B256::from([1u8; 32]); }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::REGOLITH)); let mut evm = ctx.build_op(); let handler = @@ -1340,7 +1284,7 @@ mod tests { // Set up as deposit transaction by having a deposit with source_hash tx.deposit.source_hash = B256::from([1u8; 32]); }) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::REGOLITH)); let mut evm = ctx.build_op(); let mut handler = @@ -1349,14 +1293,14 @@ mod tests { assert_eq!( handler.execution_result( &mut evm, - FrameResult::Call(CallOutcome { - result: InterpreterResult { + FrameResult::Call(CallOutcome::new( + InterpreterResult { result: InstructionResult::OutOfGas, output: Default::default(), gas: Default::default(), }, - memory_offset: Default::default(), - }) + Default::default() + )) ), Err(EVMError::Transaction( OpTransactionError::HaltedDepositPostRegolith @@ -1423,7 +1367,7 @@ mod tests { }) .build_fill(), ) - .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::ISTHMUS); + .with_cfg(CfgEnv::new_with_spec(OpSpecId::ISTHMUS)); let mut evm = ctx.build_op(); let handler = @@ -1500,4 +1444,27 @@ mod tests { 0 ); } + + #[test] + fn test_validate_missing_enveloped_tx() { + use crate::transaction::deposit::DepositTransactionParts; + + // Create a non-deposit transaction without enveloped_tx + let ctx = Context::op().with_tx(OpTransaction { + base: TxEnv::builder().build_fill(), + enveloped_tx: None, // Missing enveloped_tx for non-deposit transaction + deposit: DepositTransactionParts::default(), // No source_hash means non-deposit + }); + + let mut evm = ctx.build_op(); + let handler = + OpHandler::<_, EVMError<_, OpTransactionError>, EthFrame>::new(); + + assert_eq!( + handler.validate_env(&mut evm), + Err(EVMError::Transaction( + OpTransactionError::MissingEnvelopedTx + )) + ); + } } diff --git a/crates/op-revm/src/l1block.rs b/crates/op-revm/src/l1block.rs index fd92a32c46..eff2ab0a53 100644 --- a/crates/op-revm/src/l1block.rs +++ b/crates/op-revm/src/l1block.rs @@ -7,16 +7,14 @@ use crate::{ NON_ZERO_BYTE_COST, OPERATOR_FEE_CONSTANT_OFFSET, OPERATOR_FEE_JOVIAN_MULTIPLIER, OPERATOR_FEE_SCALARS_SLOT, OPERATOR_FEE_SCALAR_DECIMAL, OPERATOR_FEE_SCALAR_OFFSET, }, - transaction::estimate_tx_compressed_size, + transaction::{estimate_tx_compressed_size, OpTxTr}, OpSpecId, }; use revm::{ + context_interface::cfg::gas::{NON_ZERO_BYTE_MULTIPLIER_ISTANBUL, STANDARD_TOKEN_COST}, database_interface::Database, - interpreter::{ - gas::{get_tokens_in_calldata, NON_ZERO_BYTE_MULTIPLIER_ISTANBUL, STANDARD_TOKEN_COST}, - Gas, - }, - primitives::{hardfork::SpecId, U256}, + interpreter::{gas::get_tokens_in_calldata_istanbul, Gas}, + primitives::U256, }; /// L1 block info @@ -58,8 +56,8 @@ pub struct L1BlockInfo { } impl L1BlockInfo { - /// Try to fetch the L1 block info from the database, post-Jovian. - fn try_fetch_jovian(&mut self, db: &mut DB) -> Result<(), DB::Error> { + /// Fetch the DA footprint gas scalar from the database. + pub fn fetch_da_footprint_gas_scalar(db: &mut DB) -> Result { let da_footprint_gas_scalar_slot = db .storage(L1_BLOCK_CONTRACT, DA_FOOTPRINT_GAS_SCALAR_SLOT)? .to_be_bytes::<32>(); @@ -69,7 +67,12 @@ impl L1BlockInfo { da_footprint_gas_scalar_slot[DA_FOOTPRINT_GAS_SCALAR_OFFSET], da_footprint_gas_scalar_slot[DA_FOOTPRINT_GAS_SCALAR_OFFSET + 1], ]; - self.da_footprint_gas_scalar = Some(u16::from_be_bytes(bytes)); + Ok(u16::from_be_bytes(bytes)) + } + + /// Try to fetch the L1 block info from the database, post-Jovian. + fn try_fetch_jovian(&mut self, db: &mut DB) -> Result<(), DB::Error> { + self.da_footprint_gas_scalar = Some(Self::fetch_da_footprint_gas_scalar(db)?); Ok(()) } @@ -133,11 +136,8 @@ impl L1BlockInfo { l2_block: U256, spec_id: OpSpecId, ) -> Result { - // Ensure the L1 Block account is loaded into the cache after Ecotone. With EIP-4788, it is no longer the case - // that the L1 block account is loaded into the cache prior to the first inquiry for the L1 block info. - if spec_id.into_eth_spec().is_enabled_in(SpecId::CANCUN) { - let _ = db.basic(L1_BLOCK_CONTRACT)?; - } + // Ensure the L1 Block account is loaded into the cache. + let _ = db.basic(L1_BLOCK_CONTRACT)?; let mut out = L1BlockInfo { l2_block: Some(l2_block), @@ -234,7 +234,7 @@ impl L1BlockInfo { }; // tokens in calldata where non-zero bytes are priced 4 times higher than zero bytes (Same as in Istanbul). - let mut tokens_in_transaction_data = get_tokens_in_calldata(input, true); + let mut tokens_in_transaction_data = get_tokens_in_calldata_istanbul(input); // Prior to regolith, an extra 68 non zero bytes were included in the rollup data costs. if !spec_id.is_enabled_in(OpSpecId::REGOLITH) { @@ -256,6 +256,31 @@ impl L1BlockInfo { self.tx_l1_cost = None; } + /// Calculate additional transaction cost with OpTxTr. + /// + /// Internally calls [`L1BlockInfo::tx_cost`]. + pub fn tx_cost_with_tx(&mut self, tx: impl OpTxTr, spec: OpSpecId) -> Option { + // account for additional cost of l1 fee and operator fee + let enveloped_tx = tx.enveloped_tx()?; + let gas_limit = U256::from(tx.gas_limit()); + Some(self.tx_cost(enveloped_tx, gas_limit, spec)) + } + + /// Calculate additional transaction cost. + #[inline] + pub fn tx_cost(&mut self, enveloped_tx: &[u8], gas_limit: U256, spec: OpSpecId) -> U256 { + // compute L1 cost + let mut additional_cost = self.calculate_tx_l1_cost(enveloped_tx, spec); + + // compute operator fee + if spec.is_enabled_in(OpSpecId::ISTHMUS) { + let operator_fee_charge = self.operator_fee_charge(enveloped_tx, gas_limit, spec); + additional_cost = additional_cost.saturating_add(operator_fee_charge); + } + + additional_cost + } + /// Calculate the gas cost of a transaction based on L1 block data posted on L2, depending on the [OpSpecId] passed. pub fn calculate_tx_l1_cost(&mut self, input: &[u8], spec_id: OpSpecId) -> U256 { if let Some(tx_l1_cost) = self.tx_l1_cost { @@ -318,6 +343,10 @@ impl L1BlockInfo { /// `estimatedSize*(baseFeeScalar*l1BaseFee*16 + blobFeeScalar*l1BlobBaseFee)/1e12` fn calculate_tx_l1_cost_fjord(&self, input: &[u8]) -> U256 { let l1_fee_scaled = self.calculate_l1_fee_scaled_ecotone(); + if l1_fee_scaled.is_zero() { + return U256::ZERO; + } + let estimated_size = self.tx_estimated_size_fjord(input); estimated_size diff --git a/crates/op-revm/src/precompiles.rs b/crates/op-revm/src/precompiles.rs index f27bf339b5..14df489c57 100644 --- a/crates/op-revm/src/precompiles.rs +++ b/crates/op-revm/src/precompiles.rs @@ -11,8 +11,7 @@ use revm::{ }, primitives::{hardfork::SpecId, Address, OnceLock}, }; -use std::boxed::Box; -use std::string::String; +use std::{boxed::Box, string::String}; /// Optimism precompile provider #[derive(Debug, Clone)] @@ -34,7 +33,8 @@ impl OpPrecompiles { | OpSpecId::ECOTONE) => Precompiles::new(spec.into_eth_spec().into()), OpSpecId::FJORD => fjord(), OpSpecId::GRANITE | OpSpecId::HOLOCENE => granite(), - OpSpecId::ISTHMUS | OpSpecId::INTEROP | OpSpecId::OSAKA | OpSpecId::JOVIAN => isthmus(), + OpSpecId::ISTHMUS => isthmus(), + OpSpecId::INTEROP | OpSpecId::OSAKA | OpSpecId::JOVIAN => jovian(), }; Self { @@ -75,7 +75,7 @@ pub fn granite() -> &'static Precompiles { }) } -/// Returns precompiles for isthumus spec. +/// Returns precompiles for isthmus spec. pub fn isthmus() -> &'static Precompiles { static INSTANCE: OnceLock = OnceLock::new(); INSTANCE.get_or_init(|| { @@ -92,6 +92,34 @@ pub fn isthmus() -> &'static Precompiles { }) } +/// Returns precompiles for jovian spec. +pub fn jovian() -> &'static Precompiles { + static INSTANCE: OnceLock = OnceLock::new(); + INSTANCE.get_or_init(|| { + let mut precompiles = isthmus().clone(); + + let mut to_remove = Precompiles::default(); + to_remove.extend([ + bn254::pair::ISTANBUL, + bls12_381::ISTHMUS_G1_MSM, + bls12_381::ISTHMUS_G2_MSM, + bls12_381::ISTHMUS_PAIRING, + ]); + + // Replace the 4 variable-input precompiles with Jovian versions (reduced limits) + precompiles.difference(&to_remove); + + precompiles.extend([ + bn254_pair::JOVIAN, + bls12_381::JOVIAN_G1_MSM, + bls12_381::JOVIAN_G2_MSM, + bls12_381::JOVIAN_PAIRING, + ]); + + precompiles + }) +} + impl PrecompileProvider for OpPrecompiles where CTX: ContextTr>, @@ -129,7 +157,7 @@ where impl Default for OpPrecompiles { fn default() -> Self { - Self::new_with_spec(OpSpecId::ISTHMUS) + Self::new_with_spec(OpSpecId::JOVIAN) } } @@ -140,11 +168,14 @@ pub mod bn254_pair { /// Max input size for the bn254 pair precompile. pub const GRANITE_MAX_INPUT_SIZE: usize = 112687; /// Bn254 pair precompile. - pub const GRANITE: Precompile = - Precompile::new(PrecompileId::Bn254Pairing, bn254::pair::ADDRESS, run_pair); + pub const GRANITE: Precompile = Precompile::new( + PrecompileId::Bn254Pairing, + bn254::pair::ADDRESS, + run_pair_granite, + ); /// Run the bn254 pair precompile with Optimism input limit. - pub fn run_pair(input: &[u8], gas_limit: u64) -> PrecompileResult { + pub fn run_pair_granite(input: &[u8], gas_limit: u64) -> PrecompileResult { if input.len() > GRANITE_MAX_INPUT_SIZE { return Err(PrecompileError::Bn254PairLength); } @@ -155,6 +186,28 @@ pub mod bn254_pair { gas_limit, ) } + + /// Max input size for the bn254 pair precompile. + pub const JOVIAN_MAX_INPUT_SIZE: usize = 81_984; + /// Bn254 pair precompile. + pub const JOVIAN: Precompile = Precompile::new( + PrecompileId::Bn254Pairing, + bn254::pair::ADDRESS, + run_pair_jovian, + ); + + /// Run the bn254 pair precompile with Optimism input limit. + pub fn run_pair_jovian(input: &[u8], gas_limit: u64) -> PrecompileResult { + if input.len() > JOVIAN_MAX_INPUT_SIZE { + return Err(PrecompileError::Bn254PairLength); + } + bn254::run_pair( + input, + bn254::pair::ISTANBUL_PAIR_PER_POINT, + bn254::pair::ISTANBUL_PAIR_BASE, + gas_limit, + ) + } } /// Bls12_381 precompile. @@ -162,51 +215,102 @@ pub mod bls12_381 { use super::*; use revm::precompile::bls12_381_const::{G1_MSM_ADDRESS, G2_MSM_ADDRESS, PAIRING_ADDRESS}; - #[cfg(not(feature = "std"))] - use crate::std::string::ToString; - /// Max input size for the g1 msm precompile. pub const ISTHMUS_G1_MSM_MAX_INPUT_SIZE: usize = 513760; + + /// The maximum input size for the BLS12-381 g1 msm operation after the Jovian Hardfork. + pub const JOVIAN_G1_MSM_MAX_INPUT_SIZE: usize = 288_960; + /// Max input size for the g2 msm precompile. pub const ISTHMUS_G2_MSM_MAX_INPUT_SIZE: usize = 488448; + + /// Max input size for the g2 msm precompile after the Jovian Hardfork. + pub const JOVIAN_G2_MSM_MAX_INPUT_SIZE: usize = 278_784; + /// Max input size for the pairing precompile. pub const ISTHMUS_PAIRING_MAX_INPUT_SIZE: usize = 235008; + /// Max input size for the pairing precompile after the Jovian Hardfork. + pub const JOVIAN_PAIRING_MAX_INPUT_SIZE: usize = 156_672; + /// G1 msm precompile. pub const ISTHMUS_G1_MSM: Precompile = - Precompile::new(PrecompileId::Bls12G1Msm, G1_MSM_ADDRESS, run_g1_msm); + Precompile::new(PrecompileId::Bls12G1Msm, G1_MSM_ADDRESS, run_g1_msm_isthmus); /// G2 msm precompile. pub const ISTHMUS_G2_MSM: Precompile = - Precompile::new(PrecompileId::Bls12G2Msm, G2_MSM_ADDRESS, run_g2_msm); + Precompile::new(PrecompileId::Bls12G2Msm, G2_MSM_ADDRESS, run_g2_msm_isthmus); /// Pairing precompile. - pub const ISTHMUS_PAIRING: Precompile = - Precompile::new(PrecompileId::Bls12Pairing, PAIRING_ADDRESS, run_pair); + pub const ISTHMUS_PAIRING: Precompile = Precompile::new( + PrecompileId::Bls12Pairing, + PAIRING_ADDRESS, + run_pair_isthmus, + ); + + /// G1 msm precompile after the Jovian Hardfork. + pub const JOVIAN_G1_MSM: Precompile = + Precompile::new(PrecompileId::Bls12G1Msm, G1_MSM_ADDRESS, run_g1_msm_jovian); + /// G2 msm precompile after the Jovian Hardfork. + pub const JOVIAN_G2_MSM: Precompile = + Precompile::new(PrecompileId::Bls12G2Msm, G2_MSM_ADDRESS, run_g2_msm_jovian); + /// Pairing precompile after the Jovian Hardfork. + pub const JOVIAN_PAIRING: Precompile = + Precompile::new(PrecompileId::Bls12Pairing, PAIRING_ADDRESS, run_pair_jovian); /// Run the g1 msm precompile with Optimism input limit. - pub fn run_g1_msm(input: &[u8], gas_limit: u64) -> PrecompileResult { + pub fn run_g1_msm_isthmus(input: &[u8], gas_limit: u64) -> PrecompileResult { if input.len() > ISTHMUS_G1_MSM_MAX_INPUT_SIZE { return Err(PrecompileError::Other( - "G1MSM input length too long for OP Stack input size limitation".to_string(), + "G1MSM input length too long for OP Stack input size limitation after the Isthmus Hardfork".into(), + )); + } + precompile::bls12_381::g1_msm::g1_msm(input, gas_limit) + } + + /// Run the g1 msm precompile with Optimism input limit. + pub fn run_g1_msm_jovian(input: &[u8], gas_limit: u64) -> PrecompileResult { + if input.len() > JOVIAN_G1_MSM_MAX_INPUT_SIZE { + return Err(PrecompileError::Other( + "G1MSM input length too long for OP Stack input size limitation after the Jovian Hardfork".into(), )); } precompile::bls12_381::g1_msm::g1_msm(input, gas_limit) } /// Run the g2 msm precompile with Optimism input limit. - pub fn run_g2_msm(input: &[u8], gas_limit: u64) -> PrecompileResult { + pub fn run_g2_msm_isthmus(input: &[u8], gas_limit: u64) -> PrecompileResult { if input.len() > ISTHMUS_G2_MSM_MAX_INPUT_SIZE { return Err(PrecompileError::Other( - "G2MSM input length too long for OP Stack input size limitation".to_string(), + "G2MSM input length too long for OP Stack input size limitation".into(), + )); + } + precompile::bls12_381::g2_msm::g2_msm(input, gas_limit) + } + + /// Run the g2 msm precompile with Optimism input limit after the Jovian Hardfork. + pub fn run_g2_msm_jovian(input: &[u8], gas_limit: u64) -> PrecompileResult { + if input.len() > JOVIAN_G2_MSM_MAX_INPUT_SIZE { + return Err(PrecompileError::Other( + "G2MSM input length too long for OP Stack input size limitation after the Jovian Hardfork".into(), )); } precompile::bls12_381::g2_msm::g2_msm(input, gas_limit) } /// Run the pairing precompile with Optimism input limit. - pub fn run_pair(input: &[u8], gas_limit: u64) -> PrecompileResult { + pub fn run_pair_isthmus(input: &[u8], gas_limit: u64) -> PrecompileResult { if input.len() > ISTHMUS_PAIRING_MAX_INPUT_SIZE { return Err(PrecompileError::Other( - "Pairing input length too long for OP Stack input size limitation".to_string(), + "Pairing input length too long for OP Stack input size limitation".into(), + )); + } + precompile::bls12_381::pairing::pairing(input, gas_limit) + } + + /// Run the pairing precompile with Optimism input limit after the Jovian Hardfork. + pub fn run_pair_jovian(input: &[u8], gas_limit: u64) -> PrecompileResult { + if input.len() > JOVIAN_PAIRING_MAX_INPUT_SIZE { + return Err(PrecompileError::Other( + "Pairing input length too long for OP Stack input size limitation after the Jovian Hardfork".into(), )); } precompile::bls12_381::pairing::pairing(input, gas_limit) @@ -216,13 +320,15 @@ pub mod bls12_381 { #[cfg(test)] mod tests { use crate::precompiles::bls12_381::{ - run_g1_msm, run_g2_msm, ISTHMUS_G1_MSM_MAX_INPUT_SIZE, ISTHMUS_G2_MSM_MAX_INPUT_SIZE, - ISTHMUS_PAIRING_MAX_INPUT_SIZE, + run_g1_msm_isthmus, run_g1_msm_jovian, run_g2_msm_isthmus, run_g2_msm_jovian, + ISTHMUS_G1_MSM_MAX_INPUT_SIZE, ISTHMUS_G2_MSM_MAX_INPUT_SIZE, + ISTHMUS_PAIRING_MAX_INPUT_SIZE, JOVIAN_G1_MSM_MAX_INPUT_SIZE, JOVIAN_G2_MSM_MAX_INPUT_SIZE, + JOVIAN_PAIRING_MAX_INPUT_SIZE, }; use super::*; use revm::{ - precompile::PrecompileError, + precompile::{bls12_381_const, PrecompileError}, primitives::{hex, Bytes}, }; use std::vec; @@ -248,7 +354,7 @@ mod tests { let expected = hex::decode("0000000000000000000000000000000000000000000000000000000000000001") .unwrap(); - let outcome = bn254_pair::run_pair(&input, 260_000).unwrap(); + let outcome = bn254_pair::run_pair_granite(&input, 260_000).unwrap(); assert_eq!(outcome.bytes, expected); // Invalid input length @@ -261,20 +367,91 @@ mod tests { ) .unwrap(); - let res = bn254_pair::run_pair(&input, 260_000); + let res = bn254_pair::run_pair_granite(&input, 260_000); assert!(matches!(res, Err(PrecompileError::Bn254PairLength))); // Valid input length shorter than 112687 let input = vec![1u8; 586 * bn254::PAIR_ELEMENT_LEN]; - let res = bn254_pair::run_pair(&input, 260_000); + let res = bn254_pair::run_pair_granite(&input, 260_000); assert!(matches!(res, Err(PrecompileError::OutOfGas))); // Input length longer than 112687 let input = vec![1u8; 587 * bn254::PAIR_ELEMENT_LEN]; - let res = bn254_pair::run_pair(&input, 260_000); + let res = bn254_pair::run_pair_granite(&input, 260_000); + assert!(matches!(res, Err(PrecompileError::Bn254PairLength))); + } + + #[test] + fn test_accelerated_bn254_pairing_jovian() { + const TEST_INPUT: [u8; 384] = hex!( + "2cf44499d5d27bb186308b7af7af02ac5bc9eeb6a3d147c186b21fb1b76e18da2c0f001f52110ccfe69108924926e45f0b0c868df0e7bde1fe16d3242dc715f61fb19bb476f6b9e44e2a32234da8212f61cd63919354bc06aef31e3cfaff3ebc22606845ff186793914e03e21df544c34ffe2f2f3504de8a79d9159eca2d98d92bd368e28381e8eccb5fa81fc26cf3f048eea9abfdd85d7ed3ab3698d63e4f902fe02e47887507adf0ff1743cbac6ba291e66f59be6bd763950bb16041a0a85e000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd451971ff0471b09fa93caaf13cbf443c1aede09cc4328f5a62aad45f40ec133eb4091058a3141822985733cbdddfed0fd8d6c104e9e9eff40bf5abfef9ab163bc72a23af9a5ce2ba2796c1f4e453a370eb0af8c212d9dc9acd8fc02c2e907baea223a8eb0b0996252cb548a4487da97b02422ebc0e834613f954de6c7e0afdc1fc" + ); + const EXPECTED_OUTPUT: [u8; 32] = + hex!("0000000000000000000000000000000000000000000000000000000000000001"); + + let res = bn254_pair::run_pair_jovian(TEST_INPUT.as_ref(), u64::MAX); + assert!(matches!(res, Ok(outcome) if **outcome.bytes == EXPECTED_OUTPUT)); + } + + #[test] + fn test_accelerated_bn254_pairing_bad_input_len_jovian() { + let input = [0u8; bn254_pair::JOVIAN_MAX_INPUT_SIZE + 1]; + let res = bn254_pair::run_pair_jovian(&input, u64::MAX); assert!(matches!(res, Err(PrecompileError::Bn254PairLength))); } + #[test] + fn test_get_jovian_precompile_with_bad_input_len() { + let precompiles = OpPrecompiles::new_with_spec(OpSpecId::JOVIAN); + let bn254_pair_precompile = precompiles + .precompiles() + .get(&bn254::pair::ADDRESS) + .unwrap(); + + let mut bad_input_len = bn254_pair::JOVIAN_MAX_INPUT_SIZE + 1; + assert!(bad_input_len < bn254_pair::GRANITE_MAX_INPUT_SIZE); + let input = vec![0u8; bad_input_len]; + + let res = bn254_pair_precompile.execute(&input, u64::MAX); + assert!(matches!(res, Err(PrecompileError::Bn254PairLength))); + + let bls12_381_g1_msm_precompile = precompiles + .precompiles() + .get(&bls12_381_const::G1_MSM_ADDRESS) + .unwrap(); + bad_input_len = bls12_381::JOVIAN_G1_MSM_MAX_INPUT_SIZE + 1; + assert!(bad_input_len < bls12_381::ISTHMUS_G1_MSM_MAX_INPUT_SIZE); + let input = vec![0u8; bad_input_len]; + let res = bls12_381_g1_msm_precompile.execute(&input, u64::MAX); + assert!( + matches!(res, Err(PrecompileError::Other(msg)) if msg.contains("input length too long")) + ); + + let bls12_381_g2_msm_precompile = precompiles + .precompiles() + .get(&bls12_381_const::G2_MSM_ADDRESS) + .unwrap(); + bad_input_len = bls12_381::JOVIAN_G2_MSM_MAX_INPUT_SIZE + 1; + assert!(bad_input_len < bls12_381::ISTHMUS_G2_MSM_MAX_INPUT_SIZE); + let input = vec![0u8; bad_input_len]; + let res = bls12_381_g2_msm_precompile.execute(&input, u64::MAX); + assert!( + matches!(res, Err(PrecompileError::Other(msg)) if msg.contains("input length too long")) + ); + + let bls12_381_pairing_precompile = precompiles + .precompiles() + .get(&bls12_381_const::PAIRING_ADDRESS) + .unwrap(); + bad_input_len = bls12_381::JOVIAN_PAIRING_MAX_INPUT_SIZE + 1; + assert!(bad_input_len < bls12_381::ISTHMUS_PAIRING_MAX_INPUT_SIZE); + let input = vec![0u8; bad_input_len]; + let res = bls12_381_pairing_precompile.execute(&input, u64::MAX); + assert!( + matches!(res, Err(PrecompileError::Other(msg)) if msg.contains("input length too long")) + ); + } + #[test] fn test_cancun_precompiles_in_fjord() { // additional to cancun, fjord has p256verify @@ -296,6 +473,23 @@ mod tests { assert!(new_prague_precompiles.difference(isthmus()).is_empty()) } + #[test] + fn test_prague_precompiles_in_jovian() { + let new_prague_precompiles = Precompiles::prague().difference(Precompiles::cancun()); + + // jovian contains all precompiles that were new in prague, without modifications + assert!(new_prague_precompiles.difference(jovian()).is_empty()) + } + + /// All the addresses of the precompiles in isthmus should be in jovian + #[test] + fn test_isthmus_precompiles_in_jovian() { + let new_isthmus_precompiles = isthmus().difference(Precompiles::cancun()); + + // jovian contains all precompiles that were new in isthmus, without modifications + assert!(new_isthmus_precompiles.difference(jovian()).is_empty()) + } + #[test] fn test_default_precompiles_is_latest() { let latest = OpPrecompiles::new_with_spec(OpSpecId::default()) @@ -313,7 +507,19 @@ mod tests { let oversized_input = vec![0u8; ISTHMUS_G1_MSM_MAX_INPUT_SIZE + 1]; let input = Bytes::from(oversized_input); - let res = run_g1_msm(&input, 260_000); + let res = run_g1_msm_isthmus(&input, 260_000); + + assert!( + matches!(res, Err(PrecompileError::Other(msg)) if msg.contains("input length too long")) + ); + } + + #[test] + fn test_g1_jovian_max_size() { + let oversized_input = vec![0u8; JOVIAN_G1_MSM_MAX_INPUT_SIZE + 1]; + let input = Bytes::from(oversized_input); + + let res = run_g1_msm_jovian(&input, u64::MAX); assert!( matches!(res, Err(PrecompileError::Other(msg)) if msg.contains("input length too long")) @@ -324,7 +530,18 @@ mod tests { let oversized_input = vec![0u8; ISTHMUS_G2_MSM_MAX_INPUT_SIZE + 1]; let input = Bytes::from(oversized_input); - let res = run_g2_msm(&input, 260_000); + let res = run_g2_msm_isthmus(&input, 260_000); + + assert!( + matches!(res, Err(PrecompileError::Other(msg)) if msg.contains("input length too long")) + ); + } + #[test] + fn test_g2_jovian_max_size() { + let oversized_input = vec![0u8; JOVIAN_G2_MSM_MAX_INPUT_SIZE + 1]; + let input = Bytes::from(oversized_input); + + let res = run_g2_msm_jovian(&input, u64::MAX); assert!( matches!(res, Err(PrecompileError::Other(msg)) if msg.contains("input length too long")) @@ -335,7 +552,18 @@ mod tests { let oversized_input = vec![0u8; ISTHMUS_PAIRING_MAX_INPUT_SIZE + 1]; let input = Bytes::from(oversized_input); - let res = bls12_381::run_pair(&input, 260_000); + let res = bls12_381::run_pair_isthmus(&input, 260_000); + + assert!( + matches!(res, Err(PrecompileError::Other(msg)) if msg.contains("input length too long")) + ); + } + #[test] + fn test_pair_jovian_max_size() { + let oversized_input = vec![0u8; JOVIAN_PAIRING_MAX_INPUT_SIZE + 1]; + let input = Bytes::from(oversized_input); + + let res = bls12_381::run_pair_jovian(&input, u64::MAX); assert!( matches!(res, Err(PrecompileError::Other(msg)) if msg.contains("input length too long")) diff --git a/crates/op-revm/src/spec.rs b/crates/op-revm/src/spec.rs index 35b15c3c9a..e192847a0f 100644 --- a/crates/op-revm/src/spec.rs +++ b/crates/op-revm/src/spec.rs @@ -40,8 +40,8 @@ impl OpSpecId { Self::BEDROCK | Self::REGOLITH => SpecId::MERGE, Self::CANYON => SpecId::SHANGHAI, Self::ECOTONE | Self::FJORD | Self::GRANITE | Self::HOLOCENE => SpecId::CANCUN, - Self::ISTHMUS | Self::INTEROP => SpecId::PRAGUE, - Self::JOVIAN | Self::OSAKA => SpecId::OSAKA, + Self::ISTHMUS | Self::JOVIAN | Self::INTEROP => SpecId::PRAGUE, + Self::OSAKA => SpecId::OSAKA, } } @@ -201,7 +201,6 @@ mod tests { (SpecId::SHANGHAI, true), (SpecId::CANCUN, true), (SpecId::MERGE, true), - (SpecId::OSAKA, true), ], vec![ (OpSpecId::BEDROCK, true), diff --git a/crates/op-revm/src/transaction/error.rs b/crates/op-revm/src/transaction/error.rs index ba13cab90e..ad28175ee2 100644 --- a/crates/op-revm/src/transaction/error.rs +++ b/crates/op-revm/src/transaction/error.rs @@ -41,6 +41,11 @@ pub enum OpTransactionError { /// are cause for non-inclusion, so a special [OpHaltReason][crate::OpHaltReason] variant was introduced to handle this /// case for failed deposit transactions. HaltedDepositPostRegolith, + /// Missing enveloped transaction bytes for non-deposit transaction. + /// + /// Non-deposit transactions on Optimism must have `enveloped_tx` field set + /// to properly calculate L1 costs. + MissingEnvelopedTx, } impl TransactionError for OpTransactionError {} @@ -61,6 +66,12 @@ impl Display for OpTransactionError { "deposit transaction halted post-regolith; error will be bubbled up to main return handler" ) } + Self::MissingEnvelopedTx => { + write!( + f, + "missing enveloped transaction bytes for non-deposit transaction" + ) + } } } } @@ -98,7 +109,11 @@ mod test { assert_eq!( OpTransactionError::HaltedDepositPostRegolith.to_string(), "deposit transaction halted post-regolith; error will be bubbled up to main return handler" - ) + ); + assert_eq!( + OpTransactionError::MissingEnvelopedTx.to_string(), + "missing enveloped transaction bytes for non-deposit transaction" + ); } #[cfg(feature = "serde")] diff --git a/crates/precompile/CHANGELOG.md b/crates/precompile/CHANGELOG.md index fc0a5a5a9d..e1f96e977c 100644 --- a/crates/precompile/CHANGELOG.md +++ b/crates/precompile/CHANGELOG.md @@ -7,6 +7,47 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [32.0.0](https://github.com/bluealloy/revm/compare/revm-precompile-v31.0.0...revm-precompile-v32.0.0) - 2026-01-15 + +### Added + +- rm gmp use-system-libs ([#3253](https://github.com/bluealloy/revm/pull/3253)) +- dynamically link gmp ([#3250](https://github.com/bluealloy/revm/pull/3250)) + +### Other + +- *(precompile)* extract common pairing_check_bytes logic in bls12_381 ([#3301](https://github.com/bluealloy/revm/pull/3301)) +- apply improvements from ai-bot labeled PRs ([#3297](https://github.com/bluealloy/revm/pull/3297)) +- fix typos, grammar errors, and improve documentation consistency ([#3294](https://github.com/bluealloy/revm/pull/3294)) +- happy new year, 2026 licence ([#3272](https://github.com/bluealloy/revm/pull/3272)) +- optimize vector initialization using size hints ([#3200](https://github.com/bluealloy/revm/pull/3200)) +- optimize precompile `extend()` ([#3192](https://github.com/bluealloy/revm/pull/3192)) +- optimize vector initialization with size hints in state and precompile modules ([#3191](https://github.com/bluealloy/revm/pull/3191)) +- *(fmt)* merge all imports ([#3184](https://github.com/bluealloy/revm/pull/3184)) + +## [31.0.0](https://github.com/bluealloy/revm/compare/revm-precompile-v29.0.1...revm-precompile-v31.0.0) - 2025-11-10 + +### Added + +- *(precompiles)* add performant PrecompileError::OtherCowStr variant ([#3144](https://github.com/bluealloy/revm/pull/3144)) +- add gas refund to PrecompileOutput ([#3152](https://github.com/bluealloy/revm/pull/3152)) + +### Other + +- merge v98 versions bumps ([#3155](https://github.com/bluealloy/revm/pull/3155)) + +## [29.0.1](https://github.com/bluealloy/revm/compare/revm-precompile-v29.0.0...revm-precompile-v29.0.1) - 2025-11-07 + +### Other + +- updated the following local packages: revm-primitives + +## [29.0.0](https://github.com/bluealloy/revm/compare/revm-precompile-v28.1.1...revm-precompile-v29.0.0) - 2025-10-30 + +### Other + +- *(precompile)* remove unused mainnet_address() function ([#3091](https://github.com/bluealloy/revm/pull/3091)) + ## [28.1.1](https://github.com/bluealloy/revm/compare/revm-precompile-v28.1.0...revm-precompile-v28.1.1) - 2025-10-15 ### Other diff --git a/crates/precompile/Cargo.toml b/crates/precompile/Cargo.toml index 4a80c16d98..cdafcfa1e8 100644 --- a/crates/precompile/Cargo.toml +++ b/crates/precompile/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "revm-precompile" description = "Revm Precompiles - Ethereum compatible precompiled contracts" -version = "28.1.1" +version = "32.0.0" authors.workspace = true edition.workspace = true keywords.workspace = true @@ -23,8 +23,8 @@ primitives.workspace = true # modexp precompiles aurora-engine-modexp.workspace = true -# gmp wrapper -rug = { workspace = true, features = ["integer"], optional = true } +# gmp ffi +gmp-mpfr-sys = { workspace = true, default-features = false, optional = true } # ecRecover k256 = { workspace = true, features = ["ecdsa"] } @@ -75,6 +75,7 @@ rstest.workspace = true [features] default = ["std", "secp256k1", "blst", "c-kzg", "portable"] + std = [ "primitives/std", "k256/std", @@ -90,7 +91,6 @@ std = [ "ark-serialize/std", "ark-std/std", "p256/std", - "rug?/std", ] hashbrown = ["primitives/hashbrown"] asm-keccak = ["primitives/asm-keccak"] @@ -115,9 +115,10 @@ blst = ["dep:blst"] # Enables the substrate implementation of eip1962 bn = ["dep:bn"] -# Use rug (that wraps gmp) for modexp precompile. -# It is faster library but licences as GPL code, if enabled please make sure to follow the license. -gmp = ["dep:rug"] +# Use GMP (LGPL) for modexp precompile via gmp-mpfr-sys. +# If you want to use a system libs and dynamic linking of GMP, include `gmp-mpfr-sys` in your project +# with a `features = ["use-system-libs"]` enabled. +gmp = ["dep:gmp-mpfr-sys"] [[bench]] name = "bench" diff --git a/crates/precompile/LICENSE b/crates/precompile/LICENSE index be6d350ebe..7edb0405ff 100644 --- a/crates/precompile/LICENSE +++ b/crates/precompile/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021-2025 draganrakita +Copyright (c) 2021-2026 draganrakita Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/crates/precompile/bench/eip1962.rs b/crates/precompile/bench/eip1962.rs index afcc0b77f4..1e3371fc8f 100644 --- a/crates/precompile/bench/eip1962.rs +++ b/crates/precompile/bench/eip1962.rs @@ -1,7 +1,6 @@ //! Benchmarks for the BN254 precompiles use criterion::{measurement::Measurement, BenchmarkGroup}; -use primitives::hex; -use primitives::Bytes; +use primitives::{hex, Bytes}; use revm_precompile::bn254::{ add::ISTANBUL_ADD_GAS_COST, mul::ISTANBUL_MUL_GAS_COST, diff --git a/crates/precompile/src/bls12_381.rs b/crates/precompile/src/bls12_381.rs index 0c6ff83b9e..5b7474f9ee 100644 --- a/crates/precompile/src/bls12_381.rs +++ b/crates/precompile/src/bls12_381.rs @@ -38,6 +38,7 @@ pub mod g2_msm; pub mod map_fp2_to_g2; pub mod map_fp_to_g1; pub mod pairing; +mod pairing_common; mod utils; /// Returns the BLS12-381 precompiles with their addresses. diff --git a/crates/precompile/src/bls12_381/arkworks.rs b/crates/precompile/src/bls12_381/arkworks.rs index dea815f156..b6353f7263 100644 --- a/crates/precompile/src/bls12_381/arkworks.rs +++ b/crates/precompile/src/bls12_381/arkworks.rs @@ -179,7 +179,7 @@ fn read_g2( ) -> Result { let point = read_g2_no_subgroup_check(a_x_0, a_x_1, a_y_0, a_y_1)?; if !point.is_in_correct_subgroup_assuming_on_curve() { - return Err(PrecompileError::Bls12381G1NotInSubgroup); + return Err(PrecompileError::Bls12381G2NotInSubgroup); } Ok(point) } @@ -350,44 +350,7 @@ pub(crate) fn pairing_check(pairs: &[(G1Affine, G2Affine)]) -> bool { /// pairing_check_bytes performs a pairing check on a list of G1 and G2 point pairs taking byte inputs. #[inline] pub(crate) fn pairing_check_bytes(pairs: &[PairingPair]) -> Result { - if pairs.is_empty() { - return Ok(true); - } - - let mut parsed_pairs = Vec::with_capacity(pairs.len()); - for ((g1_x, g1_y), (g2_x_0, g2_x_1, g2_y_0, g2_y_1)) in pairs { - // Check if G1 point is zero (point at infinity) - let g1_is_zero = g1_x.iter().all(|&b| b == 0) && g1_y.iter().all(|&b| b == 0); - - // Check if G2 point is zero (point at infinity) - let g2_is_zero = g2_x_0.iter().all(|&b| b == 0) - && g2_x_1.iter().all(|&b| b == 0) - && g2_y_0.iter().all(|&b| b == 0) - && g2_y_1.iter().all(|&b| b == 0); - - // Skip this pair if either point is at infinity as it's a no-op - if g1_is_zero || g2_is_zero { - // Still need to validate the non-zero point if one exists - if !g1_is_zero { - let _ = read_g1(g1_x, g1_y)?; - } - if !g2_is_zero { - let _ = read_g2(g2_x_0, g2_x_1, g2_y_0, g2_y_1)?; - } - continue; - } - - let g1_point = read_g1(g1_x, g1_y)?; - let g2_point = read_g2(g2_x_0, g2_x_1, g2_y_0, g2_y_1)?; - parsed_pairs.push((g1_point, g2_point)); - } - - // If all pairs were filtered out, return true (identity element) - if parsed_pairs.is_empty() { - return Ok(true); - } - - Ok(pairing_check(&parsed_pairs)) + super::pairing_common::pairing_check_bytes_generic(pairs, read_g1, read_g2, pairing_check) } // Byte-oriented versions of the functions for external API compatibility @@ -460,8 +423,9 @@ pub(crate) fn map_fp2_to_g2_bytes( pub(crate) fn p1_msm_bytes( point_scalar_pairs: impl Iterator>, ) -> Result<[u8; G1_LENGTH], PrecompileError> { - let mut g1_points = Vec::new(); - let mut scalars = Vec::new(); + let (lower, _) = point_scalar_pairs.size_hint(); + let mut g1_points = Vec::with_capacity(lower); + let mut scalars = Vec::with_capacity(lower); // Parse all points and scalars for pair_result in point_scalar_pairs { @@ -497,8 +461,9 @@ pub(crate) fn p1_msm_bytes( pub(crate) fn p2_msm_bytes( point_scalar_pairs: impl Iterator>, ) -> Result<[u8; G2_LENGTH], PrecompileError> { - let mut g2_points = Vec::new(); - let mut scalars = Vec::new(); + let (lower, _) = point_scalar_pairs.size_hint(); + let mut g2_points = Vec::with_capacity(lower); + let mut scalars = Vec::with_capacity(lower); // Parse all points and scalars for pair_result in point_scalar_pairs { diff --git a/crates/precompile/src/bls12_381/blst.rs b/crates/precompile/src/bls12_381/blst.rs index d20c7af561..008ea5af7a 100644 --- a/crates/precompile/src/bls12_381/blst.rs +++ b/crates/precompile/src/bls12_381/blst.rs @@ -681,8 +681,9 @@ pub(crate) fn map_fp2_to_g2_bytes( pub(crate) fn p1_msm_bytes( point_scalar_pairs: impl Iterator>, ) -> Result<[u8; G1_LENGTH], crate::PrecompileError> { - let mut g1_points = Vec::new(); - let mut scalars = Vec::new(); + let (lower, _) = point_scalar_pairs.size_hint(); + let mut g1_points = Vec::with_capacity(lower); + let mut scalars = Vec::with_capacity(lower); // Parse all points and scalars for pair_result in point_scalar_pairs { @@ -718,8 +719,9 @@ pub(crate) fn p1_msm_bytes( pub(crate) fn p2_msm_bytes( point_scalar_pairs: impl Iterator>, ) -> Result<[u8; G2_LENGTH], crate::PrecompileError> { - let mut g2_points = Vec::new(); - let mut scalars = Vec::new(); + let (lower, _) = point_scalar_pairs.size_hint(); + let mut g2_points = Vec::with_capacity(lower); + let mut scalars = Vec::with_capacity(lower); // Parse all points and scalars for pair_result in point_scalar_pairs { @@ -753,42 +755,5 @@ pub(crate) fn p2_msm_bytes( /// pairing_check_bytes performs a pairing check on a list of G1 and G2 point pairs taking byte inputs. #[inline] pub(crate) fn pairing_check_bytes(pairs: &[PairingPair]) -> Result { - if pairs.is_empty() { - return Ok(true); - } - - let mut parsed_pairs = Vec::with_capacity(pairs.len()); - for ((g1_x, g1_y), (g2_x_0, g2_x_1, g2_y_0, g2_y_1)) in pairs { - // Check if G1 point is zero (point at infinity) - let g1_is_zero = g1_x.iter().all(|&b| b == 0) && g1_y.iter().all(|&b| b == 0); - - // Check if G2 point is zero (point at infinity) - let g2_is_zero = g2_x_0.iter().all(|&b| b == 0) - && g2_x_1.iter().all(|&b| b == 0) - && g2_y_0.iter().all(|&b| b == 0) - && g2_y_1.iter().all(|&b| b == 0); - - // Skip this pair if either point is at infinity as it's a no-op - if g1_is_zero || g2_is_zero { - // Still need to validate the non-zero point if one exists - if !g1_is_zero { - let _ = read_g1(g1_x, g1_y)?; - } - if !g2_is_zero { - let _ = read_g2(g2_x_0, g2_x_1, g2_y_0, g2_y_1)?; - } - continue; - } - - let g1_point = read_g1(g1_x, g1_y)?; - let g2_point = read_g2(g2_x_0, g2_x_1, g2_y_0, g2_y_1)?; - parsed_pairs.push((g1_point, g2_point)); - } - - // If all pairs were filtered out, return true (identity element) - if parsed_pairs.is_empty() { - return Ok(true); - } - - Ok(pairing_check(&parsed_pairs)) + super::pairing_common::pairing_check_bytes_generic(pairs, read_g1, read_g2, pairing_check) } diff --git a/crates/precompile/src/bls12_381/g1_add.rs b/crates/precompile/src/bls12_381/g1_add.rs index 07e0112321..facb6bc4a2 100644 --- a/crates/precompile/src/bls12_381/g1_add.rs +++ b/crates/precompile/src/bls12_381/g1_add.rs @@ -1,9 +1,7 @@ //! BLS12-381 G1 add precompile. More details in [`g1_add`] use super::utils::{pad_g1_point, remove_g1_padding}; -use crate::bls12_381_const::{ - G1_ADD_ADDRESS, G1_ADD_BASE_GAS_FEE, G1_ADD_INPUT_LENGTH, PADDED_G1_LENGTH, -}; use crate::{ + bls12_381_const::{G1_ADD_ADDRESS, G1_ADD_BASE_GAS_FEE, G1_ADD_INPUT_LENGTH, PADDED_G1_LENGTH}, crypto, Precompile, PrecompileError, PrecompileId, PrecompileOutput, PrecompileResult, }; diff --git a/crates/precompile/src/bls12_381/g1_msm.rs b/crates/precompile/src/bls12_381/g1_msm.rs index 7a2d07505c..2651be2df7 100644 --- a/crates/precompile/src/bls12_381/g1_msm.rs +++ b/crates/precompile/src/bls12_381/g1_msm.rs @@ -1,12 +1,14 @@ //! BLS12-381 G1 msm precompile. More details in [`g1_msm`] -use crate::bls12_381::utils::{pad_g1_point, remove_g1_padding}; -use crate::bls12_381::G1Point; -use crate::bls12_381_const::{ - DISCOUNT_TABLE_G1_MSM, G1_MSM_ADDRESS, G1_MSM_BASE_GAS_FEE, G1_MSM_INPUT_LENGTH, - PADDED_G1_LENGTH, SCALAR_LENGTH, -}; -use crate::bls12_381_utils::msm_required_gas; use crate::{ + bls12_381::{ + utils::{pad_g1_point, remove_g1_padding}, + G1Point, + }, + bls12_381_const::{ + DISCOUNT_TABLE_G1_MSM, G1_MSM_ADDRESS, G1_MSM_BASE_GAS_FEE, G1_MSM_INPUT_LENGTH, + PADDED_G1_LENGTH, SCALAR_LENGTH, + }, + bls12_381_utils::msm_required_gas, crypto, Precompile, PrecompileError, PrecompileId, PrecompileOutput, PrecompileResult, }; diff --git a/crates/precompile/src/bls12_381/g2_add.rs b/crates/precompile/src/bls12_381/g2_add.rs index 63cb6b6a51..d0d909e172 100644 --- a/crates/precompile/src/bls12_381/g2_add.rs +++ b/crates/precompile/src/bls12_381/g2_add.rs @@ -1,9 +1,7 @@ //! BLS12-381 G2 add precompile. More details in [`g2_add`] use super::utils::{pad_g2_point, remove_g2_padding}; -use crate::bls12_381_const::{ - G2_ADD_ADDRESS, G2_ADD_BASE_GAS_FEE, G2_ADD_INPUT_LENGTH, PADDED_G2_LENGTH, -}; use crate::{ + bls12_381_const::{G2_ADD_ADDRESS, G2_ADD_BASE_GAS_FEE, G2_ADD_INPUT_LENGTH, PADDED_G2_LENGTH}, crypto, Precompile, PrecompileError, PrecompileId, PrecompileOutput, PrecompileResult, }; diff --git a/crates/precompile/src/bls12_381/g2_msm.rs b/crates/precompile/src/bls12_381/g2_msm.rs index a37a7df623..78cc319028 100644 --- a/crates/precompile/src/bls12_381/g2_msm.rs +++ b/crates/precompile/src/bls12_381/g2_msm.rs @@ -1,11 +1,11 @@ //! BLS12-381 G2 msm precompile. More details in [`g2_msm`] use super::utils::{pad_g2_point, remove_g2_padding}; -use crate::bls12_381_const::{ - DISCOUNT_TABLE_G2_MSM, G2_MSM_ADDRESS, G2_MSM_BASE_GAS_FEE, G2_MSM_INPUT_LENGTH, - PADDED_G2_LENGTH, SCALAR_LENGTH, -}; -use crate::bls12_381_utils::msm_required_gas; use crate::{ + bls12_381_const::{ + DISCOUNT_TABLE_G2_MSM, G2_MSM_ADDRESS, G2_MSM_BASE_GAS_FEE, G2_MSM_INPUT_LENGTH, + PADDED_G2_LENGTH, SCALAR_LENGTH, + }, + bls12_381_utils::msm_required_gas, crypto, Precompile, PrecompileError, PrecompileId, PrecompileOutput, PrecompileResult, }; diff --git a/crates/precompile/src/bls12_381/map_fp2_to_g2.rs b/crates/precompile/src/bls12_381/map_fp2_to_g2.rs index f16cb8cb99..02091c4972 100644 --- a/crates/precompile/src/bls12_381/map_fp2_to_g2.rs +++ b/crates/precompile/src/bls12_381/map_fp2_to_g2.rs @@ -1,9 +1,9 @@ //! BLS12-381 map fp2 to g2 precompile. More details in [`map_fp2_to_g2`] use super::utils::{pad_g2_point, remove_fp_padding}; -use crate::bls12_381_const::{ - MAP_FP2_TO_G2_ADDRESS, MAP_FP2_TO_G2_BASE_GAS_FEE, PADDED_FP2_LENGTH, PADDED_FP_LENGTH, -}; use crate::{ + bls12_381_const::{ + MAP_FP2_TO_G2_ADDRESS, MAP_FP2_TO_G2_BASE_GAS_FEE, PADDED_FP2_LENGTH, PADDED_FP_LENGTH, + }, crypto, Precompile, PrecompileError, PrecompileId, PrecompileOutput, PrecompileResult, }; diff --git a/crates/precompile/src/bls12_381/map_fp_to_g1.rs b/crates/precompile/src/bls12_381/map_fp_to_g1.rs index 1babc3ae8a..80e548ce14 100644 --- a/crates/precompile/src/bls12_381/map_fp_to_g1.rs +++ b/crates/precompile/src/bls12_381/map_fp_to_g1.rs @@ -1,7 +1,7 @@ //! BLS12-381 map fp to g1 precompile. More details in [`map_fp_to_g1`] use super::utils::{pad_g1_point, remove_fp_padding}; -use crate::bls12_381_const::{MAP_FP_TO_G1_ADDRESS, MAP_FP_TO_G1_BASE_GAS_FEE, PADDED_FP_LENGTH}; use crate::{ + bls12_381_const::{MAP_FP_TO_G1_ADDRESS, MAP_FP_TO_G1_BASE_GAS_FEE, PADDED_FP_LENGTH}, crypto, Precompile, PrecompileError, PrecompileId, PrecompileOutput, PrecompileResult, }; diff --git a/crates/precompile/src/bls12_381/pairing.rs b/crates/precompile/src/bls12_381/pairing.rs index 654d0865f9..c598e1b2d6 100644 --- a/crates/precompile/src/bls12_381/pairing.rs +++ b/crates/precompile/src/bls12_381/pairing.rs @@ -1,11 +1,13 @@ //! BLS12-381 pairing precompile. More details in [`pairing`] -use super::utils::{remove_g1_padding, remove_g2_padding}; -use super::PairingPair; -use crate::bls12_381_const::{ - PADDED_G1_LENGTH, PADDED_G2_LENGTH, PAIRING_ADDRESS, PAIRING_INPUT_LENGTH, - PAIRING_MULTIPLIER_BASE, PAIRING_OFFSET_BASE, +use super::{ + utils::{remove_g1_padding, remove_g2_padding}, + PairingPair, }; use crate::{ + bls12_381_const::{ + PADDED_G1_LENGTH, PADDED_G2_LENGTH, PAIRING_ADDRESS, PAIRING_INPUT_LENGTH, + PAIRING_MULTIPLIER_BASE, PAIRING_OFFSET_BASE, + }, crypto, Precompile, PrecompileError, PrecompileId, PrecompileOutput, PrecompileResult, }; use primitives::B256; diff --git a/crates/precompile/src/bls12_381/pairing_common.rs b/crates/precompile/src/bls12_381/pairing_common.rs new file mode 100644 index 0000000000..845dd1a2b8 --- /dev/null +++ b/crates/precompile/src/bls12_381/pairing_common.rs @@ -0,0 +1,61 @@ +//! Common pairing validation logic shared between arkworks and blst backends. +//! +//! This module intentionally holds only the byte-level validation/filtering logic that is +//! identical across backends. Backend-specific parsing and pairing computation are injected +//! as function parameters. + +use crate::PrecompileError; +use std::vec::Vec; + +/// Shared implementation of `pairing_check_bytes`. +#[inline] +pub(super) fn pairing_check_bytes_generic( + pairs: &[super::PairingPair], + read_g1: ReadG1, + read_g2: ReadG2, + pairing_check: PairingCheck, +) -> Result +where + ReadG1: Fn(&[u8; 48], &[u8; 48]) -> Result, + ReadG2: Fn(&[u8; 48], &[u8; 48], &[u8; 48], &[u8; 48]) -> Result, + PairingCheck: FnOnce(&[(G1, G2)]) -> bool, +{ + if pairs.is_empty() { + return Ok(true); + } + + let mut parsed_pairs = Vec::with_capacity(pairs.len()); + for ((g1_x, g1_y), (g2_x_0, g2_x_1, g2_y_0, g2_y_1)) in pairs { + // Check if G1 point is zero (point at infinity) + let g1_is_zero = g1_x.iter().all(|&b| b == 0) && g1_y.iter().all(|&b| b == 0); + + // Check if G2 point is zero (point at infinity) + let g2_is_zero = g2_x_0.iter().all(|&b| b == 0) + && g2_x_1.iter().all(|&b| b == 0) + && g2_y_0.iter().all(|&b| b == 0) + && g2_y_1.iter().all(|&b| b == 0); + + // Skip this pair if either point is at infinity as it's a no-op + if g1_is_zero || g2_is_zero { + // Still need to validate the non-zero point if one exists + if !g1_is_zero { + let _ = read_g1(g1_x, g1_y)?; + } + if !g2_is_zero { + let _ = read_g2(g2_x_0, g2_x_1, g2_y_0, g2_y_1)?; + } + continue; + } + + let g1_point = read_g1(g1_x, g1_y)?; + let g2_point = read_g2(g2_x_0, g2_x_1, g2_y_0, g2_y_1)?; + parsed_pairs.push((g1_point, g2_point)); + } + + // If all pairs were filtered out, return true (identity element) + if parsed_pairs.is_empty() { + return Ok(true); + } + + Ok(pairing_check(&parsed_pairs)) +} diff --git a/crates/precompile/src/bls12_381/utils.rs b/crates/precompile/src/bls12_381/utils.rs index 335ceda278..74f91d2e92 100644 --- a/crates/precompile/src/bls12_381/utils.rs +++ b/crates/precompile/src/bls12_381/utils.rs @@ -1,8 +1,10 @@ //! BLS12-381 utilities for padding and unpadding of input. -use crate::bls12_381_const::{ - FP_LENGTH, FP_PAD_BY, G1_LENGTH, PADDED_FP_LENGTH, PADDED_G1_LENGTH, PADDED_G2_LENGTH, +use crate::{ + bls12_381_const::{ + FP_LENGTH, FP_PAD_BY, G1_LENGTH, PADDED_FP_LENGTH, PADDED_G1_LENGTH, PADDED_G2_LENGTH, + }, + PrecompileError, }; -use crate::PrecompileError; /// Removes zeros with which the precompile inputs are left padded to 64 bytes. pub(super) fn remove_fp_padding(input: &[u8]) -> Result<&[u8; FP_LENGTH], PrecompileError> { diff --git a/crates/precompile/src/id.rs b/crates/precompile/src/id.rs index 3adb5f1003..7ca8d624f2 100644 --- a/crates/precompile/src/id.rs +++ b/crates/precompile/src/id.rs @@ -1,7 +1,4 @@ -use std::borrow::Cow; -use std::fmt; - -use primitives::{address, Address}; +use std::{borrow::Cow, fmt}; use crate::{Precompile, PrecompileSpecId}; @@ -58,32 +55,6 @@ impl PrecompileId { Self::Custom(id.into()) } - /// Returns the mainnet address for the precompile. - pub fn mainnet_address(&self) -> Option
{ - let address = match self { - Self::EcRec => address!("0x0000000000000000000000000000000000000001"), - Self::Sha256 => address!("0x0000000000000000000000000000000000000002"), - Self::Ripemd160 => address!("0x0000000000000000000000000000000000000003"), - Self::Identity => address!("0x0000000000000000000000000000000000000004"), - Self::ModExp => address!("0x0000000000000000000000000000000000000005"), - Self::Bn254Add => address!("0x0000000000000000000000000000000000000006"), - Self::Bn254Mul => address!("0x0000000000000000000000000000000000000007"), - Self::Bn254Pairing => address!("0x0000000000000000000000000000000000000008"), - Self::Blake2F => address!("0x0000000000000000000000000000000000000009"), - Self::KzgPointEvaluation => address!("0x000000000000000000000000000000000000000A"), - Self::Bls12G1Add => address!("0x000000000000000000000000000000000000000B"), - Self::Bls12G1Msm => address!("0x000000000000000000000000000000000000000C"), - Self::Bls12G2Add => address!("0x000000000000000000000000000000000000000D"), - Self::Bls12G2Msm => address!("0x000000000000000000000000000000000000000E"), - Self::Bls12Pairing => address!("0x000000000000000000000000000000000000000F"), - Self::Bls12MapFpToGp1 => address!("0x0000000000000000000000000000000000000010"), - Self::Bls12MapFp2ToGp2 => address!("0x0000000000000000000000000000000000000011"), - Self::P256Verify => address!("0x0000000000000000000000000000000000000012"), - Self::Custom(_) => return None, - }; - Some(address) - } - /// Returns the name of the precompile as defined in EIP-7910. pub fn name(&self) -> &str { match self { diff --git a/crates/precompile/src/interface.rs b/crates/precompile/src/interface.rs index f05cd83bf4..618a275d1c 100644 --- a/crates/precompile/src/interface.rs +++ b/crates/precompile/src/interface.rs @@ -2,7 +2,7 @@ //! the precompile output type, and the precompile error type. use core::fmt::{self, Debug}; use primitives::{Bytes, OnceLock}; -use std::{boxed::Box, string::String, vec::Vec}; +use std::{borrow::Cow, boxed::Box, string::String, vec::Vec}; use crate::bls12_381::{G1Point, G1PointScalar, G2Point, G2PointScalar}; @@ -29,6 +29,8 @@ pub type PrecompileResult = Result; pub struct PrecompileOutput { /// Gas used by the precompile pub gas_used: u64, + /// Gas refunded by the precompile. + pub gas_refunded: i64, /// Output bytes pub bytes: Bytes, /// Whether the precompile reverted @@ -40,6 +42,7 @@ impl PrecompileOutput { pub fn new(gas_used: u64, bytes: Bytes) -> Self { Self { gas_used, + gas_refunded: 0, bytes, reverted: false, } @@ -49,6 +52,7 @@ impl PrecompileOutput { pub fn new_reverted(gas_used: u64, bytes: Bytes) -> Self { Self { gas_used, + gas_refunded: 0, bytes, reverted: true, } @@ -271,14 +275,19 @@ pub enum PrecompileError { Secp256k1RecoverFailed, /// Fatal error with a custom error message Fatal(String), - /// Catch-all variant for other errors - Other(String), + /// Catch-all variant with a custom error message + Other(Cow<'static, str>), } impl PrecompileError { /// Returns another error with the given message. pub fn other(err: impl Into) -> Self { - Self::Other(err.into()) + Self::Other(Cow::Owned(err.into())) + } + + /// Returns another error with the given static string. + pub const fn other_static(err: &'static str) -> Self { + Self::Other(Cow::Borrowed(err)) } /// Returns `true` if the error is out of gas. diff --git a/crates/precompile/src/kzg_point_evaluation/arkworks.rs b/crates/precompile/src/kzg_point_evaluation/arkworks.rs index fa30382100..8fa328f51b 100644 --- a/crates/precompile/src/kzg_point_evaluation/arkworks.rs +++ b/crates/precompile/src/kzg_point_evaluation/arkworks.rs @@ -1,7 +1,8 @@ //! KZG point evaluation precompile using Arkworks BLS12-381 implementation. -use crate::bls12_381::arkworks::pairing_check; -use crate::bls12_381_const::TRUSTED_SETUP_TAU_G2_BYTES; -use crate::PrecompileError; +use crate::{ + bls12_381::arkworks::pairing_check, bls12_381_const::TRUSTED_SETUP_TAU_G2_BYTES, + PrecompileError, +}; use ark_bls12_381::{Fr, G1Affine, G2Affine}; use ark_ec::{AffineRepr, CurveGroup}; use ark_ff::{BigInteger, PrimeField}; diff --git a/crates/precompile/src/kzg_point_evaluation/blst.rs b/crates/precompile/src/kzg_point_evaluation/blst.rs index 57377f8907..b8feafae2c 100644 --- a/crates/precompile/src/kzg_point_evaluation/blst.rs +++ b/crates/precompile/src/kzg_point_evaluation/blst.rs @@ -1,10 +1,12 @@ //! KZG point evaluation precompile using BLST BLS12-381 implementation. -use crate::bls12_381::blst::{ - p1_add_or_double, p1_from_affine, p1_scalar_mul, p1_to_affine, p2_add_or_double, - p2_from_affine, p2_scalar_mul, p2_to_affine, pairing_check, +use crate::{ + bls12_381::blst::{ + p1_add_or_double, p1_from_affine, p1_scalar_mul, p1_to_affine, p2_add_or_double, + p2_from_affine, p2_scalar_mul, p2_to_affine, pairing_check, + }, + bls12_381_const::TRUSTED_SETUP_TAU_G2_BYTES, + PrecompileError, }; -use crate::bls12_381_const::TRUSTED_SETUP_TAU_G2_BYTES; -use crate::PrecompileError; use ::blst::{ blst_p1_affine, blst_p1_affine_in_g1, blst_p1_affine_on_curve, blst_p2_affine, blst_scalar, blst_scalar_fr_check, blst_scalar_from_bendian, diff --git a/crates/precompile/src/lib.rs b/crates/precompile/src/lib.rs index effce60ae3..80bc406c99 100644 --- a/crates/precompile/src/lib.rs +++ b/crates/precompile/src/lib.rs @@ -263,18 +263,20 @@ impl Precompiles { /// Other precompiles with overwrite existing precompiles. #[inline] pub fn extend(&mut self, other: impl IntoIterator) { - let items: Vec = other.into_iter().collect::>(); - for item in items.iter() { - if let Some(short_address) = short_address(item.address()) { - self.optimized_access[short_address] = Some(item.clone()); + let iter = other.into_iter(); + let (lower, _) = iter.size_hint(); + self.addresses.reserve(lower); + self.inner.reserve(lower); + for item in iter { + let address = *item.address(); + if let Some(short_idx) = short_address(&address) { + self.optimized_access[short_idx] = Some(item.clone()); } else { self.all_short_addresses = false; } + self.addresses.insert(address); + self.inner.insert(address, item); } - - self.addresses.extend(items.iter().map(|p| *p.address())); - self.inner - .extend(items.into_iter().map(|p| (*p.address(), p.clone()))); } /// Returns complement of `other` in `self`. diff --git a/crates/precompile/src/modexp.rs b/crates/precompile/src/modexp.rs index 7a4fde3ed2..2c209754bd 100644 --- a/crates/precompile/src/modexp.rs +++ b/crates/precompile/src/modexp.rs @@ -27,20 +27,103 @@ pub const OSAKA: Precompile = #[cfg(feature = "gmp")] /// GMP-based modular exponentiation implementation pub(crate) fn modexp(base: &[u8], exponent: &[u8], modulus: &[u8]) -> Vec { - use rug::{integer::Order::Msf, Integer}; - // Convert byte slices to GMP integers - let base_int = Integer::from_digits(base, Msf); - let exp_int = Integer::from_digits(exponent, Msf); - let mod_int = Integer::from_digits(modulus, Msf); - - // Perform modular exponentiation using GMP's pow_mod - let result = base_int.pow_mod(&exp_int, &mod_int).unwrap_or_default(); - - // Convert result back to bytes - let byte_count = result.significant_bits().div_ceil(8); - let mut output = vec![0u8; byte_count as usize]; - result.write_digits(&mut output, Msf); - output + use core::ffi::c_void; + use core::mem::MaybeUninit; + use gmp_mpfr_sys::gmp; + + struct Mpz(gmp::mpz_t); + + impl Mpz { + fn new() -> Self { + unsafe { + let mut inner = MaybeUninit::::uninit(); + gmp::mpz_init(inner.as_mut_ptr()); + Self(inner.assume_init()) + } + } + + fn as_ptr(&self) -> *const gmp::mpz_t { + &self.0 + } + + fn as_mut_ptr(&mut self) -> *mut gmp::mpz_t { + &mut self.0 + } + + fn set_from_be_bytes(&mut self, bytes: &[u8]) { + unsafe { + if bytes.is_empty() { + gmp::mpz_set_ui(self.as_mut_ptr(), 0); + return; + } + + gmp::mpz_import( + self.as_mut_ptr(), + bytes.len(), + 1, + 1, + 1, + 0, + bytes.as_ptr() as *const c_void, + ); + } + } + + fn to_be_bytes(&self) -> Vec { + unsafe { + if gmp::mpz_sgn(self.as_ptr()) == 0 { + return Vec::new(); + } + + let bits = gmp::mpz_sizeinbase(self.as_ptr(), 2); + let mut output = vec![0u8; bits.div_ceil(8)]; + let mut count: usize = 0; + gmp::mpz_export( + output.as_mut_ptr() as *mut c_void, + &mut count, + 1, + 1, + 1, + 0, + self.as_ptr(), + ); + output.truncate(count); + output + } + } + } + + impl Drop for Mpz { + fn drop(&mut self) { + unsafe { + gmp::mpz_clear(self.as_mut_ptr()); + } + } + } + + let mut base_int = Mpz::new(); + let mut exp_int = Mpz::new(); + let mut mod_int = Mpz::new(); + let mut result = Mpz::new(); + + base_int.set_from_be_bytes(base); + exp_int.set_from_be_bytes(exponent); + mod_int.set_from_be_bytes(modulus); + + unsafe { + if gmp::mpz_sgn(mod_int.as_ptr()) == 0 { + return Vec::new(); + } + + gmp::mpz_powm( + result.as_mut_ptr(), + base_int.as_ptr(), + exp_int.as_ptr(), + mod_int.as_ptr(), + ); + } + + result.to_be_bytes() } #[cfg(not(feature = "gmp"))] diff --git a/crates/primitives/CHANGELOG.md b/crates/primitives/CHANGELOG.md index 7555bd7110..6af04291b6 100644 --- a/crates/primitives/CHANGELOG.md +++ b/crates/primitives/CHANGELOG.md @@ -7,32 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -## [20.2.1](https://github.com/bluealloy/revm/compare/revm-primitives-v20.2.0...revm-primitives-v20.2.1) - 2025-08-12 - -### Other - -- small performance and safety improvements ([#2868](https://github.com/bluealloy/revm/pull/2868)) - -## [20.2.0](https://github.com/bluealloy/revm/compare/revm-primitives-v20.1.0...revm-primitives-v20.2.0) - 2025-08-06 +## [21.1.0](https://github.com/bluealloy/revm/compare/revm-primitives-v21.0.2...revm-primitives-v21.1.0) - 2026-01-15 ### Added -- short address for journal cold/warm check ([#2849](https://github.com/bluealloy/revm/pull/2849)) +- BAL EIP-7928 ([#3070](https://github.com/bluealloy/revm/pull/3070)) +- JournaledAccount sload/sstore ([#3201](https://github.com/bluealloy/revm/pull/3201)) ### Other -- update README.md ([#2842](https://github.com/bluealloy/revm/pull/2842)) -- improve primitives crate documentation and consistency ([#2829](https://github.com/bluealloy/revm/pull/2829)) -- reuse global crypto provide idea ([#2786](https://github.com/bluealloy/revm/pull/2786)) -- add rust-version and note about MSRV ([#2789](https://github.com/bluealloy/revm/pull/2789)) -- add OnceLock re-export with no_std support ([#2787](https://github.com/bluealloy/revm/pull/2787)) -# Changelog -All notable changes to this project will be documented in this file. +- fix typos, grammar errors, and improve documentation consistency ([#3294](https://github.com/bluealloy/revm/pull/3294)) +- happy new year, 2026 licence ([#3272](https://github.com/bluealloy/revm/pull/3272)) +- *(database)* use fixed hashmaps in cache db ([#3231](https://github.com/bluealloy/revm/pull/3231)) +- *(fmt)* merge all imports ([#3184](https://github.com/bluealloy/revm/pull/3184)) -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [21.0.2](https://github.com/bluealloy/revm/compare/revm-primitives-v21.0.1...revm-primitives-v21.0.2) - 2025-11-07 -## [Unreleased] +### Other + +- tag v96 revm v31.0.0 ([#3135](https://github.com/bluealloy/revm/pull/3135)) ## [21.0.1](https://github.com/bluealloy/revm/compare/revm-primitives-v21.0.0...revm-primitives-v21.0.1) - 2025-10-15 @@ -60,6 +53,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - *(cleanup)* Remove EIP-7918 related functions and EIP file ([#2925](https://github.com/bluealloy/revm/pull/2925)) - cargo update ([#2930](https://github.com/bluealloy/revm/pull/2930)) +## [20.2.1](https://github.com/bluealloy/revm/compare/revm-primitives-v20.2.0...revm-primitives-v20.2.1) - 2025-08-12 + +### Other + +- small performance and safety improvements ([#2868](https://github.com/bluealloy/revm/pull/2868)) + +## [20.2.0](https://github.com/bluealloy/revm/compare/revm-primitives-v20.1.0...revm-primitives-v20.2.0) - 2025-08-06 + +### Added + +- short address for journal cold/warm check ([#2849](https://github.com/bluealloy/revm/pull/2849)) + +### Other + +- update README.md ([#2842](https://github.com/bluealloy/revm/pull/2842)) +- improve primitives crate documentation and consistency ([#2829](https://github.com/bluealloy/revm/pull/2829)) +- reuse global crypto provide idea ([#2786](https://github.com/bluealloy/revm/pull/2786)) +- add rust-version and note about MSRV ([#2789](https://github.com/bluealloy/revm/pull/2789)) +- add OnceLock re-export with no_std support ([#2787](https://github.com/bluealloy/revm/pull/2787)) +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + ## [20.1.0](https://github.com/bluealloy/revm/compare/revm-primitives-v20.0.0...revm-primitives-v20.1.0) - 2025-07-23 ### Added @@ -252,12 +270,6 @@ Stable version - Bump new logo ([#1735](https://github.com/bluealloy/revm/pull/1735)) - bump kzg-rs version ([#1734](https://github.com/bluealloy/revm/pull/1734)) -## [9.0.1](https://github.com/bluealloy/revm/compare/revm-primitives-v9.0.0...revm-primitives-v9.0.1) - 2024-08-30 - -### Other -- Bump new logo ([#1735](https://github.com/bluealloy/revm/pull/1735)) -- bump kzg-rs version ([#1734](https://github.com/bluealloy/revm/pull/1734)) - ## [9.0.0](https://github.com/bluealloy/revm/compare/revm-primitives-v8.0.0...revm-primitives-v9.0.0) - 2024-08-29 ### Added diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index 57b4f1dfd0..edc9a2e688 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "revm-primitives" description = "Revm primitives types" -version = "21.0.1" +version = "22.0.0" authors.workspace = true edition.workspace = true keywords.workspace = true @@ -19,7 +19,11 @@ workspace = true [dependencies] # alloy -alloy-primitives = { workspace = true, features = ["rlp", "map"] } +alloy-primitives = { workspace = true, features = [ + "map-indexmap", + "rlp", + "map", +] } # mics num_enum = { version = "0.7", default-features = false } diff --git a/crates/primitives/LICENSE b/crates/primitives/LICENSE index be6d350ebe..7edb0405ff 100644 --- a/crates/primitives/LICENSE +++ b/crates/primitives/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021-2025 draganrakita +Copyright (c) 2021-2026 draganrakita Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/crates/primitives/src/hints_util.rs b/crates/primitives/src/hints_util.rs new file mode 100644 index 0000000000..dc858e9d5f --- /dev/null +++ b/crates/primitives/src/hints_util.rs @@ -0,0 +1,32 @@ +//! Utility functions for hints. +//! Used from Hashbrown . + +// FIXME: Replace with `core::hint::{likely, unlikely}` once they are stable. +// pub use core::intrinsics::{likely, unlikely}; + +/// Cold path function. +#[inline(always)] +#[cold] +pub fn cold_path() {} + +/// Returns `b` but mark `false` path as cold +#[inline(always)] +pub fn likely(b: bool) -> bool { + if b { + true + } else { + cold_path(); + false + } +} + +/// Returns `b` but mark `true` path as cold +#[inline(always)] +pub fn unlikely(b: bool) -> bool { + if b { + cold_path(); + true + } else { + false + } +} diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index d1b027b2ad..1504880dbf 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -24,6 +24,7 @@ pub mod eip7823; pub mod eip7825; pub mod eip7907; pub mod hardfork; +pub mod hints_util; mod once_lock; pub use constants::*; @@ -31,10 +32,10 @@ pub use once_lock::OnceLock; // Reexport alloy primitives. -pub use alloy_primitives::map::{self, hash_map, hash_set, HashMap, HashSet}; pub use alloy_primitives::{ - self, address, b256, bytes, fixed_bytes, hex, hex_literal, keccak256, ruint, uint, Address, - Bytes, FixedBytes, Log, LogData, TxKind, B256, I128, I256, U128, U256, + self, address, b256, bytes, fixed_bytes, hex, hex_literal, keccak256, + map::{self, hash_map, hash_set, indexmap, AddressMap, B256Map, HashMap, HashSet, IndexMap}, + ruint, uint, Address, Bytes, FixedBytes, Log, LogData, TxKind, B256, I128, I256, U128, U256, }; /// Type alias for EVM storage keys (256-bit unsigned integers). diff --git a/crates/revm/CHANGELOG.md b/crates/revm/CHANGELOG.md index 770981b2dc..d12f8ffbca 100644 --- a/crates/revm/CHANGELOG.md +++ b/crates/revm/CHANGELOG.md @@ -7,6 +7,62 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [34.0.0](https://github.com/bluealloy/revm/compare/revm-v33.1.0...revm-v34.0.0) - 2026-01-15 + +### Added + +- Propagate `map-foldhash` Feature Through Dependency Chain ([#3252](https://github.com/bluealloy/revm/pull/3252)) +- dynamically link gmp ([#3250](https://github.com/bluealloy/revm/pull/3250)) + +### Other + +- fix typos, grammar errors, and improve documentation consistency ([#3294](https://github.com/bluealloy/revm/pull/3294)) +- happy new year, 2026 licence ([#3272](https://github.com/bluealloy/revm/pull/3272)) +- re-export statetest-types from revm crate behind test-types feature ([#3247](https://github.com/bluealloy/revm/pull/3247)) +- *(fmt)* merge all imports ([#3184](https://github.com/bluealloy/revm/pull/3184)) + +## [33.1.0](https://github.com/bluealloy/revm/compare/revm-v33.0.0...revm-v33.1.0) - 2025-11-14 + +### Fixed + +- expose optional_priority_fee_check feature from context crate ([#3159](https://github.com/bluealloy/revm/pull/3159)) + +## [33.0.0](https://github.com/bluealloy/revm/compare/revm-v31.0.2...revm-v33.0.0) - 2025-11-10 + +### Other + +- updated the following local packages: revm-context-interface, revm-context, revm-interpreter, revm-precompile, revm-handler, revm-inspector + +## [31.0.2](https://github.com/bluealloy/revm/compare/revm-v31.0.1...revm-v31.0.2) - 2025-11-10 + +### Other + +- updated the following local packages: revm-database, revm-context, revm-handler, revm-inspector + +## [31.0.1](https://github.com/bluealloy/revm/compare/revm-v31.0.0...revm-v31.0.1) - 2025-11-07 + +### Other + +- updated the following local packages: revm-primitives, revm-context, revm-bytecode, revm-state, revm-database-interface, revm-context-interface, revm-database, revm-interpreter, revm-precompile, revm-handler, revm-inspector + +## [31.0.0](https://github.com/bluealloy/revm/compare/revm-v30.2.0...revm-v31.0.0) - 2025-10-30 + +### Other + +- consolidate revme imports ([#3088](https://github.com/bluealloy/revm/pull/3088)) + +## [30.2.0](https://github.com/bluealloy/revm/compare/revm-v30.1.2...revm-v30.2.0) - 2025-10-17 + +### Other + +- updated the following local packages: revm-interpreter, revm-handler, revm-inspector + +## [30.1.2](https://github.com/bluealloy/revm/compare/revm-v30.1.1...revm-v30.1.2) - 2025-10-15 + +### Other + +- updated the following local packages: revm-bytecode, revm-state, revm-database-interface, revm-context-interface, revm-context, revm-database, revm-interpreter, revm-handler, revm-inspector + ## [30.1.1](https://github.com/bluealloy/revm/compare/revm-v30.1.0...revm-v30.1.1) - 2025-10-15 ### Other diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index 80d67d8b21..4519b271a1 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "revm" description = "Revm - Rust Ethereum Virtual Machine" -version = "30.1.1" +version = "34.0.0" authors.workspace = true edition.workspace = true keywords.workspace = true @@ -30,6 +30,7 @@ interpreter.workspace = true precompile.workspace = true primitives.workspace = true state.workspace = true +statetest-types = { workspace = true, optional = true } [dev-dependencies] serde_json = { workspace = true, features = ["alloc", "preserve_order"] } @@ -53,7 +54,7 @@ std = [ "serde_json/std", ] hashbrown = ["interpreter/hashbrown", "precompile/hashbrown"] -map-foldhash = ["primitives/map-foldhash"] +map-foldhash = ["primitives/map-foldhash", "statetest-types?/map-foldhash"] serde = [ "interpreter/serde", "database-interface/serde", @@ -78,6 +79,9 @@ alloydb = ["database/alloydb"] serde-json = ["serde", "inspector/tracer"] tracer = ["inspector/tracer"] +# Enables parsing opcodes from strings. +parse = ["bytecode/parse"] + dev = [ "memory_limit", "optional_balance_check", @@ -98,6 +102,7 @@ optional_fee_charge = ["context/optional_fee_charge"] enable_eip7702 = ["context/enable_eip7702"] enable_eip7623 = ["context/enable_eip7623"] require_l1_data_fee_buffer = ["context/require_l1_data_fee_buffer"] +optional_priority_fee_check = ["context/optional_priority_fee_check"] # Precompiles features: Please look at the comments in `precompile` crate for more information. @@ -113,6 +118,8 @@ asm-sha2 = ["precompile/asm-sha2"] # Binary can be executed on all systems. portable = ["precompile/portable"] -# use gmp for modexp precompile. -# It is faster library but licenced as GPL code, if enabled please make sure to follow the license. +# Use GMP for modexp precompile (LGPL; dynamically linked via system libs). gmp = ["precompile/gmp"] + +# Statetest types for running ethereum tests +test-types = ["dep:statetest-types"] diff --git a/crates/revm/LICENSE b/crates/revm/LICENSE index be6d350ebe..7edb0405ff 100644 --- a/crates/revm/LICENSE +++ b/crates/revm/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021-2025 draganrakita +Copyright (c) 2021-2026 draganrakita Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs index 759af925ae..381004090a 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -26,10 +26,16 @@ pub use primitives; #[doc(inline)] pub use state; +#[cfg(feature = "test-types")] +#[doc(inline)] +pub use statetest_types; + // Export items. -pub use context::journal::{Journal, JournalEntry}; -pub use context::Context; +pub use context::{ + journal::{Journal, JournalEntry}, + Context, +}; pub use database_interface::{Database, DatabaseCommit, DatabaseRef}; pub use handler::{ ExecuteCommitEvm, ExecuteEvm, MainBuilder, MainContext, MainnetEvm, SystemCallCommitEvm, diff --git a/crates/state/CHANGELOG.md b/crates/state/CHANGELOG.md index 190f11d1f0..2f454ba55f 100644 --- a/crates/state/CHANGELOG.md +++ b/crates/state/CHANGELOG.md @@ -7,6 +7,40 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [9.0.0](https://github.com/bluealloy/revm/compare/revm-state-v8.1.1...revm-state-v9.0.0) - 2026-01-15 + +### Added + +- move GasParams to Cfg ([#3229](https://github.com/bluealloy/revm/pull/3229)) +- Propagate `map-foldhash` Feature Through Dependency Chain ([#3252](https://github.com/bluealloy/revm/pull/3252)) +- BAL EIP-7928 ([#3070](https://github.com/bluealloy/revm/pull/3070)) + +### Other + +- apply improvements from ai-bot labeled PRs ([#3297](https://github.com/bluealloy/revm/pull/3297)) +- fix typos, grammar errors, and improve documentation consistency ([#3294](https://github.com/bluealloy/revm/pull/3294)) +- happy new year, 2026 licence ([#3272](https://github.com/bluealloy/revm/pull/3272)) +- deduplicate local/global flags setup ([#3190](https://github.com/bluealloy/revm/pull/3190)) +- *(fmt)* merge all imports ([#3184](https://github.com/bluealloy/revm/pull/3184)) + +## [8.1.1](https://github.com/bluealloy/revm/compare/revm-state-v8.1.0...revm-state-v8.1.1) - 2025-11-07 + +### Other + +- updated the following local packages: revm-primitives, revm-bytecode + +## [8.1.0](https://github.com/bluealloy/revm/compare/revm-state-v8.0.2...revm-state-v8.1.0) - 2025-10-30 + +### Added + +- JournaledAccount, a nice way to update and track changes ([#3086](https://github.com/bluealloy/revm/pull/3086)) + +## [8.0.2](https://github.com/bluealloy/revm/compare/revm-state-v8.0.1...revm-state-v8.0.2) - 2025-10-15 + +### Other + +- updated the following local packages: revm-bytecode + ## [8.0.1](https://github.com/bluealloy/revm/compare/revm-state-v8.0.0...revm-state-v8.0.1) - 2025-10-15 ### Other diff --git a/crates/state/Cargo.toml b/crates/state/Cargo.toml index c2c2505401..84597df29b 100644 --- a/crates/state/Cargo.toml +++ b/crates/state/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "revm-state" description = "Revm state types" -version = "8.0.1" +version = "9.0.0" authors.workspace = true edition.workspace = true keywords.workspace = true @@ -22,13 +22,33 @@ workspace = true primitives.workspace = true bytecode.workspace = true +# alloy +alloy-eip7928.workspace = true + # misc bitflags.workspace = true # Optional serde = { workspace = true, features = ["derive", "rc"], optional = true } +[dev-dependencies] +serde_json = { workspace = true, features = ["alloc"] } + [features] default = ["std"] -std = ["serde?/std", "primitives/std", "bitflags/std", "bytecode/std"] -serde = ["dep:serde", "primitives/serde", "bitflags/serde", "bytecode/serde"] +std = [ + "serde?/std", + "serde_json/std", + "primitives/std", + "bitflags/std", + "bytecode/std", + "alloy-eip7928/std", +] +serde = [ + "dep:serde", + "primitives/serde", + "bitflags/serde", + "bytecode/serde", + "alloy-eip7928/serde", +] +map-foldhash = ["primitives/map-foldhash"] diff --git a/crates/state/LICENSE b/crates/state/LICENSE index be6d350ebe..7edb0405ff 100644 --- a/crates/state/LICENSE +++ b/crates/state/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021-2025 draganrakita +Copyright (c) 2021-2026 draganrakita Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/crates/state/src/account_info.rs b/crates/state/src/account_info.rs index 8c2a41aea8..c91fac8ff5 100644 --- a/crates/state/src/account_info.rs +++ b/crates/state/src/account_info.rs @@ -3,7 +3,7 @@ use core::{ cmp::Ordering, hash::{Hash, Hasher}, }; -use primitives::{B256, KECCAK_EMPTY, U256}; +use primitives::{OnceLock, B256, KECCAK_EMPTY, U256}; /// Account information that contains balance, nonce, code hash and code /// @@ -17,6 +17,12 @@ pub struct AccountInfo { pub nonce: u64, /// Hash of the raw bytes in `code`, or [`KECCAK_EMPTY`]. pub code_hash: B256, + /// Used as a hint to optimize the access to the storage of account. + /// + /// It is set when account is loaded from the database, and if it is `Some` it will called + /// by journal to ask database the storage with this account_id (It will still send the address to the database). + #[cfg_attr(feature = "serde", serde(skip))] + pub account_id: Option, /// [`Bytecode`] data associated with this account. /// /// If [`None`], `code_hash` will be used to fetch it from the database, if code needs to be @@ -28,12 +34,16 @@ pub struct AccountInfo { impl Default for AccountInfo { fn default() -> Self { - Self { - balance: U256::ZERO, - code_hash: KECCAK_EMPTY, - code: Some(Bytecode::default()), - nonce: 0, - } + static DEFAULT: OnceLock = OnceLock::new(); + DEFAULT + .get_or_init(|| Self { + balance: U256::ZERO, + code_hash: KECCAK_EMPTY, + account_id: None, + nonce: 0, + code: Some(Bytecode::default()), + }) + .clone() } } @@ -77,6 +87,7 @@ impl AccountInfo { nonce, code: Some(code), code_hash, + account_id: None, } } @@ -87,10 +98,9 @@ impl AccountInfo { /// As code hash is calculated with [`Bytecode::hash_slow`] there will be performance penalty if used frequently. pub fn with_code(self, code: Bytecode) -> Self { Self { - balance: self.balance, - nonce: self.nonce, code_hash: code.hash_slow(), code: Some(code), + ..self } } @@ -102,10 +112,9 @@ impl AccountInfo { /// also [Self::with_code_and_hash]. pub fn with_code_hash(self, code_hash: B256) -> Self { Self { - balance: self.balance, - nonce: self.nonce, code_hash, code: None, + ..self } } @@ -118,10 +127,9 @@ impl AccountInfo { pub fn with_code_and_hash(self, code: Bytecode, code_hash: B256) -> Self { debug_assert_eq!(code.hash_slow(), code_hash); Self { - balance: self.balance, - nonce: self.nonce, code_hash, code: Some(code), + ..self } } @@ -201,6 +209,7 @@ impl AccountInfo { balance: self.balance, nonce: self.nonce, code_hash: self.code_hash, + account_id: self.account_id, code: None, } } @@ -289,6 +298,7 @@ impl AccountInfo { nonce: 1, code: Some(bytecode), code_hash: hash, + account_id: None, } } } @@ -298,25 +308,17 @@ mod tests { use crate::AccountInfo; use bytecode::Bytecode; use core::cmp::Ordering; - use primitives::{KECCAK_EMPTY, U256}; use std::collections::BTreeSet; #[test] fn test_account_info_trait_consistency() { let bytecode = Bytecode::default(); let account1 = AccountInfo { - balance: U256::ZERO, - nonce: 0, - code_hash: KECCAK_EMPTY, - code: Some(bytecode.clone()), + code: Some(bytecode), + ..AccountInfo::default() }; - let account2 = AccountInfo { - balance: U256::ZERO, - nonce: 0, - code_hash: KECCAK_EMPTY, - code: None, - }; + let account2 = AccountInfo::default(); assert_eq!(account1, account2, "Accounts should be equal ignoring code"); @@ -340,7 +342,7 @@ mod tests { "Set contains account2 (since equal)" ); - let mut accounts = [account2.clone(), account1.clone()]; + let mut accounts = [account2, account1]; accounts.sort(); assert_eq!(accounts[0], accounts[1], "Sorted vec treats them as equal"); } diff --git a/crates/state/src/bal.rs b/crates/state/src/bal.rs new file mode 100644 index 0000000000..bdaaa6953b --- /dev/null +++ b/crates/state/src/bal.rs @@ -0,0 +1,244 @@ +//! Block Access List (BAL) data structures for efficient state access in blockchain execution. +//! +//! This module provides types for managing Block Access Lists, which optimize state access +//! by pre-computing and organizing data that will be accessed during block execution. +//! +//! ## Key Types +//! +//! - **`BalIndex`**: Block access index (0 for pre-execution, 1..n for transactions, n+1 for post-execution) +//! - **`Bal`**: Main BAL structure containing a map of accounts +//! - **`BalWrites`**: Array of (index, value) pairs representing sequential writes to a state item +//! - **`AccountBal`**: Complete BAL structure for an account (balance, nonce, code, and storage) +//! - **`AccountInfoBal`**: Account info BAL data (nonce, balance, code) +//! - **`StorageBal`**: Storage-level BAL data for an account + +pub mod account; +pub mod alloy; +pub mod writes; + +pub use account::{AccountBal, AccountInfoBal, StorageBal}; +pub use writes::BalWrites; + +use crate::{Account, AccountInfo}; +use alloy_eip7928::BlockAccessList as AlloyBal; +use primitives::{Address, IndexMap, StorageKey, StorageValue}; + +/// Block access index (0 for pre-execution, 1..n for transactions, n+1 for post-execution) +pub type BalIndex = u64; + +/// BAL structure. +#[derive(Debug, Default, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct Bal { + /// Accounts bal. + pub accounts: IndexMap, +} + +impl FromIterator<(Address, AccountBal)> for Bal { + fn from_iter>(iter: I) -> Self { + Self { + accounts: iter.into_iter().collect(), + } + } +} + +impl Bal { + /// Create a new BAL builder. + pub fn new() -> Self { + Self { + accounts: IndexMap::default(), + } + } + + /// Pretty print the entire BAL structure in a human-readable format. + #[cfg(feature = "std")] + pub fn pretty_print(&self) { + println!("=== Block Access List (BAL) ==="); + println!("Total accounts: {}", self.accounts.len()); + println!(); + + if self.accounts.is_empty() { + println!("(empty)"); + return; + } + + // Sort accounts by address before printing + let mut sorted_accounts: Vec<_> = self.accounts.iter().collect(); + sorted_accounts.sort_by_key(|(address, _)| *address); + + for (idx, (address, account)) in sorted_accounts.into_iter().enumerate() { + println!("Account #{idx} - Address: {address:?}"); + println!(" Account Info:"); + + // Print nonce writes + if account.account_info.nonce.is_empty() { + println!(" Nonce: (read-only, no writes)"); + } else { + println!(" Nonce writes:"); + for (bal_index, nonce) in &account.account_info.nonce.writes { + println!(" [{bal_index}] -> {nonce}"); + } + } + + // Print balance writes + if account.account_info.balance.is_empty() { + println!(" Balance: (read-only, no writes)"); + } else { + println!(" Balance writes:"); + for (bal_index, balance) in &account.account_info.balance.writes { + println!(" [{bal_index}] -> {balance}"); + } + } + + // Print code writes + if account.account_info.code.is_empty() { + println!(" Code: (read-only, no writes)"); + } else { + println!(" Code writes:"); + for (bal_index, (code_hash, bytecode)) in &account.account_info.code.writes { + println!( + " [{}] -> hash: {:?}, size: {} bytes", + bal_index, + code_hash, + bytecode.len() + ); + } + } + + // Print storage writes + println!(" Storage:"); + if account.storage.storage.is_empty() { + println!(" (no storage slots)"); + } else { + println!(" Total slots: {}", account.storage.storage.len()); + for (storage_key, storage_writes) in &account.storage.storage { + println!(" Slot: {storage_key:#x}"); + if storage_writes.is_empty() { + println!(" (read-only, no writes)"); + } else { + println!(" Writes:"); + for (bal_index, value) in &storage_writes.writes { + println!(" [{bal_index}] -> {value:?}"); + } + } + } + } + + println!(); + } + println!("=== End of BAL ==="); + } + + #[inline] + /// Extend BAL with account. + pub fn update_account(&mut self, bal_index: BalIndex, address: Address, account: &Account) { + let bal_account = self.accounts.entry(address).or_default(); + bal_account.update(bal_index, account); + } + + /// Populate account from BAL. Return true if account info got changed + pub fn populate_account_info( + &self, + account_id: usize, + bal_index: BalIndex, + account: &mut AccountInfo, + ) -> Result { + let Some((_, bal_account)) = self.accounts.get_index(account_id) else { + return Err(BalError::AccountNotFound); + }; + account.account_id = Some(account_id); + + Ok(bal_account.populate_account_info(bal_index, account)) + } + + /// Populate storage slot from BAL. + /// + /// If slot is not found in BAL, it will return an error. + #[inline] + pub fn populate_storage_slot_by_account_id( + &self, + account_index: usize, + bal_index: BalIndex, + key: StorageKey, + value: &mut StorageValue, + ) -> Result<(), BalError> { + let Some((_, bal_account)) = self.accounts.get_index(account_index) else { + return Err(BalError::AccountNotFound); + }; + + if let Some(bal_value) = bal_account.storage.get(key, bal_index)? { + *value = bal_value; + }; + + Ok(()) + } + + /// Populate storage slot from BAL by account address. + #[inline] + pub fn populate_storage_slot( + &self, + account_address: Address, + bal_index: BalIndex, + key: StorageKey, + value: &mut StorageValue, + ) -> Result<(), BalError> { + let Some(bal_account) = self.accounts.get(&account_address) else { + return Err(BalError::AccountNotFound); + }; + + if let Some(bal_value) = bal_account.storage.get(key, bal_index)? { + *value = bal_value; + }; + Ok(()) + } + + /// Get storage from BAL. + pub fn account_storage( + &self, + account_index: usize, + key: StorageKey, + bal_index: BalIndex, + ) -> Result { + let Some((_, bal_account)) = self.accounts.get_index(account_index) else { + return Err(BalError::AccountNotFound); + }; + + let Some(storage_value) = bal_account.storage.get(key, bal_index)? else { + return Err(BalError::SlotNotFound); + }; + + Ok(storage_value) + } + + /// Consume Bal and create [`AlloyBal`] + pub fn into_alloy_bal(self) -> AlloyBal { + let mut alloy_bal = AlloyBal::from_iter( + self.accounts + .into_iter() + .map(|(address, account)| account.into_alloy_account(address)), + ); + alloy_bal.sort_by_key(|a| a.address); + alloy_bal + } +} + +/// BAL error. +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum BalError { + /// Account not found in BAL. + AccountNotFound, + /// Slot not found in BAL. + SlotNotFound, +} + +impl core::fmt::Display for BalError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::AccountNotFound => write!(f, "Account not found in BAL"), + Self::SlotNotFound => write!(f, "Slot not found in BAL"), + } + } +} + +impl core::error::Error for BalError {} diff --git a/crates/state/src/bal/account.rs b/crates/state/src/bal/account.rs new file mode 100644 index 0000000000..6ca4b7458a --- /dev/null +++ b/crates/state/src/bal/account.rs @@ -0,0 +1,320 @@ +//! BAL builder module + +use crate::{ + bal::{writes::BalWrites, BalError, BalIndex}, + Account, AccountInfo, EvmStorage, +}; +use alloy_eip7928::{ + AccountChanges as AlloyAccountChanges, BalanceChange as AlloyBalanceChange, + CodeChange as AlloyCodeChange, NonceChange as AlloyNonceChange, + SlotChanges as AlloySlotChanges, StorageChange as AlloyStorageChange, +}; +use bytecode::{Bytecode, BytecodeDecodeError}; +use core::ops::{Deref, DerefMut}; +use primitives::{Address, StorageKey, StorageValue, B256, U256}; +use std::{ + collections::{btree_map::Entry, BTreeMap}, + vec::Vec, +}; + +/// Account BAL structure. +#[derive(Debug, Default, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct AccountBal { + /// Account info bal. + pub account_info: AccountInfoBal, + /// Storage bal. + pub storage: StorageBal, +} + +impl Deref for AccountBal { + type Target = AccountInfoBal; + + fn deref(&self) -> &Self::Target { + &self.account_info + } +} + +impl DerefMut for AccountBal { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.account_info + } +} + +impl AccountBal { + /// Populate account from BAL. Return true if account info got changed + pub fn populate_account_info(&self, bal_index: BalIndex, account: &mut AccountInfo) -> bool { + self.account_info.populate_account_info(bal_index, account) + } + + /// Extend account from another account. + #[inline] + pub fn update(&mut self, bal_index: BalIndex, account: &Account) { + if account.is_selfdestructed_locally() { + let empty_info = AccountInfo::default(); + self.account_info + .update(bal_index, &account.original_info, &empty_info); + self.storage.update_reads(account.storage.keys().copied()); + return; + } + + self.account_info + .update(bal_index, &account.original_info, &account.info); + + self.storage.update(bal_index, &account.storage); + } + + /// Create account from alloy account changes. + #[inline] + pub fn try_from_alloy( + alloy_account: AlloyAccountChanges, + ) -> Result<(Address, Self), BytecodeDecodeError> { + Ok(( + alloy_account.address, + AccountBal { + account_info: AccountInfoBal { + nonce: BalWrites::from(alloy_account.nonce_changes), + balance: BalWrites::from(alloy_account.balance_changes), + code: BalWrites::try_from(alloy_account.code_changes)?, + }, + storage: StorageBal::from_iter( + alloy_account + .storage_changes + .into_iter() + .chain( + alloy_account + .storage_reads + .into_iter() + .map(|key| AlloySlotChanges::new(key, Default::default())), + ) + .map(|slot| (slot.slot, BalWrites::from(slot.changes))), + ), + }, + )) + } + + /// Consumes AccountBal and converts it into [`AlloyAccountChanges`]. + #[inline] + pub fn into_alloy_account(self, address: Address) -> AlloyAccountChanges { + let mut storage_reads = Vec::new(); + let mut storage_changes = Vec::new(); + for (key, value) in self.storage.storage { + if value.writes.is_empty() { + storage_reads.push(key); + } else { + storage_changes.push(AlloySlotChanges::new( + key, + value + .writes + .into_iter() + .map(|(index, value)| AlloyStorageChange::new(index, value)) + .collect(), + )); + } + } + + AlloyAccountChanges { + address, + storage_changes, + storage_reads, + balance_changes: self + .account_info + .balance + .writes + .into_iter() + .map(|(index, value)| AlloyBalanceChange::new(index, value)) + .collect(), + nonce_changes: self + .account_info + .nonce + .writes + .into_iter() + .map(|(index, value)| AlloyNonceChange::new(index, value)) + .collect(), + code_changes: self + .account_info + .code + .writes + .into_iter() + .map(|(index, (_, value))| AlloyCodeChange::new(index, value.original_bytes())) + .collect(), + } + } +} + +/// Account info bal structure. +#[derive(Debug, Default, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct AccountInfoBal { + /// Nonce builder. + pub nonce: BalWrites, + /// Balance builder. + pub balance: BalWrites, + /// Code builder. + pub code: BalWrites<(B256, Bytecode)>, +} + +impl AccountInfoBal { + /// Populate account info from BAL. Return true if account info got changed + pub fn populate_account_info(&self, bal_index: BalIndex, account: &mut AccountInfo) -> bool { + let mut changed = false; + if let Some(nonce) = self.nonce.get(bal_index) { + account.nonce = nonce; + changed = true; + } + if let Some(balance) = self.balance.get(bal_index) { + account.balance = balance; + changed = true; + } + if let Some(code) = self.code.get(bal_index) { + account.code_hash = code.0; + account.code = Some(code.1); + changed = true; + } + changed + } + + /// Extend account info from another account info. + #[inline] + pub fn update(&mut self, index: BalIndex, original: &AccountInfo, present: &AccountInfo) { + self.nonce.update(index, &original.nonce, present.nonce); + self.balance + .update(index, &original.balance, present.balance); + self.code.update_with_key( + index, + &original.code_hash, + (present.code_hash, present.code.clone().unwrap_or_default()), + |i| &i.0, + ); + } + + /// Extend account info from another account info. + #[inline] + pub fn extend(&mut self, bal_account: AccountInfoBal) { + self.nonce.extend(bal_account.nonce); + self.balance.extend(bal_account.balance); + self.code.extend(bal_account.code); + } + + /// Update account balance in BAL. + #[inline] + pub fn balance_update(&mut self, bal_index: BalIndex, original_balance: &U256, balance: U256) { + self.balance.update(bal_index, original_balance, balance); + } + + /// Update account nonce in BAL. + #[inline] + pub fn nonce_update(&mut self, bal_index: BalIndex, original_nonce: &u64, nonce: u64) { + self.nonce.update(bal_index, original_nonce, nonce); + } + + /// Update account code in BAL. + #[inline] + pub fn code_update( + &mut self, + bal_index: BalIndex, + original_code_hash: &B256, + code_hash: B256, + code: Bytecode, + ) { + self.code + .update_with_key(bal_index, original_code_hash, (code_hash, code), |i| &i.0); + } +} + +/// Storage BAL +#[derive(Debug, Default, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct StorageBal { + /// Storage with writes and reads. + pub storage: BTreeMap>, +} + +impl StorageBal { + /// Get storage from the builder. + #[inline] + pub fn get( + &self, + key: StorageKey, + bal_index: BalIndex, + ) -> Result, BalError> { + Ok(self.get_bal_writes(key)?.get(bal_index)) + } + + /// Get storage writes from the builder. + #[inline] + pub fn get_bal_writes(&self, key: StorageKey) -> Result<&BalWrites, BalError> { + self.storage.get(&key).ok_or(BalError::SlotNotFound) + } + + /// Extend storage from another storage. + #[inline] + pub fn extend(&mut self, storage: StorageBal) { + for (key, value) in storage.storage { + match self.storage.entry(key) { + Entry::Occupied(mut entry) => { + entry.get_mut().extend(value); + } + Entry::Vacant(entry) => { + entry.insert(value); + } + } + } + } + + /// Update storage from [`EvmStorage`]. + #[inline] + pub fn update(&mut self, bal_index: BalIndex, storage: &EvmStorage) { + for (key, value) in storage { + self.storage.entry(*key).or_default().update( + bal_index, + &value.original_value, + value.present_value, + ); + } + } + + /// Update reads from [`EvmStorage`]. + /// + /// It will expend inner map with new reads. + #[inline] + pub fn update_reads(&mut self, storage: impl Iterator) { + for key in storage { + self.storage.entry(key).or_default(); + } + } + + /// Insert storage into the builder. + pub fn extend_iter( + &mut self, + storage: impl Iterator)>, + ) { + for (key, value) in storage { + self.storage.insert(key, value); + } + } + + /// Convert the storage into a vector of reads and writes + pub fn into_vecs(self) -> (Vec, Vec<(StorageKey, BalWrites)>) { + let mut reads = Vec::new(); + let mut writes = Vec::new(); + + for (key, value) in self.storage { + if value.writes.is_empty() { + reads.push(key); + } else { + writes.push((key, value)); + } + } + + (reads, writes) + } +} + +impl FromIterator<(StorageKey, BalWrites)> for StorageBal { + fn from_iter)>>(iter: I) -> Self { + Self { + storage: iter.into_iter().collect(), + } + } +} diff --git a/crates/state/src/bal/alloy.rs b/crates/state/src/bal/alloy.rs new file mode 100644 index 0000000000..f17288fd66 --- /dev/null +++ b/crates/state/src/bal/alloy.rs @@ -0,0 +1,80 @@ +//! Alloy BAL types conversions. + +// Re-export Alloy BAL types. +pub use alloy_eip7928::{ + BalanceChange as AlloyBalanceChange, BlockAccessList as AlloyBal, + CodeChange as AlloyCodeChange, NonceChange as AlloyNonceChange, + StorageChange as AlloyStorageChange, +}; + +use crate::bal::{AccountBal, Bal, BalWrites}; +use bytecode::{Bytecode, BytecodeDecodeError}; +use primitives::{IndexMap, B256, U256}; +use std::vec::Vec; + +impl TryFrom for Bal { + type Error = BytecodeDecodeError; + + fn try_from(alloy_bal: AlloyBal) -> Result { + let accounts = IndexMap::from_iter( + alloy_bal + .into_iter() + .map(AccountBal::try_from_alloy) + .collect::, _>>()?, + ); + + Ok(Self { accounts }) + } +} + +impl From> for BalWrites { + fn from(value: Vec) -> Self { + Self { + writes: value + .into_iter() + .map(|change| (change.block_access_index, change.post_balance)) + .collect(), + } + } +} + +impl From> for BalWrites { + fn from(value: Vec) -> Self { + Self { + writes: value + .into_iter() + .map(|change| (change.block_access_index, change.new_nonce)) + .collect(), + } + } +} + +impl From> for BalWrites { + fn from(value: Vec) -> Self { + Self { + writes: value + .into_iter() + .map(|change| (change.block_access_index, change.new_value)) + .collect(), + } + } +} + +impl TryFrom> for BalWrites<(B256, Bytecode)> { + type Error = BytecodeDecodeError; + + fn try_from(value: Vec) -> Result { + Ok(Self { + writes: value + .into_iter() + .map(|change| { + // convert bytes to bytecode. + Bytecode::new_raw_checked(change.new_code).map(|bytecode| { + let hash = bytecode.hash_slow(); + (change.block_access_index, (hash, bytecode)) + }) + }) + .collect::, Self::Error>>()?, + }) + } +} diff --git a/crates/state/src/bal/writes.rs b/crates/state/src/bal/writes.rs new file mode 100644 index 0000000000..db4f1efbfc --- /dev/null +++ b/crates/state/src/bal/writes.rs @@ -0,0 +1,184 @@ +//! BAL containing writes. + +use crate::bal::BalIndex; +use std::vec::Vec; + +/// Use to store values +/// +/// If empty it means that this item was read from database. +#[derive(Debug, Default, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct BalWrites { + /// List of writes with BalIndex. + pub writes: Vec<(BalIndex, T)>, +} + +impl BalWrites { + /// Create a new BalWrites. + pub fn new(mut writes: Vec<(BalIndex, T)>) -> Self { + writes.sort_by_key(|(index, _)| *index); + Self { writes } + } + + /// Linear search is used for small number of writes. It is faster than binary search. + #[inline(never)] + pub fn get_linear_search(&self, bal_index: BalIndex) -> Option { + let mut last_item = None; + for (index, item) in self.writes.iter() { + // if index is greater than bal_index we return the last item. + if index >= &bal_index { + return last_item; + } + last_item = Some(item.clone()); + } + last_item + } + + /// Get value from BAL. + pub fn get(&self, bal_index: BalIndex) -> Option { + if self.writes.len() < 5 { + return self.get_linear_search(bal_index); + } + // else do binary search. + let i = match self + .writes + .binary_search_by_key(&bal_index, |(index, _)| *index) + { + Ok(i) => i, + Err(i) => i, + }; + // only if i is not zero, we return the previous value. + (i != 0).then(|| self.writes[i - 1].1.clone()) + } + + /// Extend the builder with another builder. + pub fn extend(&mut self, other: BalWrites) { + self.writes.extend(other.writes); + } + + /// Returns true if the builder is empty. + pub fn is_empty(&self) -> bool { + self.writes.is_empty() + } + + /// Force insert a value into the BalWrites. + /// + /// Check if last index is same as the index to insert. + /// If it is, we override the value. + /// If it is not, we push the value to the end of the vector. + /// + /// No checks for original value is done. This is useful when we know that value is different. + #[inline] + pub fn force_update(&mut self, index: BalIndex, value: T) { + if let Some(last) = self.writes.last_mut() { + if index == last.0 { + last.1 = value; + return; + } + } + self.writes.push((index, value)); + } + + /// Insert a value into the builder. + /// + /// If BalIndex is same as last it will override the value. + pub fn update(&mut self, index: BalIndex, original_value: &T, value: T) { + self.update_with_key(index, original_value, value, |i| i); + } + + /// Insert a value into the builder. + /// + /// If BalIndex is same as last it will override the value. + /// + /// Assumes that index is always greater than last one and that Writes are updated in proper order. + #[inline] + pub fn update_with_key( + &mut self, + index: BalIndex, + original_subvalue: &K, + value: T, + f: F, + ) where + F: Fn(&T) -> &K, + { + // if index is different, we push the new value. + if let Some(last) = self.writes.last_mut() { + if last.0 != index { + // we push the new value only if it is changed. + if f(&last.1) != f(&value) { + self.writes.push((index, value)); + } + return; + } + } + + // extract previous (Can be original_subvalue or previous value) and last value. + let (previous, last) = match self.writes.as_mut_slice() { + [.., previous, last] => (f(&previous.1), last), + [last] => (original_subvalue, last), + [] => { + // if writes are empty check if original value is same as newly set value. + if original_subvalue != f(&value) { + self.writes.push((index, value)); + } + return; + } + }; + + // if previous value is same, we pop the last value. + if previous == f(&value) { + self.writes.pop(); + return; + } + + // if it is different, we update the last value. + last.1 = value; + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_get() { + let bal_writes = BalWrites::new(vec![(0, 1), (1, 2), (2, 3)]); + assert_eq!(bal_writes.get(0), None); + assert_eq!(bal_writes.get(1), Some(1)); + assert_eq!(bal_writes.get(2), Some(2)); + assert_eq!(bal_writes.get(3), Some(3)); + assert_eq!(bal_writes.get(4), Some(3)); + } + + fn get_binary_search(threshold: BalIndex) { + // Construct test data up to (threshold - 1), skipping one key to simulate a gap. + let entries: Vec<_> = (0..threshold - 1) + .map(|i| (i, i + 1)) + .chain(std::iter::once((threshold, threshold + 1))) + .collect(); + + let bal_writes = BalWrites::new(entries); + + // Case 1: lookup before any entries + assert_eq!(bal_writes.get(0), None); + + // Case 2: lookups for existing keys before the gap + for i in 1..threshold - 1 { + assert_eq!(bal_writes.get(i), Some(i)); + } + + // Case 3: lookup at the skipped key — should return the previous value + assert_eq!(bal_writes.get(threshold), Some(threshold - 1)); + + // Case 4: lookup after the skipped key — should return the next valid value + assert_eq!(bal_writes.get(threshold + 1), Some(threshold + 1)); + } + + #[test] + fn test_get_binary_search() { + get_binary_search(4); + get_binary_search(5); + get_binary_search(6); + get_binary_search(7); + } +} diff --git a/crates/state/src/lib.rs b/crates/state/src/lib.rs index a06534d56a..e7c229e45b 100644 --- a/crates/state/src/lib.rs +++ b/crates/state/src/lib.rs @@ -2,8 +2,13 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] #![cfg_attr(not(feature = "std"), no_std)] +#[cfg(not(feature = "std"))] +extern crate alloc as std; + mod account_info; +pub mod bal; mod types; + pub use bytecode; pub use account_info::AccountInfo; @@ -12,15 +17,28 @@ pub use primitives; pub use types::{EvmState, EvmStorage, TransientStorage}; use bitflags::bitflags; -use primitives::hardfork::SpecId; -use primitives::{HashMap, StorageKey, StorageValue, U256}; - -/// Account type used inside Journal to track changed to state. +use primitives::{hardfork::SpecId, HashMap, OnceLock, StorageKey, StorageValue, U256}; +use std::boxed::Box; + +/// The main account type used inside Revm. It is stored inside Journal and contains all the information about the account. +/// +/// Other than standard Account information it contains its status that can be both cold and warm +/// additional to that it contains BAL that is used to load data for this particular account. +/// +/// On loading from database: +/// * If CompiledBal is present, load values from BAL into Account (Assume account has read data from database) +/// * In case of parallel execution, AccountInfo would be same over all parallel executions. +/// * Maybe use transaction_id as a way to notify user that this is obsolete data. +/// * Database needs to load account and tie to with BAL writes +/// If CompiledBal is not present, use loaded values +/// * Account is already up to date (uses present flow). #[derive(Debug, Clone, PartialEq, Eq, Default)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] pub struct Account { /// Balance, nonce, and code pub info: AccountInfo, + /// Original account info used by BAL, changed only on cold load by BAL. + pub original_info: Box, /// Transaction id, used to track when account was toched/loaded into journal. pub transaction_id: usize, /// Storage cache @@ -32,12 +50,16 @@ pub struct Account { impl Account { /// Creates new account and mark it as non existing. pub fn new_not_existing(transaction_id: usize) -> Self { - Self { - info: AccountInfo::default(), - storage: HashMap::default(), - transaction_id, - status: AccountStatus::LoadedAsNotExisting, - } + static DEFAULT: OnceLock = OnceLock::new(); + DEFAULT + .get_or_init(|| Self { + info: AccountInfo::default(), + storage: HashMap::default(), + transaction_id, + status: AccountStatus::LoadedAsNotExisting, + original_info: Box::new(AccountInfo::default()), + }) + .clone() } /// Make changes to the caller account. @@ -161,10 +183,7 @@ impl Account { /// Returns true if it is created globally for first time. #[inline] pub fn mark_created_locally(&mut self) -> bool { - self.status |= AccountStatus::CreatedLocal; - let is_created_globaly = !self.status.contains(AccountStatus::Created); - self.status |= AccountStatus::Created; - is_created_globaly + self.mark_local_and_global(AccountStatus::CreatedLocal, AccountStatus::Created) } /// Unmark account as locally created @@ -176,10 +195,22 @@ impl Account { /// Mark account as locally and globally selfdestructed #[inline] pub fn mark_selfdestructed_locally(&mut self) -> bool { - self.status |= AccountStatus::SelfDestructedLocal; - let is_global_selfdestructed = !self.status.contains(AccountStatus::SelfDestructed); - self.status |= AccountStatus::SelfDestructed; - is_global_selfdestructed + self.mark_local_and_global( + AccountStatus::SelfDestructedLocal, + AccountStatus::SelfDestructed, + ) + } + + #[inline] + fn mark_local_and_global( + &mut self, + local_flag: AccountStatus, + global_flag: AccountStatus, + ) -> bool { + self.status |= local_flag; + let is_global_first_time = !self.status.contains(global_flag); + self.status |= global_flag; + is_global_first_time } /// Unmark account as locally selfdestructed @@ -275,11 +306,54 @@ impl Account { impl From for Account { fn from(info: AccountInfo) -> Self { + let original_info = Box::new(info.clone()); Self { info, storage: HashMap::default(), transaction_id: 0, status: AccountStatus::empty(), + original_info, + } + } +} + +#[cfg(feature = "serde")] +mod serde_impl { + use super::*; + use serde::{Deserialize, Serialize}; + + #[derive(Serialize, Deserialize)] + struct AccountSerde { + info: AccountInfo, + original_info: Option, + storage: HashMap, + transaction_id: usize, + status: AccountStatus, + } + + impl<'de> Deserialize<'de> for super::Account { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let AccountSerde { + info, + original_info, + storage, + transaction_id, + status, + } = Deserialize::deserialize(deserializer)?; + + // If original info is not present, use info as original info + let original_info = original_info.unwrap_or_else(|| info.clone()); + + Ok(Account { + info, + original_info: Box::new(original_info), + storage, + transaction_id, + status, + }) } } } @@ -343,6 +417,14 @@ bitflags! { } } +impl AccountStatus { + /// Returns true if the account status is touched. + #[inline] + pub fn is_touched(&self) -> bool { + self.contains(AccountStatus::Touched) + } +} + impl Default for AccountStatus { fn default() -> Self { AccountStatus::empty() @@ -350,7 +432,7 @@ impl Default for AccountStatus { } /// This type keeps track of the current value of a storage slot. -#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Default, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct EvmStorageSlot { /// Original value of the storage slot @@ -423,6 +505,10 @@ impl EvmStorageSlot { #[inline] pub fn mark_warm_with_transaction_id(&mut self, transaction_id: usize) -> bool { let is_cold = self.is_cold_transaction_id(transaction_id); + if is_cold { + // if slot is cold original value should be reset to present value. + self.original_value = self.present_value; + } self.transaction_id = transaction_id; self.is_cold = false; is_cold @@ -551,6 +637,27 @@ mod tests { assert!(!account.is_created()); } + #[test] + #[cfg(feature = "serde")] + fn test_account_serialize_deserialize() { + let account = Account::default().with_selfdestruct_mark(); + let serialized = serde_json::to_string(&account).unwrap(); + let deserialized: Account = serde_json::from_str(&serialized).unwrap(); + assert_eq!(account, deserialized); + } + + #[test] + #[cfg(feature = "serde")] + fn test_account_serialize_deserialize_without_original_info() { + let deserialize_without_original_info = r#" + {"info":{"balance":"0x0","nonce":0,"code_hash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470","storage_id":null,"code":{"LegacyAnalyzed":{"bytecode":"0x00","original_len":0,"jump_table":{"order":"bitvec::order::Lsb0","head":{"width":8,"index":0},"bits":0,"data":[]}}}},"transaction_id":0,"storage":{},"status":"SelfDestructed"}"#; + + let account = Account::default().with_selfdestruct_mark(); + let deserialized: Account = + serde_json::from_str(deserialize_without_original_info).unwrap(); + assert_eq!(account, deserialized); + } + #[test] fn test_account_with_touched_mark() { let account = Account::default().with_touched_mark(); diff --git a/crates/statetest-types/CHANGELOG.md b/crates/statetest-types/CHANGELOG.md index 2c3730f786..f41f6f6401 100644 --- a/crates/statetest-types/CHANGELOG.md +++ b/crates/statetest-types/CHANGELOG.md @@ -7,6 +7,68 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [14.0.0](https://github.com/bluealloy/revm/compare/revm-statetest-types-v13.1.0...revm-statetest-types-v14.0.0) - 2026-01-15 + +### Added + +- move GasParams to Cfg ([#3229](https://github.com/bluealloy/revm/pull/3229)) +- Propagate `map-foldhash` Feature Through Dependency Chain ([#3252](https://github.com/bluealloy/revm/pull/3252)) +- BAL EIP-7928 ([#3070](https://github.com/bluealloy/revm/pull/3070)) + +### Fixed + +- *(statetest)* use spec-aware blob base fee update fraction ([#3210](https://github.com/bluealloy/revm/pull/3210)) + +### Other + +- apply improvements from ai-bot labeled PRs ([#3297](https://github.com/bluealloy/revm/pull/3297)) +- fix typos, grammar errors, and improve documentation consistency ([#3294](https://github.com/bluealloy/revm/pull/3294)) +- happy new year, 2026 licence ([#3272](https://github.com/bluealloy/revm/pull/3272)) +- re-export statetest-types from revm crate behind test-types feature ([#3247](https://github.com/bluealloy/revm/pull/3247)) +- *(fmt)* merge all imports ([#3184](https://github.com/bluealloy/revm/pull/3184)) + +## [13.1.0](https://github.com/bluealloy/revm/compare/revm-statetest-types-v13.0.0...revm-statetest-types-v13.1.0) - 2025-11-14 + +### Other + +- updated the following local packages: revm + +## [13.0.0](https://github.com/bluealloy/revm/compare/revm-statetest-types-v11.0.2...revm-statetest-types-v13.0.0) - 2025-11-10 + +### Other + +- updated the following local packages: revm + +## [11.0.2](https://github.com/bluealloy/revm/compare/revm-statetest-types-v11.0.1...revm-statetest-types-v11.0.2) - 2025-11-10 + +### Other + +- updated the following local packages: revm + +## [11.0.1](https://github.com/bluealloy/revm/compare/revm-statetest-types-v11.0.0...revm-statetest-types-v11.0.1) - 2025-11-07 + +### Fixed + +- *(revme)* use primitive hashmap in statetest ([#3137](https://github.com/bluealloy/revm/pull/3137)) + +## [11.0.0](https://github.com/bluealloy/revm/compare/revm-statetest-types-v10.2.0...revm-statetest-types-v11.0.0) - 2025-10-30 + +### Other + +- updated the following local packages: revm + +## [10.2.0](https://github.com/bluealloy/revm/compare/revm-statetest-types-v10.1.2...revm-statetest-types-v10.2.0) - 2025-10-17 + +### Other + +- updated the following local packages: revm + +## [10.1.2](https://github.com/bluealloy/revm/compare/revm-statetest-types-v10.1.1...revm-statetest-types-v10.1.2) - 2025-10-15 + +### Other + +- updated the following local packages: revm + ## [10.1.1](https://github.com/bluealloy/revm/compare/revm-statetest-types-v10.1.0...revm-statetest-types-v10.1.1) - 2025-10-15 ### Other diff --git a/crates/statetest-types/Cargo.toml b/crates/statetest-types/Cargo.toml index 2492ab7a63..b50e6cf270 100644 --- a/crates/statetest-types/Cargo.toml +++ b/crates/statetest-types/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "revm-statetest-types" description = "Statetest types for revme" -version = "10.1.1" +version = "14.0.0" authors.workspace = true edition.workspace = true keywords.workspace = true @@ -18,10 +18,26 @@ rustdoc-args = ["--cfg", "docsrs"] workspace = true [dependencies] -# revm -revm = { workspace = true, features = ["std", "serde"] } +# revm crates +primitives = { workspace = true, features = ["std", "serde"] } +bytecode = { workspace = true, features = ["std", "serde"] } +state = { workspace = true, features = ["std", "serde"] } +context = { workspace = true, features = ["std", "serde"] } +context-interface = { workspace = true, features = ["std", "serde"] } +database = { workspace = true, features = ["std", "serde"] } + serde = { workspace = true, features = ["derive", "rc"] } serde_json = { workspace = true, features = ["preserve_order"] } k256 = { workspace = true } thiserror = { workspace = true } -alloy-eips = { workspace = true } + +# alloy +alloy-eip7928 = { workspace = true, features = ["std", "serde", "rlp"] } + +[features] +map-foldhash = [ + "primitives/map-foldhash", + "state/map-foldhash", + "context/map-foldhash", + "database/map-foldhash", +] diff --git a/crates/statetest-types/LICENSE b/crates/statetest-types/LICENSE index be6d350ebe..7edb0405ff 100644 --- a/crates/statetest-types/LICENSE +++ b/crates/statetest-types/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021-2025 draganrakita +Copyright (c) 2021-2026 draganrakita Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/crates/statetest-types/src/account_info.rs b/crates/statetest-types/src/account_info.rs index df87ec10e8..0796800bea 100644 --- a/crates/statetest-types/src/account_info.rs +++ b/crates/statetest-types/src/account_info.rs @@ -1,4 +1,4 @@ -use revm::primitives::{Bytes, HashMap, StorageKey, StorageValue, U256}; +use primitives::{Bytes, HashMap, StorageKey, StorageValue, U256}; use serde::Deserialize; use crate::deserializer::deserialize_str_as_u64; diff --git a/crates/statetest-types/src/blockchain.rs b/crates/statetest-types/src/blockchain.rs index 7637ebd731..d76dfaa259 100644 --- a/crates/statetest-types/src/blockchain.rs +++ b/crates/statetest-types/src/blockchain.rs @@ -4,11 +4,10 @@ //! from the Ethereum test suite. use crate::{deserialize_maybe_empty, AccountInfo, TestAuthorization}; -use revm::{ - context::{transaction::AccessList, BlockEnv, TxEnv}, - context_interface::block::BlobExcessGasAndPrice, - primitives::{Address, Bytes, FixedBytes, TxKind, B256, U256}, -}; +use alloy_eip7928::BlockAccessList; +use context::{transaction::AccessList, BlockEnv, TxEnv}; +use context_interface::block::BlobExcessGasAndPrice; +use primitives::{Address, Bytes, FixedBytes, TxKind, B256, U256}; use serde::Deserialize; use std::collections::BTreeMap; @@ -108,6 +107,12 @@ pub struct Block { pub uncle_headers: Option>, /// Withdrawals in the block (post-Shanghai) pub withdrawals: Option>, + /// Block access list + pub block_access_list: Option, + /// Withdrawal requests (EIP-7002) + pub withdrawal_requests: Option>, + /// Consolidation requests (EIP-7251) + pub consolidation_requests: Option>, } /// Transaction structure @@ -171,6 +176,34 @@ pub struct Withdrawal { pub amount: U256, } +/// Withdrawal request structure (EIP-7002) +/// +/// Represents an execution layer triggerable withdrawal request. +#[derive(Debug, PartialEq, Eq, Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct WithdrawalRequest { + /// Address of the source of the exit + pub source_address: Address, + /// Validator public key (48 bytes) + pub validator_pubkey: FixedBytes<48>, + /// Amount of withdrawn ether in gwei + pub amount: U256, +} + +/// Consolidation request structure (EIP-7251) +/// +/// Represents a consolidation request for validator consolidation. +#[derive(Debug, PartialEq, Eq, Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct ConsolidationRequest { + /// Source address + pub source_address: Address, + /// Source public key (48 bytes) + pub source_pubkey: FixedBytes<48>, + /// Target public key (48 bytes) + pub target_pubkey: FixedBytes<48>, +} + /// Ethereum blockchain test data state #[derive(Clone, Debug, Eq, PartialEq, Deserialize, Default)] pub struct State(pub BTreeMap); @@ -280,6 +313,8 @@ pub enum ForkSpec { Osaka, /// BPO1 to BPO2 transition BPO1ToBPO2AtTime15k, + /// Amsterdam + Amsterdam, } /// Possible seal engines @@ -317,7 +352,7 @@ impl BlockHeader { impl Transaction { /// Convert Transaction to TxEnv - /// Note: The 'to' and 'sender' fields need to be provided separately in the reth model + /// Note: The 'to' and 'sender' fields need to be provided separately in TxEnv pub fn to_tx_env(&self) -> Result { // Determine transaction type let tx_type = self.transaction_type.map(|t| t.to::()).unwrap_or(0); @@ -407,7 +442,7 @@ impl BlockchainTestCase { #[cfg(test)] mod test { - use revm::primitives::address; + use primitives::address; use super::*; @@ -437,7 +472,7 @@ mod test { let result: Result = serde_json::from_str(SAMPLE); // Note: The test may fail because the sample JSON has a different structure - // than what reth expects (e.g., network is a string instead of ForkSpec enum) + // than the expected format (e.g., network is a string instead of ForkSpec enum) // This is expected as the formats differ slightly if let Err(e) = result { println!("Expected deserialization error due to format differences: {e}"); @@ -470,6 +505,7 @@ mod test { ), ("\"Osaka\"", ForkSpec::Osaka), ("\"BPO1ToBPO2AtTime15k\"", ForkSpec::BPO1ToBPO2AtTime15k), + ("\"Amsterdam\"", ForkSpec::Amsterdam), ]; for (json, expected) in fork_specs { @@ -481,7 +517,7 @@ mod test { #[test] fn test_transaction_conversion() { use crate::blockchain::Transaction; - use revm::primitives::{Bytes, U256}; + use primitives::{Bytes, U256}; let tx = Transaction { transaction_type: Some(U256::from(0)), diff --git a/crates/statetest-types/src/deserializer.rs b/crates/statetest-types/src/deserializer.rs index 8ba97e928e..a092e4c40b 100644 --- a/crates/statetest-types/src/deserializer.rs +++ b/crates/statetest-types/src/deserializer.rs @@ -1,4 +1,4 @@ -use revm::primitives::Address; +use primitives::Address; use serde::{de, Deserialize}; /// Deserializes a [string][String] as a [u64]. diff --git a/crates/statetest-types/src/env.rs b/crates/statetest-types/src/env.rs index ae99f6538a..afda9af13d 100644 --- a/crates/statetest-types/src/env.rs +++ b/crates/statetest-types/src/env.rs @@ -1,4 +1,4 @@ -use revm::primitives::{Address, B256, U256}; +use primitives::{Address, B256, U256}; use serde::Deserialize; /// Environment variables diff --git a/crates/statetest-types/src/error.rs b/crates/statetest-types/src/error.rs index 3146a2f37a..9da12f77e5 100644 --- a/crates/statetest-types/src/error.rs +++ b/crates/statetest-types/src/error.rs @@ -1,4 +1,4 @@ -use revm::primitives::B256; +use primitives::B256; use thiserror::Error; /// Errors that can occur during test setup and execution diff --git a/crates/statetest-types/src/spec.rs b/crates/statetest-types/src/spec.rs index 434b73bdcc..c94db87f59 100644 --- a/crates/statetest-types/src/spec.rs +++ b/crates/statetest-types/src/spec.rs @@ -1,4 +1,4 @@ -use revm::primitives::hardfork::SpecId; +use primitives::hardfork::SpecId; use serde::Deserialize; /// Ethereum specification names @@ -48,8 +48,10 @@ pub enum SpecName { Cancun, /// Prague hardfork (future) Prague, - /// Osaka hardfork (skipped) - Osaka, // SKIPPED + /// Osaka hardfork + Osaka, + /// Amsterdam hardfork + Amsterdam, /// Unknown or unsupported specification #[serde(other)] Unknown, @@ -75,6 +77,7 @@ impl SpecName { Self::Cancun => SpecId::CANCUN, Self::Prague => SpecId::PRAGUE, Self::Osaka => SpecId::OSAKA, + Self::Amsterdam => SpecId::AMSTERDAM, Self::ByzantiumToConstantinopleAt5 | Self::Constantinople => { panic!("Overridden with PETERSBURG") } diff --git a/crates/statetest-types/src/test.rs b/crates/statetest-types/src/test.rs index 8bc7cb9c42..72bf953c31 100644 --- a/crates/statetest-types/src/test.rs +++ b/crates/statetest-types/src/test.rs @@ -1,7 +1,5 @@ -use revm::{ - context::tx::TxEnv, - primitives::{Address, Bytes, HashMap, TxKind, B256}, -}; +use context::tx::TxEnv; +use primitives::{Address, Bytes, HashMap, TxKind, B256}; use serde::Deserialize; use crate::{ @@ -119,7 +117,7 @@ impl Test { .map(|auth_list| { auth_list .into_iter() - .map(|i| revm::context::either::Either::Left(i.into())) + .map(|i| context::either::Either::Left(i.into())) .collect::>() }) .unwrap_or_default(), diff --git a/crates/statetest-types/src/test_authorization.rs b/crates/statetest-types/src/test_authorization.rs index c71977c1d1..1dd9d175ea 100644 --- a/crates/statetest-types/src/test_authorization.rs +++ b/crates/statetest-types/src/test_authorization.rs @@ -1,6 +1,5 @@ -use revm::context_interface::transaction::SignedAuthorization; -use serde::de::Error; -use serde::{Deserialize, Deserializer, Serialize}; +use context_interface::transaction::SignedAuthorization; +use serde::{de::Error, Deserialize, Deserializer, Serialize}; /// Struct for test authorization #[derive(Debug, Clone, PartialEq, Eq, Serialize)] diff --git a/crates/statetest-types/src/test_unit.rs b/crates/statetest-types/src/test_unit.rs index d02b6f1469..ec11eb7acf 100644 --- a/crates/statetest-types/src/test_unit.rs +++ b/crates/statetest-types/src/test_unit.rs @@ -1,13 +1,10 @@ -use serde::Deserialize; -use std::collections::{BTreeMap, HashMap}; - use crate::{AccountInfo, Env, SpecName, Test, TransactionParts}; -use revm::{ - context::{block::BlockEnv, cfg::CfgEnv}, - database::CacheState, - primitives::{hardfork::SpecId, keccak256, Address, Bytes, B256}, - state::Bytecode, -}; +use context::{block::BlockEnv, cfg::CfgEnv}; +use database::CacheState; +use primitives::{hardfork::SpecId, keccak256, Address, Bytes, HashMap, B256}; +use serde::Deserialize; +use state::Bytecode; +use std::collections::BTreeMap; /// Single test unit struct #[derive(Debug, PartialEq, Eq, Deserialize)] @@ -72,11 +69,12 @@ impl TestUnit { let code_hash = keccak256(&info.code); let bytecode = Bytecode::new_raw_checked(info.code.clone()) .unwrap_or(Bytecode::new_legacy(info.code.clone())); - let acc_info = revm::state::AccountInfo { + let acc_info = state::AccountInfo { balance: info.balance, code_hash, code: Some(bytecode), nonce: info.nonce, + ..Default::default() }; cache_state.insert_account_with_storage(*address, acc_info, info.storage.clone()); } @@ -95,7 +93,7 @@ impl TestUnit { /// # Returns /// /// A configured [`BlockEnv`] ready for execution - pub fn block_env(&self, cfg: &CfgEnv) -> BlockEnv { + pub fn block_env(&self, cfg: &mut CfgEnv) -> BlockEnv { let mut block = BlockEnv { number: self.env.current_number, beneficiary: self.env.current_coinbase, @@ -113,18 +111,166 @@ impl TestUnit { }; // Handle EIP-4844 blob gas + // Use spec-aware blob fee fraction: Cancun uses 3338477, Prague/Osaka use 5007716 if let Some(current_excess_blob_gas) = self.env.current_excess_blob_gas { block.set_blob_excess_gas_and_price( current_excess_blob_gas.to(), - revm::primitives::eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN, + cfg.blob_base_fee_update_fraction(), ); } // Set default prevrandao for merge - if cfg.spec.is_enabled_in(SpecId::MERGE) && block.prevrandao.is_none() { + if cfg.spec().is_enabled_in(SpecId::MERGE) && block.prevrandao.is_none() { block.prevrandao = Some(B256::default()); } block } } + +#[cfg(test)] +mod tests { + use super::*; + use context_interface::block::calc_blob_gasprice; + use primitives::{ + eip4844::{BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN, BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE}, + U256, + }; + + /// Creates a minimal TestUnit with excess blob gas set for testing blob fee calculation + fn create_test_unit_with_excess_blob_gas(excess_blob_gas: u64) -> TestUnit { + TestUnit { + info: None, + env: Env { + current_chain_id: None, + current_coinbase: Address::ZERO, + current_difficulty: U256::ZERO, + current_gas_limit: U256::from(1_000_000u64), + current_number: U256::from(1u64), + current_timestamp: U256::from(1u64), + current_base_fee: Some(U256::from(1u64)), + previous_hash: None, + current_random: None, + current_beacon_root: None, + current_withdrawals_root: None, + current_excess_blob_gas: Some(U256::from(excess_blob_gas)), + }, + pre: HashMap::default(), + post: BTreeMap::default(), + transaction: TransactionParts { + tx_type: None, + data: vec![], + gas_limit: vec![], + gas_price: None, + nonce: U256::ZERO, + secret_key: B256::ZERO, + sender: None, + to: None, + value: vec![], + max_fee_per_gas: None, + max_priority_fee_per_gas: None, + initcodes: None, + access_lists: vec![], + authorization_list: None, + blob_versioned_hashes: vec![], + max_fee_per_blob_gas: None, + }, + out: None, + } + } + + /// Test that block_env uses the correct blob base fee update fraction for Cancun + #[test] + fn test_block_env_blob_fee_fraction_cancun() { + let unit = create_test_unit_with_excess_blob_gas(0x240000); // 2,359,296 + + let mut cfg = CfgEnv::new_with_spec(SpecId::CANCUN); + + let block = unit.block_env(&mut cfg); + + // Verify blob gas price is calculated with Cancun fraction + let blob_info = block + .blob_excess_gas_and_price + .expect("blob info should be set"); + assert_eq!(blob_info.excess_blob_gas, 0x240000); + + // Calculate expected price with Cancun fraction (3338477) + // blob_gasprice = fake_exponential(1, excess_blob_gas, BLOB_BASE_FEE_UPDATE_FRACTION) + // With excess_blob_gas=0x240000 and CANCUN fraction=3338477, price should be 2 + let expected_price = calc_blob_gasprice(0x240000, BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN); + assert_eq!(blob_info.blob_gasprice, expected_price); + assert_eq!(blob_info.blob_gasprice, 2); // With Cancun fraction, price is 2 + } + + /// Test that block_env uses the correct blob base fee update fraction for Prague + #[test] + fn test_block_env_blob_fee_fraction_prague() { + let unit = create_test_unit_with_excess_blob_gas(0x240000); // 2,359,296 + + let mut cfg = CfgEnv::new_with_spec(SpecId::PRAGUE); + + let block = unit.block_env(&mut cfg); + + // Verify blob gas price is calculated with Prague fraction + let blob_info = block + .blob_excess_gas_and_price + .expect("blob info should be set"); + assert_eq!(blob_info.excess_blob_gas, 0x240000); + + // Calculate expected price with Prague fraction (5007716) + // With excess_blob_gas=0x240000 and PRAGUE fraction=5007716, price should be 1 + let expected_price = calc_blob_gasprice(0x240000, BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE); + assert_eq!(blob_info.blob_gasprice, expected_price); + assert_eq!(blob_info.blob_gasprice, 1); // With Prague fraction, price is 1 + } + + /// Test that block_env uses the correct blob base fee update fraction for Osaka + #[test] + fn test_block_env_blob_fee_fraction_osaka() { + let unit = create_test_unit_with_excess_blob_gas(0x240000); // 2,359,296 + + let mut cfg = CfgEnv::new_with_spec(SpecId::OSAKA); + + let block = unit.block_env(&mut cfg); + + // Osaka should use Prague fraction (same as Prague) + let blob_info = block + .blob_excess_gas_and_price + .expect("blob info should be set"); + let expected_price = calc_blob_gasprice(0x240000, BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE); + assert_eq!(blob_info.blob_gasprice, expected_price); + assert_eq!(blob_info.blob_gasprice, 1); // With Prague fraction, price is 1 + } + + /// Test that demonstrates the bug scenario from IMPLEMENTATION_PROMPT.md + /// With excess_blob_gas=0x240000 and maxFeePerBlobGas=0x01: + /// - Cancun fraction (3338477): blob_price = 2, tx FAILS (insufficient fee) + /// - Prague fraction (5007716): blob_price = 1, tx SUCCEEDS + #[test] + fn test_blob_fee_difference_affects_tx_validity() { + let excess_blob_gas = 0x240000u64; + + // Calculate prices with both fractions + let cancun_price = + calc_blob_gasprice(excess_blob_gas, BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN); + let prague_price = + calc_blob_gasprice(excess_blob_gas, BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE); + + // Verify the prices are different + assert_eq!(cancun_price, 2, "Cancun blob price should be 2"); + assert_eq!(prague_price, 1, "Prague blob price should be 1"); + + // With maxFeePerBlobGas=1: + // - Cancun: 1 < 2, tx would fail with insufficient fee + // - Prague: 1 >= 1, tx would succeed + let max_fee_per_blob_gas = 1u128; + assert!( + max_fee_per_blob_gas < cancun_price, + "Tx should fail with Cancun fraction" + ); + assert!( + max_fee_per_blob_gas >= prague_price, + "Tx should succeed with Prague fraction" + ); + } +} diff --git a/crates/statetest-types/src/transaction.rs b/crates/statetest-types/src/transaction.rs index 81a98a30a7..dc216d46ad 100644 --- a/crates/statetest-types/src/transaction.rs +++ b/crates/statetest-types/src/transaction.rs @@ -1,9 +1,7 @@ use crate::{deserializer::deserialize_maybe_empty, TestAuthorization}; -use revm::{ - context::TransactionType, - context_interface::transaction::AccessList, - primitives::{Address, Bytes, B256, U256}, -}; +use context::TransactionType; +use context_interface::transaction::AccessList; +use primitives::{Address, Bytes, B256, U256}; use serde::{Deserialize, Serialize}; /// Transaction parts. diff --git a/crates/statetest-types/src/utils.rs b/crates/statetest-types/src/utils.rs index 3ecc89594a..f9c30dd1cf 100644 --- a/crates/statetest-types/src/utils.rs +++ b/crates/statetest-types/src/utils.rs @@ -1,5 +1,5 @@ use k256::ecdsa::SigningKey; -use revm::primitives::Address; +use primitives::Address; /// Recover the address from a private key ([SigningKey]). pub fn recover_address(private_key: &[u8]) -> Option
{ @@ -11,7 +11,7 @@ pub fn recover_address(private_key: &[u8]) -> Option
{ #[cfg(test)] mod tests { use super::*; - use revm::primitives::{address, hex}; + use primitives::{address, hex}; #[test] fn sanity_test() { diff --git a/examples/block_traces/src/main.rs b/examples/block_traces/src/main.rs index af1b3f95e9..29e2667eca 100644 --- a/examples/block_traces/src/main.rs +++ b/examples/block_traces/src/main.rs @@ -15,13 +15,12 @@ use revm::{ primitives::{TxKind, U256}, Context, MainBuilder, MainContext, }; -use std::fs::create_dir_all; -use std::fs::OpenOptions; -use std::io::BufWriter; -use std::io::Write; -use std::sync::Arc; -use std::sync::Mutex; -use std::time::Instant; +use std::{ + fs::{create_dir_all, OpenOptions}, + io::{BufWriter, Write}, + sync::{Arc, Mutex}, + time::Instant, +}; struct FlushWriter { writer: Arc>>, @@ -92,16 +91,7 @@ async fn main() -> anyhow::Result<()> { c.chain_id = chain_id; }); - let write = OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .open("traces/0.json"); - let inner = Arc::new(Mutex::new(BufWriter::new( - write.expect("Failed to open file"), - ))); - let writer = FlushWriter::new(Arc::clone(&inner)); - let mut evm = ctx.build_mainnet_with_inspector(TracerEip3155::new(Box::new(writer))); + let mut evm = ctx.build_mainnet_with_inspector(TracerEip3155::new(Box::new(std::io::sink()))); let txs = block.transactions.len(); println!("Found {txs} transactions."); @@ -109,9 +99,6 @@ async fn main() -> anyhow::Result<()> { let console_bar = Arc::new(ProgressBar::new(txs as u64)); let start = Instant::now(); - // Create the traces directory if it doesn't exist - std::fs::create_dir_all("traces").expect("Failed to create traces directory"); - // Fill in CfgEnv let BlockTransactions::Full(transactions) = block.transactions else { panic!("Wrong transaction type") diff --git a/examples/cheatcode_inspector/src/main.rs b/examples/cheatcode_inspector/src/main.rs index 2d35bdcce5..0a911efb64 100644 --- a/examples/cheatcode_inspector/src/main.rs +++ b/examples/cheatcode_inspector/src/main.rs @@ -8,7 +8,7 @@ use revm::{ context::{ - journaled_state::{AccountInfoLoad, JournalLoadError}, + journaled_state::{account::JournaledAccount, AccountInfoLoad, JournalLoadError}, result::InvalidTransaction, BlockEnv, Cfg, CfgEnv, ContextTr, Evm, LocalContext, TxEnv, }, @@ -60,6 +60,7 @@ impl Backend { impl JournalTr for Backend { type Database = InMemoryDB; type State = EvmState; + type JournaledAccount<'a> = JournaledAccount<'a, InMemoryDB, JournalEntry>; fn new(database: InMemoryDB) -> Self { Self::new(SpecId::default(), database) @@ -102,21 +103,25 @@ impl JournalTr for Backend { self.journaled_state.log(log) } + fn logs(&self) -> &[Log] { + self.journaled_state.logs() + } + fn selfdestruct( &mut self, address: Address, target: Address, - ) -> Result, Infallible> { - self.journaled_state.selfdestruct(address, target) + skip_cold_load: bool, + ) -> Result, JournalLoadError> { + self.journaled_state + .selfdestruct(address, target, skip_cold_load) } - fn warm_account_and_storage( + fn warm_access_list( &mut self, - address: Address, - storage_keys: impl IntoIterator, - ) -> Result<(), ::Error> { - self.journaled_state - .warm_account_and_storage(address, storage_keys) + access_list: revm::primitives::HashMap>, + ) { + self.journaled_state.warm_access_list(access_list); } fn warm_coinbase_account(&mut self, address: Address) { @@ -157,15 +162,15 @@ impl JournalTr for Backend { self.journaled_state.transfer_loaded(from, to, balance) } - fn load_account(&mut self, address: Address) -> Result, Infallible> { + fn load_account(&mut self, address: Address) -> Result, Infallible> { self.journaled_state.load_account(address) } - fn load_account_code( + fn load_account_with_code( &mut self, address: Address, - ) -> Result, Infallible> { - self.journaled_state.load_account_code(address) + ) -> Result, Infallible> { + self.journaled_state.load_account_with_code(address) } fn load_account_delegated( @@ -289,22 +294,36 @@ impl JournalTr for Backend { .sstore_skip_cold_load(address, key, value, skip_cold_load) } + fn load_account_mut_skip_cold_load( + &mut self, + address: Address, + skip_cold_load: bool, + ) -> Result>, Infallible> { + self.journaled_state + .load_account_mut_skip_cold_load(address, skip_cold_load) + } + fn load_account_info_skip_cold_load( &mut self, address: Address, load_code: bool, skip_cold_load: bool, - ) -> Result, JournalLoadError<::Error>> { + ) -> Result, JournalLoadError> { self.journaled_state .load_account_info_skip_cold_load(address, load_code, skip_cold_load) } -} -impl JournalExt for Backend { - fn logs(&self) -> &[Log] { - self.journaled_state.logs() + fn load_account_mut_optional_code( + &mut self, + address: Address, + load_code: bool, + ) -> Result>, Infallible> { + self.journaled_state + .load_account_mut_optional_code(address, load_code) } +} +impl JournalExt for Backend { fn journal(&self) -> &[JournalEntry] { self.journaled_state.journal() } diff --git a/examples/contract_deployment/src/main.rs b/examples/contract_deployment/src/main.rs index 02bf76e96b..0fe18462b0 100644 --- a/examples/contract_deployment/src/main.rs +++ b/examples/contract_deployment/src/main.rs @@ -55,7 +55,7 @@ fn main() -> anyhow::Result<()> { let ref_tx = evm.transact_commit( TxEnv::builder() .kind(TxKind::Create) - .data(bytecode.clone()) + .data(bytecode) .build() .unwrap(), )?; diff --git a/examples/custom_precompile_journal/src/custom_evm.rs b/examples/custom_precompile_journal/src/custom_evm.rs index 47b1f59344..3974d6e490 100644 --- a/examples/custom_precompile_journal/src/custom_evm.rs +++ b/examples/custom_precompile_journal/src/custom_evm.rs @@ -150,3 +150,256 @@ where self.0.all_mut_inspector() } } + +#[cfg(test)] +mod tests { + use crate::{custom_evm::CustomEvm, precompile_provider::CUSTOM_PRECOMPILE_ADDRESS}; + use revm::{ + context::{Context, ContextSetters, TxEnv}, + context_interface::{result::EVMError, ContextTr}, + database::InMemoryDB, + handler::{Handler, MainnetHandler}, + inspector::{Inspector, JournalExt}, + interpreter::interpreter::EthInterpreter, + primitives::{address, Log, TxKind, U256}, + state::AccountInfo, + MainContext, + }; + use std::vec::Vec; + + /// Custom inspector that captures logs + #[derive(Debug, Default)] + struct LogCapturingInspector { + captured_logs: Vec, + } + + impl LogCapturingInspector { + fn new() -> Self { + Self { + captured_logs: Vec::new(), + } + } + + fn logs(&self) -> &[Log] { + &self.captured_logs + } + } + + impl Inspector for LogCapturingInspector + where + CTX: ContextTr + ContextSetters, + { + fn log(&mut self, _context: &mut CTX, log: Log) { + // Capture logs as they're created + self.captured_logs.push(log); + } + } + + #[test] + fn test_custom_precompile_creates_log() { + // Setup initial accounts + let user_address = address!("0000000000000000000000000000000000000001"); + let mut db = InMemoryDB::default(); + + // Give the user some ETH for gas + let user_balance = U256::from(10).pow(U256::from(18)); // 1 ETH + db.insert_account_info( + user_address, + AccountInfo { + balance: user_balance, + nonce: 0, + code_hash: revm::primitives::KECCAK_EMPTY, + code: None, + account_id: None, + }, + ); + + // Give the precompile some initial balance for transfers + db.insert_account_info( + CUSTOM_PRECOMPILE_ADDRESS, + AccountInfo { + balance: U256::from(1000), // 1000 wei + nonce: 0, + code_hash: revm::primitives::KECCAK_EMPTY, + code: None, + account_id: None, + }, + ); + + // Create custom EVM with log capturing inspector + let context = Context::mainnet().with_db(db); + let inspector = LogCapturingInspector::new(); + let mut evm = CustomEvm::new(context, inspector); + + // Write value 42 to storage (this should create a log) + let storage_value = U256::from(42); + evm.0.ctx.set_tx( + TxEnv::builder() + .caller(user_address) + .kind(TxKind::Call(CUSTOM_PRECOMPILE_ADDRESS)) + .data(storage_value.to_be_bytes_vec().into()) + .gas_limit(100_000) + .build() + .unwrap(), + ); + + let result: Result< + _, + EVMError, + > = MainnetHandler::default().run(&mut evm); + + // Verify transaction succeeded + assert!( + result.is_ok(), + "Transaction should succeed, got: {result:?}" + ); + + match result.unwrap() { + revm::context::result::ExecutionResult::Success { logs, .. } => { + // Transaction succeeded, now check logs from execution result + // Note: Inspector might not be called for precompile logs, + // so we check the execution result logs instead + + // Also check inspector logs (though they may be empty) + let inspector_logs = evm.0.inspector.logs(); + + // Combine both sources - use execution result logs if inspector logs are empty + let all_logs = if inspector_logs.is_empty() { + &logs + } else { + inspector_logs + }; + + // Verify that at least one log was captured + assert!( + !all_logs.is_empty(), + "Should have captured at least one log (either from inspector or execution result)" + ); + + // Find the log from our custom precompile + let precompile_log = all_logs + .iter() + .find(|log| log.address == CUSTOM_PRECOMPILE_ADDRESS); + + assert!( + precompile_log.is_some(), + "Should have a log from the custom precompile. Found {} total logs", + all_logs.len() + ); + + let log = precompile_log.unwrap(); + + // Verify log structure + assert_eq!(log.address, CUSTOM_PRECOMPILE_ADDRESS); + assert_eq!(log.data.topics().len(), 2, "Should have 2 topics"); + + // Topic 1 should be the caller address (left-padded to 32 bytes) + let topic1 = log.data.topics()[1]; + let mut expected_caller_bytes = [0u8; 32]; + expected_caller_bytes[12..32].copy_from_slice(user_address.as_slice()); + let expected_caller_topic = revm::primitives::B256::from(expected_caller_bytes); + assert_eq!( + topic1, expected_caller_topic, + "Second topic should be caller address" + ); + + // Data should contain the value that was written (42) + let log_data_bytes = &log.data.data; + let logged_value = U256::from_be_slice(log_data_bytes); + assert_eq!( + logged_value, + U256::from(42), + "Log data should contain the written value (42)" + ); + + println!("✅ Test passed! Log was successfully created and captured"); + println!(" Log address: {}", log.address); + println!(" Number of topics: {}", log.data.topics().len()); + println!(" Logged value: {logged_value}"); + println!( + " Inspector logs: {}, Execution result logs: {}", + inspector_logs.len(), + logs.len() + ); + } + revm::context::result::ExecutionResult::Revert { .. } => { + panic!("Transaction reverted unexpectedly"); + } + revm::context::result::ExecutionResult::Halt { reason, .. } => { + panic!("Transaction halted unexpectedly: {reason:?}"); + } + } + } + + #[test] + fn test_read_operation_does_not_create_log() { + // Setup initial accounts + let user_address = address!("0000000000000000000000000000000000000001"); + let mut db = InMemoryDB::default(); + + // Give the user some ETH for gas + let user_balance = U256::from(10).pow(U256::from(18)); // 1 ETH + db.insert_account_info( + user_address, + AccountInfo { + balance: user_balance, + nonce: 0, + code_hash: revm::primitives::KECCAK_EMPTY, + code: None, + account_id: None, + }, + ); + + // Create custom EVM with log capturing inspector + let context = Context::mainnet().with_db(db); + let inspector = LogCapturingInspector::new(); + let mut evm = CustomEvm::new(context, inspector); + + // Read from storage (empty input - should not create a log) + evm.0.ctx.set_tx( + TxEnv::builder() + .caller(user_address) + .kind(TxKind::Call(CUSTOM_PRECOMPILE_ADDRESS)) + .data(revm::primitives::Bytes::new()) // Empty data for read operation + .gas_limit(100_000) + .build() + .unwrap(), + ); + + let result: Result< + _, + EVMError, + > = MainnetHandler::default().run(&mut evm); + + // Verify transaction succeeded + assert!( + result.is_ok(), + "Transaction should succeed, got: {result:?}" + ); + + match result.unwrap() { + revm::context::result::ExecutionResult::Success { .. } => { + // Transaction succeeded, check that no logs were created + let logs = evm.0.inspector.logs(); + + // Verify that no logs from the precompile were captured + let precompile_log = logs + .iter() + .find(|log| log.address == CUSTOM_PRECOMPILE_ADDRESS); + + assert!( + precompile_log.is_none(), + "Read operation should not create any logs" + ); + + println!("✅ Test passed! Read operation correctly did not create any logs"); + } + revm::context::result::ExecutionResult::Revert { .. } => { + panic!("Transaction reverted unexpectedly"); + } + revm::context::result::ExecutionResult::Halt { reason, .. } => { + panic!("Transaction halted unexpectedly: {reason:?}"); + } + } + } +} diff --git a/examples/custom_precompile_journal/src/main.rs b/examples/custom_precompile_journal/src/main.rs index 6a4595775c..1cf2189498 100644 --- a/examples/custom_precompile_journal/src/main.rs +++ b/examples/custom_precompile_journal/src/main.rs @@ -39,6 +39,7 @@ fn main() -> anyhow::Result<()> { nonce: 0, code_hash: revm::primitives::KECCAK_EMPTY, code: None, + ..Default::default() }, ); @@ -50,6 +51,7 @@ fn main() -> anyhow::Result<()> { nonce: 0, code_hash: revm::primitives::KECCAK_EMPTY, code: None, + ..Default::default() }, ); diff --git a/examples/custom_precompile_journal/src/precompile_provider.rs b/examples/custom_precompile_journal/src/precompile_provider.rs index 7e4214ff66..1ffef4454b 100644 --- a/examples/custom_precompile_journal/src/precompile_provider.rs +++ b/examples/custom_precompile_journal/src/precompile_provider.rs @@ -6,10 +6,9 @@ use revm::{ handler::{EthPrecompiles, PrecompileProvider}, interpreter::{CallInputs, Gas, InstructionResult, InterpreterResult}, precompile::{PrecompileError, PrecompileOutput, PrecompileResult}, - primitives::{address, hardfork::SpecId, Address, Bytes, U256}, + primitives::{address, hardfork::SpecId, Address, Bytes, Log, B256, U256}, }; -use std::boxed::Box; -use std::string::String; +use std::{boxed::Box, string::String}; // Define our custom precompile address pub const CUSTOM_PRECOMPILE_ADDRESS: Address = address!("0000000000000000000000000000000000000100"); @@ -105,7 +104,7 @@ fn run_custom_precompile( // Write storage operation handle_write_storage(context, &input_bytes, inputs.gas_limit) } else { - Err(PrecompileError::Other("Invalid input length".to_string())) + Err(PrecompileError::Other("Invalid input length".into())) }; match result { @@ -158,7 +157,7 @@ fn handle_read_storage(context: &mut CTX, gas_limit: u64) -> Pre let value = context .journal_mut() .sload(CUSTOM_PRECOMPILE_ADDRESS, STORAGE_KEY) - .map_err(|e| PrecompileError::Other(format!("Storage read failed: {e:?}")))? + .map_err(|e| PrecompileError::Other(format!("Storage read failed: {e:?}").into()))? .data; // Return the value as output @@ -189,7 +188,7 @@ fn handle_write_storage( context .journal_mut() .sstore(CUSTOM_PRECOMPILE_ADDRESS, STORAGE_KEY, value) - .map_err(|e| PrecompileError::Other(format!("Storage write failed: {e:?}")))?; + .map_err(|e| PrecompileError::Other(format!("Storage write failed: {e:?}").into()))?; // Get the caller address let caller = context.tx().caller(); @@ -199,18 +198,43 @@ fn handle_write_storage( context .journal_mut() .balance_incr(CUSTOM_PRECOMPILE_ADDRESS, U256::from(1)) - .map_err(|e| PrecompileError::Other(format!("Balance increment failed: {e:?}")))?; + .map_err(|e| PrecompileError::Other(format!("Balance increment failed: {e:?}").into()))?; // Then transfer to caller let transfer_result = context .journal_mut() .transfer(CUSTOM_PRECOMPILE_ADDRESS, caller, U256::from(1)) - .map_err(|e| PrecompileError::Other(format!("Transfer failed: {e:?}")))?; + .map_err(|e| PrecompileError::Other(format!("Transfer failed: {e:?}").into()))?; if let Some(error) = transfer_result { - return Err(PrecompileError::Other(format!("Transfer error: {error:?}"))); + return Err(PrecompileError::Other( + format!("Transfer error: {error:?}").into(), + )); } + // Create a log to record the storage write operation + // Topic 0: keccak256("StorageWritten(address,uint256)") + let topic0 = B256::from_slice(&[ + 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, + 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, + 0xde, 0xf0, + ]); + // Topic 1: caller address (indexed) - left-padded to 32 bytes + let mut topic1_bytes = [0u8; 32]; + topic1_bytes[12..32].copy_from_slice(caller.as_slice()); + let topic1 = B256::from(topic1_bytes); + // Data: the value that was written + let log_data = value.to_be_bytes_vec(); + + let log = Log::new( + CUSTOM_PRECOMPILE_ADDRESS, + vec![topic0, topic1], + log_data.into(), + ) + .expect("Failed to create log"); + + context.journal_mut().log(log); + // Return success with empty output Ok(PrecompileOutput::new(BASE_GAS + SSTORE_GAS, Bytes::new())) } diff --git a/examples/database_components/src/block_hash.rs b/examples/database_components/src/block_hash.rs index 449929d204..fe7d7213b5 100644 --- a/examples/database_components/src/block_hash.rs +++ b/examples/database_components/src/block_hash.rs @@ -1,7 +1,7 @@ //! BlockHash database component from [`revm::Database`] use auto_impl::auto_impl; -use core::{error::Error as StdError, ops::Deref}; +use core::ops::Deref; use revm::primitives::B256; use std::sync::Arc; @@ -11,7 +11,7 @@ use std::sync::Arc; #[auto_impl(&mut, Box)] pub trait BlockHash { /// Error type for block hash operations - type Error: StdError; + type Error: core::error::Error + Send + Sync + 'static; /// Gets block hash by block number. fn block_hash(&mut self, number: u64) -> Result; @@ -23,7 +23,7 @@ pub trait BlockHash { #[auto_impl(&, &mut, Box, Rc, Arc)] pub trait BlockHashRef { /// Error type for block hash operations - type Error: StdError; + type Error: core::error::Error + Send + Sync + 'static; /// Gets block hash by block number. fn block_hash(&self, number: u64) -> Result; diff --git a/examples/database_components/src/lib.rs b/examples/database_components/src/lib.rs index c683d19f0e..276bc32587 100644 --- a/examples/database_components/src/lib.rs +++ b/examples/database_components/src/lib.rs @@ -28,7 +28,10 @@ pub struct DatabaseComponents { /// Error type for database component operations. /// Wraps errors from both state and block hash components. #[derive(Debug, thiserror::Error)] -pub enum DatabaseComponentError { +pub enum DatabaseComponentError< + SE: core::error::Error + Send + Sync + 'static, + BHE: core::error::Error + Send + Sync + 'static, +> { /// Error from state component operations #[error(transparent)] State(SE), @@ -37,7 +40,19 @@ pub enum DatabaseComponentError { BlockHash(BHE), } -impl DBErrorMarker for DatabaseComponentError {} +impl< + SE: core::error::Error + Send + Sync + 'static, + BHE: core::error::Error + Send + Sync + 'static, + > DBErrorMarker for DatabaseComponentError +{ +} + +unsafe impl< + SE: core::error::Error + Send + Sync + 'static, + BHE: core::error::Error + Send + Sync + 'static, + > Send for DatabaseComponentError +{ +} impl Database for DatabaseComponents { type Error = DatabaseComponentError; diff --git a/examples/database_components/src/state.rs b/examples/database_components/src/state.rs index 35f8d40ed8..b9dd75f636 100644 --- a/examples/database_components/src/state.rs +++ b/examples/database_components/src/state.rs @@ -6,7 +6,7 @@ use revm::{ primitives::{Address, StorageKey, StorageValue, B256}, state::{AccountInfo, Bytecode}, }; -use std::{error::Error as StdError, sync::Arc}; +use std::sync::Arc; /// Trait for mutable access to state data including accounts, code, and storage. /// This is typically used for database implementations that may modify state @@ -14,7 +14,7 @@ use std::{error::Error as StdError, sync::Arc}; #[auto_impl(&mut, Box)] pub trait State { /// Error type for state operations - type Error: StdError; + type Error: core::error::Error + Send + Sync + 'static; /// Gets basic account information. fn basic(&mut self, address: Address) -> Result, Self::Error>; @@ -33,7 +33,7 @@ pub trait State { #[auto_impl(&, &mut, Box, Rc, Arc)] pub trait StateRef { /// Error type for state operations - type Error: StdError; + type Error: core::error::Error + Send + Sync + 'static; /// Gets basic account information. fn basic(&self, address: Address) -> Result, Self::Error>; diff --git a/examples/erc20_gas/src/handler.rs b/examples/erc20_gas/src/handler.rs index 230f2d161c..29b673aad4 100644 --- a/examples/erc20_gas/src/handler.rs +++ b/examples/erc20_gas/src/handler.rs @@ -1,8 +1,8 @@ use revm::{ - context::Cfg, + context::{journaled_state::account::JournaledAccountTr, Cfg}, context_interface::{result::HaltReason, Block, ContextTr, JournalTr, Transaction}, handler::{ - pre_execution::{calculate_caller_fee, validate_account_nonce_and_code}, + pre_execution::{calculate_caller_fee, validate_account_nonce_and_code_with_components}, EvmTr, EvmTrError, FrameResult, FrameTr, Handler, }, interpreter::interpreter_action::FrameInit, @@ -49,24 +49,23 @@ where let (block, tx, cfg, journal, _, _) = evm.ctx_mut().all_mut(); // load TOKEN contract - journal.load_account(TOKEN)?.data.mark_touch(); + journal.load_account_mut(TOKEN)?.touch(); // Load caller's account. - let caller_account = journal.load_account_code(tx.caller())?.data; + let mut caller_account = journal.load_account_with_code_mut(tx.caller())?; - validate_account_nonce_and_code( - &mut caller_account.info, - tx.nonce(), - cfg.is_eip3607_disabled(), - cfg.is_nonce_check_disabled(), - )?; + validate_account_nonce_and_code_with_components(&caller_account.account().info, tx, cfg)?; // make changes to the account. Account balance stays the same - caller_account - .caller_initial_modification(caller_account.info.balance, tx.kind().is_call()); + caller_account.touch(); + if tx.kind().is_call() { + caller_account.bump_nonce(); + } let account_balance_slot = erc_address_storage(tx.caller()); + drop(caller_account); // Drop caller_account to avoid borrow checker issues. + // load account balance let account_balance = journal.sload(TOKEN, account_balance_slot)?.data; diff --git a/examples/erc20_gas/src/main.rs b/examples/erc20_gas/src/main.rs index 1f16c47f9a..8724cdc038 100644 --- a/examples/erc20_gas/src/main.rs +++ b/examples/erc20_gas/src/main.rs @@ -9,12 +9,10 @@ use alloy_sol_types::SolValue; use anyhow::Result; use exec::transact_erc20evm_commit; use revm::{ - context::TxEnv, + context::{CfgEnv, TxEnv}, database::{AlloyDB, BlockId, CacheDB}, database_interface::WrapDatabaseAsync, - primitives::{ - address, hardfork::SpecId, keccak256, Address, StorageValue, TxKind, KECCAK_EMPTY, U256, - }, + primitives::{address, hardfork::SpecId, keccak256, Address, StorageValue, TxKind, U256}, state::AccountInfo, Context, Database, MainBuilder, MainContext, }; @@ -57,10 +55,8 @@ async fn main() -> Result<()> { cache_db.insert_account_info( account, AccountInfo { - nonce: 0, balance: hundred_tokens * U256::from(2), - code_hash: KECCAK_EMPTY, - code: None, + ..Default::default() }, ); @@ -85,9 +81,7 @@ fn balance_of(address: Address, alloy_db: &mut AlloyCacheDB) -> Result Result<()> { let mut ctx = Context::mainnet() .with_db(cache_db) - .modify_cfg_chained(|cfg| { - cfg.spec = SpecId::CANCUN; - }) + .with_cfg(CfgEnv::new_with_spec(SpecId::CANCUN)) .with_tx( TxEnv::builder() .caller(from) diff --git a/examples/my_evm/README.md b/examples/my_evm/README.md index 7b0a852968..58363aab6e 100644 --- a/examples/my_evm/README.md +++ b/examples/my_evm/README.md @@ -68,7 +68,7 @@ let _res = my_evm.inspect_commit_replay(); ``` ### [`revm::SystemCallEvm`] -Allows executing system transaction, only input needed is system contract add address and input +Allows executing a system transaction. Only input needed is system contract address and input. Validation and pre-execution and most of post execution phases of ordinary transact flow will be skipped. System calls are needed for inserting of fetching data on pre or post block state. diff --git a/examples/uniswap_v2_usdc_swap/src/main.rs b/examples/uniswap_v2_usdc_swap/src/main.rs index 4b374ab537..2315698ef7 100644 --- a/examples/uniswap_v2_usdc_swap/src/main.rs +++ b/examples/uniswap_v2_usdc_swap/src/main.rs @@ -10,7 +10,7 @@ use revm::{ context_interface::result::{ExecutionResult, Output}, database::{AlloyDB, CacheDB}, database_interface::WrapDatabaseAsync, - primitives::{address, keccak256, Address, Bytes, StorageKey, TxKind, KECCAK_EMPTY, U256}, + primitives::{address, keccak256, Address, Bytes, StorageKey, TxKind, U256}, state::AccountInfo, Context, ExecuteCommitEvm, ExecuteEvm, MainBuilder, MainContext, }; @@ -44,10 +44,8 @@ async fn main() -> Result<()> { .unwrap(); let acc_info = AccountInfo { - nonce: 0_u64, balance: one_ether, - code_hash: KECCAK_EMPTY, - code: None, + ..Default::default() }; cache_db.insert_account_info(account, acc_info); diff --git a/scripts/publish.sh b/scripts/publish.sh index d0ac3c4a24..3443a5b235 100755 --- a/scripts/publish.sh +++ b/scripts/publish.sh @@ -14,7 +14,7 @@ cargo publish --package revm-database cargo publish --package revm-context cargo publish --package revm-handler cargo publish --package revm-inspector -cargo publish --package revm cargo publish --package revm-statetest-types +cargo publish --package revm cargo publish --package revme cargo publish --package op-revm diff --git a/scripts/run-tests.sh b/scripts/run-tests.sh index 4cc64460e5..b9aa17480f 100755 --- a/scripts/run-tests.sh +++ b/scripts/run-tests.sh @@ -31,9 +31,12 @@ LEGACY_REPO_URL="https://github.com/ethereum/legacytests.git" # Print usage information and exit usage() { - echo "Usage: $0 [clean] [runner] [profile] [target]" + echo "Usage: $0 [clean] [--keep-going] [runner] [profile] [target]" echo "" - echo "Arguments (after optional 'clean'):" + echo "Flags (can be specified before or after 'clean'):" + echo " --keep-going Continue running tests even after failures." + echo "" + echo "Arguments (after optional 'clean' and '--keep-going'):" echo " runner (Optional) Rust runner command. Must be either 'cargo' or 'cross'. Defaults to 'cargo'." echo " profile (Optional) Rust profile to use. Defaults to 'debug' if not provided." echo " target (Optional) Rust target. Only used if provided." @@ -45,14 +48,17 @@ usage() { echo " $0 release" echo " Uses runner 'cargo', profile 'release', and no target." echo "" + echo " $0 --keep-going release" + echo " Uses runner 'cargo', profile 'release', and keeps going on test failures." + echo "" echo " $0 release x86-win" echo " Uses runner 'cargo', profile 'release', with target 'x86-win'." echo "" echo " $0 clean" echo " Cleans fixtures then uses runner 'cargo', profile 'debug', and no target." echo "" - echo " $0 clean cross release x86-win" - echo " Cleans fixtures then uses runner 'cross', profile 'release', and target 'x86-win'." + echo " $0 clean --keep-going cross release x86-win" + echo " Cleans fixtures then uses runner 'cross', profile 'release', target 'x86-win', and keeps going on failures." exit 1 } @@ -95,20 +101,25 @@ download_and_extract() { # This is needed because when we extract the tar, it is placed under an # unnecessary "fixtures/" directory. tar -xzf "${FIXTURES_DIR}/${tar_file}" --strip-components=1 -C "$target_dir" + + # Remove the tar file + rm "${FIXTURES_DIR}/${tar_file}" + + # remove all unused folders + find "$target_dir" -depth -name blockchain_tests_engine -exec rm -rf {} \; + find "$target_dir" -depth -name blockchain_tests_engine_x -exec rm -rf {} \; + find "$target_dir" -depth -name blockchain_tests_sync -exec rm -rf {} \; } # Download all fixtures download_fixtures() { echo "Creating fixtures directory structure..." mkdir -p "$MAIN_STABLE_DIR" "$MAIN_DEVELOP_DIR" "$DEVNET_DIR" "$LEGACY_DIR" - + download_and_extract "$MAIN_STABLE_DIR" "$MAIN_STABLE_TAR" "main stable" "$MAIN_VERSION" download_and_extract "$MAIN_DEVELOP_DIR" "$MAIN_DEVELOP_TAR" "main develop" "$MAIN_VERSION" download_and_extract "$DEVNET_DIR" "$DEVNET_TAR" "devnet" "$DEVNET_VERSION" - echo "Cleaning up tar files..." - rm "${FIXTURES_DIR}/${MAIN_STABLE_TAR}" "${FIXTURES_DIR}/${MAIN_DEVELOP_TAR}" "${FIXTURES_DIR}/${DEVNET_TAR}" - # Clone legacytests repository echo "Cloning legacytests repository..." git clone --depth 1 "$LEGACY_REPO_URL" "$LEGACY_DIR" @@ -140,33 +151,51 @@ build_cargo_options() { # Run tests for each set of fixtures using the chosen runner. run_tests() { echo "Running main stable statetests..." - $RUST_RUNNER run $CARGO_OPTS -p revme -- statetest "$MAIN_STABLE_DIR/state_tests" + $RUST_RUNNER run $CARGO_OPTS -p revme -- statetest $KEEP_GOING_FLAG "$MAIN_STABLE_DIR/state_tests" echo "Running main develop statetests..." - $RUST_RUNNER run $CARGO_OPTS -p revme -- statetest "$MAIN_DEVELOP_DIR/state_tests" + $RUST_RUNNER run $CARGO_OPTS -p revme -- statetest $KEEP_GOING_FLAG "$MAIN_DEVELOP_DIR/state_tests" echo "Running devnet statetests..." - $RUST_RUNNER run $CARGO_OPTS -p revme -- statetest "$DEVNET_DIR/state_tests" + $RUST_RUNNER run $CARGO_OPTS -p revme -- statetest $KEEP_GOING_FLAG "$DEVNET_DIR/state_tests" + + echo "Running legacy Cancun tests..." + $RUST_RUNNER run $CARGO_OPTS -p revme -- statetest $KEEP_GOING_FLAG "$LEGACY_DIR/Cancun/GeneralStateTests" - echo "Running legacy tests..." - $RUST_RUNNER run $CARGO_OPTS -p revme -- statetest "$LEGACY_DIR/Cancun/GeneralStateTests" + echo "Running legacy Constantinople tests..." + $RUST_RUNNER run $CARGO_OPTS -p revme -- statetest $KEEP_GOING_FLAG "$LEGACY_DIR/Constantinople/GeneralStateTests" echo "Running main develop blockchain tests..." - $RUST_RUNNER run $CARGO_OPTS -p revme -- btest "$MAIN_DEVELOP_DIR/blockchain_tests" + $RUST_RUNNER run $CARGO_OPTS -p revme -- btest $KEEP_GOING_FLAG "$MAIN_DEVELOP_DIR/blockchain_tests" echo "Running main stable blockchain tests..." - $RUST_RUNNER run $CARGO_OPTS -p revme -- btest "$MAIN_STABLE_DIR/blockchain_tests" + $RUST_RUNNER run $CARGO_OPTS -p revme -- btest $KEEP_GOING_FLAG "$MAIN_STABLE_DIR/blockchain_tests" } ############################## # Main logic -# If the first argument is "clean", perform cleaning and download fixtures. -if [ "$1" = "clean" ]; then - clean - download_fixtures - shift -else +# Initialize flags +KEEP_GOING_FLAG="" +DID_CLEAN=false + +# Process "clean" and "--keep-going" flags +while true; do + if [ "$1" = "clean" ]; then + clean + download_fixtures + DID_CLEAN=true + shift + elif [ "$1" = "--keep-going" ]; then + KEEP_GOING_FLAG="--keep-going" + shift + else + break + fi +done + +# If no clean was specified, check for existing fixtures +if [ "$DID_CLEAN" = false ]; then if check_fixtures; then echo "Using existing test fixtures." else @@ -176,7 +205,7 @@ else fi # Argument parsing for runner, profile, target. -# Expected order (after optional clean): [runner] [profile] [target] +# Expected order (after optional clean and --keep-going): [runner] [profile] [target] # If the first argument is "cargo" or "cross", then it is the runner. # Otherwise, runner defaults to "cargo", and the arguments are profile and target. if [ "$#" -eq 0 ]; then