diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 0000000000..b44d4966f4 --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,5 @@ +# Security Policy + +## Reporting a Vulnerability + +For any security questions or findings, please reach out to me directly via email at [dragan0rakita@gmail.com](mailto:dragan0rakita@gmail.com) or contact me on Keybase under the username @draganrakita. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9eb02638d6..4c003b11bd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,22 +31,23 @@ jobs: - run: cargo test --workspace ${{ matrix.flags }} check-no-std: - name: check no_std ${{ matrix.features }} + name: check no_std ${{ matrix.target }} ${{ matrix.features }} runs-on: ubuntu-latest timeout-minutes: 30 strategy: fail-fast: false matrix: - features: ["", "kzg-rs"] + target: ["riscv32imac-unknown-none-elf", "riscv64imac-unknown-none-elf"] + features: [""] steps: - uses: actions/checkout@v5 - uses: dtolnay/rust-toolchain@stable with: - targets: riscv32imac-unknown-none-elf + targets: ${{ matrix.target }} - run: | - cargo check --target riscv32imac-unknown-none-elf --no-default-features --features=${{ matrix.features }} - cargo check --target riscv32imac-unknown-none-elf -p op-revm --no-default-features --features=${{ matrix.features }} - cargo check --target riscv32imac-unknown-none-elf -p revm-database --no-default-features + cargo check --target ${{ matrix.target }} --no-default-features --features=${{ matrix.features }} + cargo check --target ${{ matrix.target }} -p op-revm --no-default-features --features=${{ matrix.features }} + cargo check --target ${{ matrix.target }} -p revm-database --no-default-features check: name: check ${{ matrix.features }} @@ -82,6 +83,8 @@ jobs: steps: - uses: actions/checkout@v5 - uses: dtolnay/rust-toolchain@stable + with: + components: clippy - run: cargo clippy --workspace --all-targets --all-features env: RUSTFLAGS: -Dwarnings diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c484c1528..0b571d16b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,166 @@ Because this is workspace with multi libraries, tags will be simplified, and with this document you can match version of project with git tag. +# 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 + +Patch release with a JumpTable serde fix + +* `revm-primitives`: 21.0.0 -> 21.0.1 (✓ API compatible changes) +* `revm-bytecode`: 7.0.0 -> 7.0.1 (✓ API compatible changes) +* `revm-context`: 10.1.0 -> 10.1.1 (✓ API compatible changes) +* `revm-interpreter`: 27.0.0 -> 27.0.1 (✓ API compatible changes) +* `revm-precompile`: 28.1.0 -> 28.1.1 (✓ API compatible changes) +* `revm-handler`: 11.1.0 -> 11.1.1 (✓ API compatible changes) +* `revm-ee-tests`: 0.1.0 +* `revm-state`: 8.0.0 -> 8.0.1 +* `revm-database-interface`: 8.0.1 -> 8.0.2 +* `revm-context-interface`: 11.1.0 -> 11.1.1 +* `revm-database`: 9.0.0 -> 9.0.1 +* `revm-inspector`: 11.1.0 -> 11.1.1 +* `revm`: 30.1.0 -> 30.1.1 +* `revm-statetest-types`: 10.1.0 -> 10.1.1 +* `revme`: 8.2.0 -> 8.2.1 +* `op-revm`: 11.1.0 -> 11.1.1 + +# v90 +date: 10.10.2025 + +Maintanance release. fix for op-revm + +* `revm-database-interface`: 8.0.0 -> 8.0.1 (✓ API compatible changes) +* `revm-database`: 8.0.0 -> 9.0.0 (⚠ API breaking changes) +* `revm-interpreter`: 26.0.0 -> 27.0.0 (⚠ API breaking changes) +* `revm-precompile`: 28.0.0 -> 28.1.0 (✓ API compatible changes) +* `revm-handler`: 11.0.0 -> 11.1.0 (✓ API compatible changes) +* `revm-inspector`: 11.0.0 -> 11.1.0 (✓ API compatible changes) +* `revme`: 8.0.0 -> 8.1.0 (✓ API compatible changes) +* `op-revm`: 11.0.0 -> 11.1.0 (✓ API compatible changes) +* `revm-context-interface`: 11.0.0 -> 11.1.0 +* `revm-context`: 10.0.0 -> 10.1.0 +* `revm`: 30.0.0 -> 30.1.0 +* `revm-statetest-types`: 10.0.0 -> 10.1.0 + +# v89 +date: 09.10.2025 +branch: release/v87 + +Small fix for op-revm + +* `op-revm`: 10.1.0 -> 10.1.1: + +# v88 +date: 07.10.2025 + +* `revm-primitives`: 20.2.1 -> 21.0.0 (⚠ API breaking changes) +* `revm-bytecode`: 6.2.2 -> 6.3.0 (✓ API compatible changes) +* `revm-state`: 7.0.5 -> 7.0.6 (✓ API compatible changes) +* `revm-database-interface`: 7.0.5 -> 7.0.6 (✓ API compatible changes) +* `revm-context-interface`: 10.2.0 -> 11.0.0 (⚠ API breaking changes) +* `revm-context`: 9.1.0 -> 10.0.0 (⚠ API breaking changes) +* `revm-database`: 7.0.5 -> 7.0.6 (✓ API compatible changes) +* `revm-interpreter`: 25.0.3 -> 26.0.0 (⚠ API breaking changes) +* `revm-precompile`: 27.0.0 -> 28.0.0 (⚠ API breaking changes) +* `revm-handler`: 10.0.1 -> 11.0.0 (⚠ API breaking changes) +* `revm-inspector`: 10.0.1 -> 11.0.0 (⚠ API breaking changes) +* `revm`: 29.0.1 -> 30.0.0 (⚠ API breaking changes) +* `revm-statetest-types`: 9.0.3 -> 10.0.0 (⚠ API breaking changes) +* `revme`: 7.2.3 -> 8.0.0 (⚠ API breaking changes) +* `op-revm`: 10.1.0 -> 11.0.0 (⚠ API breaking changes) +* `revm-ee-tests`: 0.1.0 + # v87 date: 23.09.2025 diff --git a/CLAUDE.md b/CLAUDE.md index 7e192850f5..9c296fde0c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -19,7 +19,7 @@ cargo build cargo build --release # Run all tests -cargo nexttest run --workspace +cargo nextest run --workspace # Lint and format cargo clippy --workspace --all-targets --all-features @@ -27,6 +27,7 @@ cargo fmt --all # Check no_std compatibility cargo check --target riscv32imac-unknown-none-elf --no-default-features +cargo check --target riscv64imac-unknown-none-elf --no-default-features # Run Ethereum state tests cargo run -p revme statetest legacytests/Cancun/GeneralStateTests @@ -90,4 +91,4 @@ When adding new features: - Ensure no_std compatibility - Add appropriate feature flags - Include tests for new functionality -- Update relevant examples if needed \ No newline at end of file +- Update relevant examples if needed diff --git a/Cargo.lock b/Cargo.lock index ad79469956..c841ba71b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,17 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "addchain" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2e69442aa5628ea6951fa33e24efe8313f4321a91bd729fc2f75bdfc858570" -dependencies = [ - "num-bigint 0.3.3", - "num-integer", - "num-traits", -] - [[package]] name = "addr2line" version = "0.24.2" @@ -57,9 +46,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy-chains" -version = "0.2.4" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19a9cc9d81ace3da457883b0bdf76776e55f1b84219a9e9d55c27ad308548d3f" +checksum = "ef8ff73a143281cb77c32006b04af9c047a6b8fe5860e85a88ad325328965355" dependencies = [ "alloy-primitives", "num_enum", @@ -68,9 +57,9 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "1.0.12" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bcb57295c4b632b6b3941a089ee82d00ff31ff9eb3eac801bf605ffddc81041" +checksum = "b7345077623aaa080fc06735ac13b8fa335125c8550f9c4f64135a5bf6f79967" dependencies = [ "alloy-eips", "alloy-primitives", @@ -93,9 +82,9 @@ dependencies = [ [[package]] name = "alloy-consensus-any" -version = "1.0.12" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab669be40024565acb719daf1b2a050e6dc065fc0bec6050d97a81cdb860bd7" +checksum = "501f83565d28bdb9d6457dd3b5d646e19db37709d0f27608a26a1839052ddade" dependencies = [ "alloy-consensus", "alloy-eips", @@ -144,9 +133,9 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "1.0.12" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f853de9ca1819f54de80de5d03bfc1bb7c9fafcf092b480a654447141bc354d" +checksum = "c219a87fb386a75780ddbdbbced242477321887e426b0f946c05815ceabe5e09" dependencies = [ "alloy-eip2124", "alloy-eip2930", @@ -159,14 +148,16 @@ dependencies = [ "derive_more", "either", "serde", - "sha2 0.10.9", + "serde_with", + "sha2", + "thiserror", ] [[package]] name = "alloy-json-abi" -version = "1.2.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b26fdd571915bafe857fccba4ee1a4f352965800e46a53e4a5f50187b7776fa" +checksum = "125a1c373261b252e53e04d6e92c37d881833afc1315fceab53fd46045695640" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -176,9 +167,9 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "1.0.12" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4997a9873c8639d079490f218e50e5fa07e70f957e9fc187c0a0535977f482f" +checksum = "334555c323fa2bb98f1d4c242b62da9de8c715557a2ed680a76cefbcac19fefd" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -191,9 +182,9 @@ dependencies = [ [[package]] name = "alloy-network" -version = "1.0.12" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0306e8d148b7b94d988615d367443c1b9d6d2e9fecd2e1f187ac5153dce56f5" +checksum = "c7ea377c9650203d7a7da9e8dee7f04906b49a9253f554b110edd7972e75ef34" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -217,9 +208,9 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "1.0.12" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eef189583f4c53d231dd1297b28a675ff842b551fb34715f562868a1937431a" +checksum = "b9f9ab9a9e92c49a357edaee2d35deea0a32ac8f313cfa37448f04e7e029c9d9" dependencies = [ "alloy-consensus", "alloy-eips", @@ -230,28 +221,27 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "1.2.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a326d47106039f38b811057215a92139f46eef7983a4b77b10930a0ea5685b1e" +checksum = "bc9485c56de23438127a731a6b4c87803d49faf1a7068dcd1d8768aca3a9edb9" dependencies = [ "alloy-rlp", "arbitrary", "bytes", "cfg-if", "const-hex", - "derive_arbitrary", "derive_more", "foldhash", "getrandom 0.3.3", - "hashbrown 0.15.4", - "indexmap 2.9.0", + "hashbrown 0.15.5", + "indexmap 2.11.0", "itoa", "k256", "keccak-asm", "paste", "proptest", "proptest-derive", - "rand 0.9.1", + "rand 0.9.2", "ruint", "rustc-hash", "serde", @@ -261,9 +251,9 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "1.0.12" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea624ddcdad357c33652b86aa7df9bd21afd2080973389d3facf1a221c573948" +checksum = "9a85361c88c16116defbd98053e3d267054d6b82729cdbef0236f7881590f924" dependencies = [ "alloy-chains", "alloy-consensus", @@ -285,7 +275,6 @@ dependencies = [ "either", "futures", "futures-utils-wasm", - "http", "lru", "parking_lot", "pin-project", @@ -318,20 +307,19 @@ checksum = "64b728d511962dda67c1bc7ea7c03736ec275ed2cf4c35d9585298ac9ccf3b73" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] name = "alloy-rpc-client" -version = "1.0.12" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e43d00b4de38432304c4e4b01ae6a3601490fd9824c852329d158763ec18663c" +checksum = "743fc964abb0106e454e9e8683fb0809fb32940270ef586a58e913531360b302" dependencies = [ "alloy-json-rpc", "alloy-primitives", "alloy-transport", "alloy-transport-http", - "async-stream", "futures", "pin-project", "reqwest", @@ -341,16 +329,15 @@ dependencies = [ "tokio-stream", "tower", "tracing", - "tracing-futures", "url", "wasmtimer", ] [[package]] name = "alloy-rpc-types-any" -version = "1.0.12" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5958f2310d69f4806e6f6b90ceb4f2b781cc5a843517a7afe2e7cfec6de3cfb9" +checksum = "97372c51a14a804fb9c17010e3dd6c117f7866620b264e24b64d2259be44bcdf" dependencies = [ "alloy-consensus-any", "alloy-rpc-types-eth", @@ -359,9 +346,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "1.0.12" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1826285e4ffc2372a8c061d5cc145858e67a0be3309b768c5b77ddb6b9e6cbc7" +checksum = "672286c19528007df058bafd82c67e23247b4b3ebbc538cbddc705a82d8a930f" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -374,14 +361,15 @@ dependencies = [ "itertools 0.14.0", "serde", "serde_json", + "serde_with", "thiserror", ] [[package]] name = "alloy-serde" -version = "1.0.12" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "906ce0190afeded19cb2e963cb8507c975a7862216b9e74f39bf91ddee6ae74b" +checksum = "1aae653f049267ae7e040eab6c9b9a417064ca1a6cb21e3dd59b9f1131ef048f" dependencies = [ "alloy-primitives", "serde", @@ -390,9 +378,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "1.0.12" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89baab06195c4be9c5d66f15c55e948013d1aff3ec1cfb0ed469e1423313fce" +checksum = "d97cedce202f848592b96f7e891503d3adb33739c4e76904da73574290141b93" dependencies = [ "alloy-primitives", "async-trait", @@ -405,9 +393,9 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "1.0.12" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a249a923e302ac6db932567c43945392f0b6832518aab3c4274858f58756774" +checksum = "83ae7d854db5b7cdd5b9ed7ad13d1e5e034cdd8be85ffef081f61dc6c9e18351" dependencies = [ "alloy-consensus", "alloy-network", @@ -421,41 +409,41 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "1.2.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4be1ce1274ddd7fdfac86e5ece1b225e9bba1f2327e20fbb30ee6b9cc1423fe" +checksum = "d20d867dcf42019d4779519a1ceb55eba8d7f3d0e4f0a89bcba82b8f9eb01e48" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] name = "alloy-sol-macro-expander" -version = "1.2.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01e92f3708ea4e0d9139001c86c051c538af0146944a2a9c7181753bd944bf57" +checksum = "b74e91b0b553c115d14bd0ed41898309356dc85d0e3d4b9014c4e7715e48c8ad" dependencies = [ "alloy-sol-macro-input", "const-hex", "heck", - "indexmap 2.9.0", + "indexmap 2.11.0", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", "syn-solidity", "tiny-keccak", ] [[package]] name = "alloy-sol-macro-input" -version = "1.2.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afe1bd348a41f8c9b4b54dfb314886786d6201235b0b3f47198b9d910c86bb2" +checksum = "84194d31220803f5f62d0a00f583fd3a062b36382e2bea446f1af96727754565" dependencies = [ "const-hex", "dunce", @@ -463,15 +451,15 @@ dependencies = [ "macro-string", "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", "syn-solidity", ] [[package]] name = "alloy-sol-type-parser" -version = "1.2.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6195df2acd42df92a380a8db6205a5c7b41282d0ce3f4c665ecf7911ac292f1" +checksum = "fe8c27b3cf6b2bb8361904732f955bc7c05e00be5f469cec7e2280b6167f3ff0" dependencies = [ "serde", "winnow", @@ -479,9 +467,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "1.2.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6185e98a79cf19010722f48a74b5a65d153631d2f038cabd250f4b9e9813b8ad" +checksum = "f5383d34ea00079e6dd89c652bcbdb764db160cef84e6250926961a0b2295d04" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -491,12 +479,13 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "1.0.12" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d1ae10b1bc77fde38161e242749e41e65e34000d05da0a3d3f631e03bfcb19e" +checksum = "c08b383bc903c927635e39e1dae7df2180877d93352d1abd389883665a598afc" dependencies = [ "alloy-json-rpc", "alloy-primitives", + "auto_impl", "base64", "derive_more", "futures", @@ -514,9 +503,9 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "1.0.12" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b234272ee449e32c9f1afbbe4ee08ea7c4b52f14479518f95c844ab66163c545" +checksum = "6e58dee1f7763ef302074b645fc4f25440637c09a60e8de234b62993f06c0ae3" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -529,9 +518,9 @@ dependencies = [ [[package]] name = "alloy-trie" -version = "0.8.1" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "983d99aa81f586cef9dae38443245e585840fcf0fc58b09aee0b1f27aed1d500" +checksum = "e3412d52bb97c6c6cc27ccc28d4e6e8cf605469101193b50b0bd5813b1f990b5" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -545,15 +534,15 @@ dependencies = [ [[package]] name = "alloy-tx-macros" -version = "1.0.12" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b75ef8609ea2b31c799b0a56c724dca4c73105c5ccc205d9dfeb1d038df6a1da" +checksum = "d14809f908822dbff0dc472c77ca4aa129ab12e22fd9bff2dd1ef54603e68e3d" dependencies = [ "alloy-primitives", "darling", "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] @@ -579,9 +568,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.19" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" dependencies = [ "anstyle", "anstyle-parse", @@ -609,35 +598,47 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.9" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" + +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] [[package]] name = "arbitrary" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +dependencies = [ + "derive_arbitrary", +] [[package]] name = "ark-bls12-381" @@ -676,9 +677,9 @@ dependencies = [ "ark-std 0.5.0", "educe", "fnv", - "hashbrown 0.15.4", + "hashbrown 0.15.5", "itertools 0.13.0", - "num-bigint 0.4.6", + "num-bigint", "num-integer", "num-traits", "zeroize", @@ -695,7 +696,7 @@ dependencies = [ "ark-serialize 0.3.0", "ark-std 0.3.0", "derivative", - "num-bigint 0.4.6", + "num-bigint", "num-traits", "paste", "rustc_version 0.3.3", @@ -715,7 +716,7 @@ dependencies = [ "derivative", "digest 0.10.7", "itertools 0.10.5", - "num-bigint 0.4.6", + "num-bigint", "num-traits", "paste", "rustc_version 0.4.1", @@ -736,7 +737,7 @@ dependencies = [ "digest 0.10.7", "educe", "itertools 0.13.0", - "num-bigint 0.4.6", + "num-bigint", "num-traits", "paste", "zeroize", @@ -769,7 +770,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" dependencies = [ "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] @@ -778,7 +779,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" dependencies = [ - "num-bigint 0.4.6", + "num-bigint", "num-traits", "quote", "syn 1.0.109", @@ -790,7 +791,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ - "num-bigint 0.4.6", + "num-bigint", "num-traits", "proc-macro2", "quote", @@ -803,11 +804,11 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09be120733ee33f7693ceaa202ca41accd5653b779563608f1234f78ae07c4b3" dependencies = [ - "num-bigint 0.4.6", + "num-bigint", "num-traits", "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] @@ -822,7 +823,7 @@ dependencies = [ "ark-std 0.5.0", "educe", "fnv", - "hashbrown 0.15.4", + "hashbrown 0.15.5", ] [[package]] @@ -836,7 +837,7 @@ dependencies = [ "ark-relations", "ark-std 0.5.0", "educe", - "num-bigint 0.4.6", + "num-bigint", "num-integer", "num-traits", "tracing", @@ -872,7 +873,7 @@ checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" dependencies = [ "ark-std 0.4.0", "digest 0.10.7", - "num-bigint 0.4.6", + "num-bigint", ] [[package]] @@ -885,7 +886,7 @@ dependencies = [ "ark-std 0.5.0", "arrayvec", "digest 0.10.7", - "num-bigint 0.4.6", + "num-bigint", ] [[package]] @@ -896,7 +897,7 @@ checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] @@ -963,20 +964,26 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] name = "async-trait" -version = "0.1.88" +version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "aurora-engine-modexp" version = "1.2.0" @@ -995,7 +1002,7 @@ checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] @@ -1022,7 +1029,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1052,26 +1059,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bincode" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740" -dependencies = [ - "bincode_derive", - "serde", - "unty", -] - -[[package]] -name = "bincode_derive" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf95709a440f45e986983918d0e8a1f30a9b1df04918fc828670606804ac3c09" -dependencies = [ - "virtue", -] - [[package]] name = "bit-set" version = "0.8.0" @@ -1105,9 +1092,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.9.1" +version = "2.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d" dependencies = [ "serde", ] @@ -1125,28 +1112,6 @@ dependencies = [ "wyz", ] -[[package]] -name = "blake3" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" -dependencies = [ - "arrayref", - "arrayvec", - "cc", - "cfg-if", - "constant_time_eq", -] - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] - [[package]] name = "block-buffer" version = "0.10.4" @@ -1170,9 +1135,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.18.1" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "byte-slice-cast" @@ -1197,9 +1162,9 @@ dependencies = [ [[package]] name = "c-kzg" -version = "2.1.1" +version = "2.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7318cfa722931cb5fe0838b98d3ce5621e75f6a6408abc21721d80de9223f2e4" +checksum = "137a2a2878ed823ef1bd73e5441e245602aae5360022113b8ad259ca4b5b8727" dependencies = [ "blst", "cc", @@ -1218,18 +1183,24 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.27" +version = "1.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" +checksum = "42bc4aea80032b7bf409b0bc7ccad88853858911b7713a8062fdc0623867bedc" dependencies = [ "shlex", ] [[package]] name = "cfg-if" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" @@ -1273,9 +1244,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.40" +version = "4.5.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" +checksum = "2c5e4fcf9c21d2e544ca1ee9d8552de13019a42aa7dbf32747fa7aaf1df76e57" dependencies = [ "clap_builder", "clap_derive", @@ -1283,9 +1254,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.40" +version = "4.5.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" +checksum = "fecb53a0e6fcfb055f686001bc2e2592fa527efaf38dbe81a6a9563562e57d41" dependencies = [ "anstream", "anstyle", @@ -1295,14 +1266,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.40" +version = "4.5.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" +checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] @@ -1313,22 +1284,27 @@ checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "codspeed" -version = "2.10.1" +version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f4cce9c27c49c4f101fffeebb1826f41a9df2e7498b7cd4d95c0658b796c6c" +checksum = "35584c5fcba8059780748866387fb97c5a203bcfc563fc3d0790af406727a117" dependencies = [ + "anyhow", + "bincode", "colored", + "glob", "libc", + "nix", "serde", "serde_json", + "statrs", "uuid", ] [[package]] name = "codspeed-criterion-compat" -version = "2.10.1" +version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c23d880a28a2aab52d38ca8481dd7a3187157d0a952196b6db1db3c8499725" +checksum = "78f6c1c6bed5fd84d319e8b0889da051daa361c79b7709c9394dfe1a882bba67" dependencies = [ "codspeed", "codspeed-criterion-compat-walltime", @@ -1337,9 +1313,9 @@ dependencies = [ [[package]] name = "codspeed-criterion-compat-walltime" -version = "2.10.1" +version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0a2f7365e347f4f22a67e9ea689bf7bc89900a354e22e26cf8a531a42c8fbb" +checksum = "c989289ce6b1cbde72ed560496cb8fbf5aa14d5ef5666f168e7f87751038352e" dependencies = [ "anes", "cast", @@ -1375,27 +1351,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "console" -version = "0.15.11" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" +checksum = "2e09ced7ebbccb63b4c65413d821f2e00ce54c5ca4514ddc6b3c892fdbcbc69d" dependencies = [ "encode_unicode", "libc", "once_cell", "unicode-width", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "const-hex" -version = "1.14.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83e22e0ed40b96a48d3db274f72fd365bd78f67af39b6bbd47e8a15e1c6207ff" +checksum = "dccd746bf9b1038c0507b7cec21eb2b11222db96a2902c96e8c185d6d20fb9c4" dependencies = [ "cfg-if", "cpufeatures", @@ -1430,12 +1406,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "constant_time_eq" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" - [[package]] name = "core-foundation" version = "0.9.4" @@ -1513,9 +1483,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "crypto-bigint" @@ -1581,7 +1551,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] @@ -1592,7 +1562,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] @@ -1642,24 +1612,24 @@ dependencies = [ [[package]] name = "derive-where" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "510c292c8cf384b1a340b816a9a6cf2599eb8f566a44949024af88418000c50b" +checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] name = "derive_arbitrary" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" +checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] @@ -1679,7 +1649,7 @@ checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", "unicode-xid", ] @@ -1698,7 +1668,7 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer 0.10.4", + "block-buffer", "const-oid", "crypto-common", "subtle", @@ -1712,7 +1682,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] @@ -1723,9 +1693,9 @@ checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] name = "dyn-clone" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "ecdsa" @@ -1751,7 +1721,7 @@ dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] @@ -1806,7 +1776,7 @@ checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] @@ -1817,12 +1787,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -1954,27 +1924,10 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" dependencies = [ - "byteorder", - "ff_derive", "rand_core 0.6.4", "subtle", ] -[[package]] -name = "ff_derive" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f10d12652036b0e99197587c6ba87a8fc3031986499973c030d8b44fcc151b60" -dependencies = [ - "addchain", - "num-bigint 0.3.3", - "num-integer", - "num-traits", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "fixed-hash" version = "0.8.0" @@ -2016,9 +1969,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ "percent-encoding", ] @@ -2085,7 +2038,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] @@ -2130,12 +2083,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" -[[package]] -name = "gcd" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" - [[package]] name = "generic-array" version = "0.14.7" @@ -2178,18 +2125,18 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "glob" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "gmp-mpfr-sys" -version = "1.6.5" +version = "1.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66d61197a68f6323b9afa616cf83d55d69191e1bf364d4eb7d35ae18defe776" +checksum = "a636fb6a653382a379ee1e5593dacdc628667994167024c143214cafd40c1a86" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -2233,9 +2180,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashbrown" -version = "0.15.4" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", @@ -2324,18 +2271,20 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "hyper" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" dependencies = [ + "atomic-waker", "bytes", "futures-channel", - "futures-util", + "futures-core", "http", "http-body", "httparse", "itoa", "pin-project-lite", + "pin-utils", "smallvec", "tokio", "want", @@ -2359,9 +2308,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.14" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb" +checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" dependencies = [ "base64", "bytes", @@ -2499,9 +2448,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ "idna_adapter", "smallvec", @@ -2535,7 +2484,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] @@ -2551,29 +2500,40 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.9.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9" dependencies = [ "arbitrary", "equivalent", - "hashbrown 0.15.4", + "hashbrown 0.15.5", "serde", ] [[package]] name = "indicatif" -version = "0.17.11" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235" +checksum = "70a646d946d06bedbbc4cac4c218acf4bbf2d87757a784857025f4d447e4e1cd" dependencies = [ "console", - "number_prefix", "portable-atomic", "unicode-width", + "unit-prefix", "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" @@ -2598,7 +2558,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2616,15 +2576,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.13.0" @@ -2670,7 +2621,7 @@ dependencies = [ "elliptic-curve", "once_cell", "serdect", - "sha2 0.10.9", + "sha2", ] [[package]] @@ -2692,20 +2643,6 @@ dependencies = [ "sha3-asm", ] -[[package]] -name = "kzg-rs" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9201effeea3fcc93b587904ae2df9ce97e433184b9d6d299e9ebc9830a546636" -dependencies = [ - "ff", - "hex", - "serde_arrays", - "sha2 0.10.9", - "sp1_bls12_381", - "spin", -] - [[package]] name = "lazy_static" version = "1.5.0" @@ -2717,9 +2654,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.174" +version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "libm" @@ -2727,52 +2664,6 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" -[[package]] -name = "libsecp256k1" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79019718125edc905a079a70cfa5f3820bc76139fc91d6f9abc27ea2a887139" -dependencies = [ - "arrayref", - "base64", - "digest 0.9.0", - "libsecp256k1-core", - "libsecp256k1-gen-ecmult", - "libsecp256k1-gen-genmult", - "rand 0.8.5", - "serde", - "sha2 0.9.9", -] - -[[package]] -name = "libsecp256k1-core" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" -dependencies = [ - "crunchy", - "digest 0.9.0", - "subtle", -] - -[[package]] -name = "libsecp256k1-gen-ecmult" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" -dependencies = [ - "libsecp256k1-core", -] - -[[package]] -name = "libsecp256k1-gen-genmult" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" -dependencies = [ - "libsecp256k1-core", -] - [[package]] name = "linux-raw-sys" version = "0.9.4" @@ -2807,7 +2698,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "227748d55f2f0ab4735d87fd623798cb6b664512fe979705f829c9f81c934465" dependencies = [ - "hashbrown 0.15.4", + "hashbrown 0.15.5", ] [[package]] @@ -2818,7 +2709,7 @@ checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] @@ -2864,13 +2755,25 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "num" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" dependencies = [ - "num-bigint 0.4.6", + "num-bigint", "num-complex", "num-integer", "num-iter", @@ -2878,17 +2781,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-bigint" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - [[package]] name = "num-bigint" version = "0.4.6" @@ -2940,7 +2832,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ - "num-bigint 0.4.6", + "num-bigint", "num-integer", "num-traits", ] @@ -2967,40 +2859,36 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" dependencies = [ "num_enum_derive", + "rustversion", ] [[package]] name = "num_enum_derive" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] -[[package]] -name = "number_prefix" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" - [[package]] name = "nybbles" -version = "0.3.4" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8983bb634df7248924ee0c4c3a749609b5abcb082c28fffe3254b3eb3602b307" +checksum = "63cb50036b1ad148038105af40aaa70ff24d8a14fbc44ae5c914e1348533d12e" dependencies = [ "alloy-rlp", - "const-hex", + "cfg-if", "proptest", + "ruint", "serde", "smallvec", ] @@ -3034,7 +2922,7 @@ checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" [[package]] name = "op-revm" -version = "10.1.0" +version = "12.0.1" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -3043,15 +2931,9 @@ dependencies = [ "rstest", "serde", "serde_json", - "sha2 0.10.9", + "sha2", ] -[[package]] -name = "opaque-debug" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" - [[package]] name = "openssl" version = "0.10.73" @@ -3075,7 +2957,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] @@ -3105,128 +2987,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "primeorder", - "sha2 0.10.9", -] - -[[package]] -name = "p3-baby-bear" -version = "0.2.3-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7521838ecab2ddf4f7bc4ceebad06ec02414729598485c1ada516c39900820e8" -dependencies = [ - "num-bigint 0.4.6", - "p3-field", - "p3-mds", - "p3-poseidon2", - "p3-symmetric", - "rand 0.8.5", - "serde", -] - -[[package]] -name = "p3-dft" -version = "0.2.3-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46414daedd796f1eefcdc1811c0484e4bced5729486b6eaba9521c572c76761a" -dependencies = [ - "p3-field", - "p3-matrix", - "p3-maybe-rayon", - "p3-util", - "tracing", -] - -[[package]] -name = "p3-field" -version = "0.2.3-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48948a0516b349e9d1cdb95e7236a6ee010c44e68c5cc78b4b92bf1c4022a0d9" -dependencies = [ - "itertools 0.12.1", - "num-bigint 0.4.6", - "num-traits", - "p3-util", - "rand 0.8.5", - "serde", -] - -[[package]] -name = "p3-matrix" -version = "0.2.3-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4de3f373589477cb735ea58e125898ed20935e03664b4614c7fac258b3c42f" -dependencies = [ - "itertools 0.12.1", - "p3-field", - "p3-maybe-rayon", - "p3-util", - "rand 0.8.5", - "serde", - "tracing", -] - -[[package]] -name = "p3-maybe-rayon" -version = "0.2.3-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3968ad1160310296eb04f91a5f4edfa38fe1d6b2b8cd6b5c64e6f9b7370979e" - -[[package]] -name = "p3-mds" -version = "0.2.3-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2356b1ed0add6d5dfbf7a338ce534a6fde827374394a52cec16a0840af6e97c9" -dependencies = [ - "itertools 0.12.1", - "p3-dft", - "p3-field", - "p3-matrix", - "p3-symmetric", - "p3-util", - "rand 0.8.5", -] - -[[package]] -name = "p3-poseidon2" -version = "0.2.3-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da1eec7e1b6900581bedd95e76e1ef4975608dd55be9872c9d257a8a9651c3a" -dependencies = [ - "gcd", - "p3-field", - "p3-mds", - "p3-symmetric", - "rand 0.8.5", - "serde", -] - -[[package]] -name = "p3-symmetric" -version = "0.2.3-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edb439bea1d822623b41ff4b51e3309e80d13cadf8b86d16ffd5e6efb9fdc360" -dependencies = [ - "itertools 0.12.1", - "p3-field", - "serde", -] - -[[package]] -name = "p3-util" -version = "0.2.3-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c2c2010678b9332b563eaa38364915b585c1a94b5ca61e2c7541c087ddda5c" -dependencies = [ - "serde", -] - -[[package]] -name = "pairing" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fec4625e73cf41ef4bb6846cafa6d44736525f442ba45e407c4a000a13996f" -dependencies = [ - "group", + "sha2", ] [[package]] @@ -3254,7 +3015,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] @@ -3277,7 +3038,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -3288,9 +3049,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "percent-encoding" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" @@ -3305,9 +3066,9 @@ dependencies = [ [[package]] name = "phf" -version = "0.11.3" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" dependencies = [ "phf_macros", "phf_shared", @@ -3316,32 +3077,32 @@ dependencies = [ [[package]] name = "phf_generator" -version = "0.11.3" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" dependencies = [ + "fastrand", "phf_shared", - "rand 0.8.5", ] [[package]] name = "phf_macros" -version = "0.11.3" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" dependencies = [ "phf_generator", "phf_shared", "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] name = "phf_shared" -version = "0.11.3" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" dependencies = [ "siphasher", ] @@ -3363,7 +3124,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] @@ -3509,14 +3270,14 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] @@ -3532,7 +3293,7 @@ dependencies = [ "bitflags", "lazy_static", "num-traits", - "rand 0.9.1", + "rand 0.9.2", "rand_chacha 0.9.0", "rand_xorshift", "regex-syntax", @@ -3549,7 +3310,7 @@ checksum = "4ee1c9ac207483d5e7db4940700de86a9aae46ef90c48b57f99fe7edb8345e49" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] @@ -3593,9 +3354,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", @@ -3652,9 +3413,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" dependencies = [ "either", "rayon-core", @@ -3662,9 +3423,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -3672,9 +3433,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.13" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ "bitflags", ] @@ -3696,14 +3457,14 @@ checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] name = "regex" -version = "1.11.1" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" dependencies = [ "aho-corasick", "memchr", @@ -3713,9 +3474,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" dependencies = [ "aho-corasick", "memchr", @@ -3724,9 +3485,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" [[package]] name = "relative-path" @@ -3736,9 +3497,9 @@ checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" [[package]] name = "reqwest" -version = "0.12.20" +version = "0.12.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabf4c97d9130e2bf606614eb937e86edac8292eaa6f422f995d7e8de1eb1813" +checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" dependencies = [ "base64", "bytes", @@ -3772,7 +3533,7 @@ dependencies = [ [[package]] name = "revm" -version = "29.0.1" +version = "31.0.1" dependencies = [ "revm-bytecode", "revm-context", @@ -3791,18 +3552,19 @@ dependencies = [ [[package]] name = "revm-bytecode" -version = "6.2.2" +version = "7.1.1" dependencies = [ "bitvec", "paste", "phf", "revm-primitives", "serde", + "serde_json", ] [[package]] name = "revm-context" -version = "9.1.0" +version = "11.0.1" dependencies = [ "bitvec", "cfg-if", @@ -3818,7 +3580,7 @@ dependencies = [ [[package]] name = "revm-context-interface" -version = "10.2.0" +version = "12.0.1" dependencies = [ "alloy-eip2930", "alloy-eip7702", @@ -3832,7 +3594,7 @@ dependencies = [ [[package]] name = "revm-database" -version = "7.0.5" +version = "9.0.4" dependencies = [ "alloy-eips", "alloy-provider", @@ -3848,7 +3610,7 @@ dependencies = [ [[package]] name = "revm-database-interface" -version = "7.0.5" +version = "8.0.5" dependencies = [ "auto_impl", "either", @@ -3869,14 +3631,13 @@ dependencies = [ "rstest", "serde", "serde_json", - "sha2 0.10.9", + "sha2", ] [[package]] name = "revm-handler" -version = "10.0.1" +version = "12.0.1" dependencies = [ - "alloy-eip7702", "alloy-provider", "alloy-signer", "alloy-signer-local", @@ -3896,7 +3657,7 @@ dependencies = [ [[package]] name = "revm-inspector" -version = "10.0.1" +version = "12.0.1" dependencies = [ "auto_impl", "either", @@ -3913,18 +3674,19 @@ dependencies = [ [[package]] name = "revm-interpreter" -version = "25.0.3" +version = "29.0.1" dependencies = [ - "bincode 2.0.1", "revm-bytecode", "revm-context-interface", "revm-primitives", + "revm-state", "serde", + "serde_json", ] [[package]] name = "revm-precompile" -version = "27.0.0" +version = "29.0.1" dependencies = [ "ark-bls12-381", "ark-bn254", @@ -3939,22 +3701,20 @@ dependencies = [ "cfg-if", "codspeed-criterion-compat", "k256", - "kzg-rs", - "libsecp256k1", "p256", - "rand 0.9.1", + "rand 0.9.2", "revm-primitives", "ripemd", "rstest", "rug", - "secp256k1 0.31.0", - "sha2 0.10.9", + "secp256k1 0.31.1", + "sha2", "substrate-bn", ] [[package]] name = "revm-primitives" -version = "20.2.1" +version = "21.0.2" dependencies = [ "alloy-primitives", "num_enum", @@ -3964,7 +3724,7 @@ dependencies = [ [[package]] name = "revm-state" -version = "7.0.5" +version = "8.1.1" dependencies = [ "bitflags", "revm-bytecode", @@ -3974,8 +3734,9 @@ dependencies = [ [[package]] name = "revm-statetest-types" -version = "9.0.3" +version = "11.0.1" dependencies = [ + "alloy-eips", "k256", "revm", "serde", @@ -3985,7 +3746,7 @@ dependencies = [ [[package]] name = "revme" -version = "7.2.3" +version = "9.0.1" dependencies = [ "alloy-rlp", "alloy-sol-types", @@ -3997,14 +3758,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", @@ -4044,21 +3797,20 @@ dependencies = [ [[package]] name = "rstest" -version = "0.25.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fc39292f8613e913f7df8fa892b8944ceb47c247b78e1b1ae2f09e019be789d" +checksum = "f5a3193c063baaa2a95a33f03035c8a72b83d97a54916055ba22d35ed3839d49" dependencies = [ "futures-timer", "futures-util", "rstest_macros", - "rustc_version 0.4.1", ] [[package]] name = "rstest_macros" -version = "0.25.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f168d99749d307be9de54d23fd226628d99768225ef08f6ffb52e0182a27746" +checksum = "9c845311f0ff7951c5506121a9ad75aec44d083c31583b2ea5a30bcb0b0abba0" dependencies = [ "cfg-if", "glob", @@ -4068,15 +3820,15 @@ dependencies = [ "regex", "relative-path", "rustc_version 0.4.1", - "syn 2.0.103", + "syn 2.0.106", "unicode-ident", ] [[package]] name = "rug" -version = "1.27.0" +version = "1.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4207e8d668e5b8eb574bda8322088ccd0d7782d3d03c7e8d562e82ed82bdcbc3" +checksum = "58ad2e973fe3c3214251a840a621812a4f40468da814b1a3d6947d433c2af11f" dependencies = [ "az", "gmp-mpfr-sys", @@ -4097,14 +3849,14 @@ dependencies = [ "bytes", "fastrlp 0.3.1", "fastrlp 0.4.0", - "num-bigint 0.4.6", + "num-bigint", "num-integer", "num-traits", "parity-scale-codec", "primitive-types", "proptest", "rand 0.8.5", - "rand 0.9.1", + "rand 0.9.2", "rlp", "ruint-macro", "serde", @@ -4120,9 +3872,9 @@ checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" [[package]] name = "rustc-demangle" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustc-hash" @@ -4159,15 +3911,15 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -4181,9 +3933,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "rusty-fork" @@ -4233,6 +3985,18 @@ dependencies = [ "serde_json", ] +[[package]] +name = "schemars" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -4268,12 +4032,12 @@ dependencies = [ [[package]] name = "secp256k1" -version = "0.31.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a3dff2d01c9aa65c3186a45ff846bfea52cbe6de3b6320ed2a358d90dad0d76" +checksum = "2c3c81b43dc2d8877c216a3fccf76677ee1ebccd429566d3e67447290d0c42b2" dependencies = [ "bitcoin_hashes", - "rand 0.9.1", + "rand 0.9.2", "secp256k1-sys 0.11.0", ] @@ -4351,15 +4115,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde_arrays" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94a16b99c5ea4fe3daccd14853ad260ec00ea043b2708d1fd1da3106dcd8d9df" -dependencies = [ - "serde", -] - [[package]] name = "serde_derive" version = "1.0.219" @@ -4368,16 +4123,16 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" dependencies = [ - "indexmap 2.9.0", + "indexmap 2.11.0", "itoa", "memchr", "ryu", @@ -4398,16 +4153,17 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf65a400f8f66fb7b0552869ad70157166676db75ed8181f8104ea91cf9d0b42" +checksum = "f2c45cd61fefa9db6f254525d46e392b852e0e61d9a1fd36e5bd183450a556d5" dependencies = [ "base64", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.9.0", - "schemars", + "indexmap 2.11.0", + "schemars 0.9.0", + "schemars 1.0.4", "serde", "serde_derive", "serde_json", @@ -4417,14 +4173,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81679d9ed988d5e9a5e6531dc3f2c28efbd639cbd1dfb628df08edea6004da77" +checksum = "de90945e6565ce0d9a25098082ed4ee4002e047cb59892c318d66821e14bb30f" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] @@ -4437,19 +4193,6 @@ dependencies = [ "serde", ] -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - [[package]] name = "sha2" version = "0.10.9" @@ -4515,9 +4258,9 @@ checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] name = "slab" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "smallvec" @@ -4530,58 +4273,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.10" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" dependencies = [ "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "sp1-lib" -version = "5.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0fd8bc101e5603ccf2dc1836ea06410f25ce2298755b2dac626add9be2424b4" -dependencies = [ - "bincode 1.3.3", - "serde", - "sp1-primitives", -] - -[[package]] -name = "sp1-primitives" -version = "5.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "699935774a5131c1a8b371108d0666c0c80c43611045fb77fae43f2f242676d5" -dependencies = [ - "bincode 1.3.3", - "blake3", - "cfg-if", - "hex", - "lazy_static", - "num-bigint 0.4.6", - "p3-baby-bear", - "p3-field", - "p3-poseidon2", - "p3-symmetric", - "serde", - "sha2 0.10.9", -] - -[[package]] -name = "sp1_bls12_381" -version = "0.8.0-sp1-5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac255e1704ebcdeec5e02f6a0ebc4d2e9e6b802161938330b6810c13a610c583" -dependencies = [ - "cfg-if", - "ff", - "group", - "pairing", - "rand_core 0.6.4", - "sp1-lib", - "subtle", + "windows-sys 0.59.0", ] [[package]] @@ -4612,6 +4309,16 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "statrs" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a3fe7c28c6512e766b0874335db33c94ad7b8f9054228ae1c2abd47ce7d335e" +dependencies = [ + "approx", + "num-traits", +] + [[package]] name = "strsim" version = "0.11.1" @@ -4620,24 +4327,23 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" dependencies = [ "heck", "proc-macro2", "quote", - "rustversion", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] @@ -4672,9 +4378,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.103" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", @@ -4683,14 +4389,14 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "1.2.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c8c8f496c33dc6343dac05b4be8d9e0bca180a4caa81d7b8416b10cc2273cd" +checksum = "a0b198d366dbec045acfcd97295eb653a7a2b40e4dc764ef1e79aafcad439d3c" dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] @@ -4710,7 +4416,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] @@ -4721,35 +4427,35 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.20.0" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e" dependencies = [ "fastrand", "getrandom 0.3.3", "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] @@ -4823,18 +4529,20 @@ dependencies = [ [[package]] name = "tokio" -version = "1.45.1" +version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ "backtrace", "bytes", + "io-uring", "libc", "mio", "pin-project-lite", + "slab", "socket2", "tokio-macros", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -4845,7 +4553,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] @@ -4872,9 +4580,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.15" +version = "0.7.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" dependencies = [ "bytes", "futures-core", @@ -4895,7 +4603,7 @@ version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.9.0", + "indexmap 2.11.0", "toml_datetime", "winnow", ] @@ -4964,7 +4672,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] @@ -4977,18 +4685,6 @@ dependencies = [ "valuable", ] -[[package]] -name = "tracing-futures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "futures", - "futures-task", - "pin-project", - "tracing", -] - [[package]] name = "tracing-subscriber" version = "0.2.25" @@ -5063,20 +4759,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] -name = "unty" -version = "0.0.4" +name = "unit-prefix" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" +checksum = "323402cff2dd658f39ca17c789b502021b3f18707c91cdf22e3838e1b4023817" [[package]] name = "url" -version = "2.5.4" +version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] [[package]] @@ -5093,9 +4790,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" +checksum = "f33196643e165781c20a5ead5582283a7dacbb87855d867fbc2df3f81eddc1be" dependencies = [ "getrandom 0.3.3", "js-sys", @@ -5120,12 +4817,6 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" -[[package]] -name = "virtue" -version = "0.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1" - [[package]] name = "wait-timeout" version = "0.2.1" @@ -5191,7 +4882,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", "wasm-bindgen-shared", ] @@ -5226,7 +4917,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5276,11 +4967,11 @@ dependencies = [ [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -5304,7 +4995,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] @@ -5315,7 +5006,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] @@ -5344,20 +5035,20 @@ dependencies = [ [[package]] name = "windows-sys" -version = "0.52.0" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] name = "windows-sys" -version = "0.59.0" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets", + "windows-targets 0.53.3", ] [[package]] @@ -5366,14 +5057,31 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +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", ] [[package]] @@ -5382,53 +5090,101 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "winnow" -version = "0.7.11" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" dependencies = [ "memchr", ] @@ -5477,7 +5233,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", "synstructure", ] @@ -5498,7 +5254,7 @@ checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] @@ -5518,7 +5274,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", "synstructure", ] @@ -5539,7 +5295,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] [[package]] @@ -5555,9 +5311,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.2" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" dependencies = [ "yoke", "zerofrom", @@ -5572,5 +5328,5 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.103", + "syn 2.0.106", ] diff --git a/Cargo.toml b/Cargo.toml index f93a837922..b2148eb507 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,35 +41,35 @@ default-members = ["crates/revm"] [workspace.dependencies] # revm -revm = { path = "crates/revm", version = "29.0.1", default-features = false } -primitives = { path = "crates/primitives", package = "revm-primitives", version = "20.2.1", default-features = false } -bytecode = { path = "crates/bytecode", package = "revm-bytecode", version = "6.2.2", default-features = false } -database = { path = "crates/database", package = "revm-database", version = "7.0.5", default-features = false } -database-interface = { path = "crates/database/interface", package = "revm-database-interface", version = "7.0.5", default-features = false } -state = { path = "crates/state", package = "revm-state", version = "7.0.5", default-features = false } -interpreter = { path = "crates/interpreter", package = "revm-interpreter", version = "25.0.3", default-features = false } -inspector = { path = "crates/inspector", package = "revm-inspector", version = "10.0.1", default-features = false } -precompile = { path = "crates/precompile", package = "revm-precompile", version = "27.0.0", default-features = false } -statetest-types = { path = "crates/statetest-types", package = "revm-statetest-types", version = "9.0.3", default-features = false } -context = { path = "crates/context", package = "revm-context", version = "9.1.0", default-features = false } -context-interface = { path = "crates/context/interface", package = "revm-context-interface", version = "10.2.0", default-features = false } -handler = { path = "crates/handler", package = "revm-handler", version = "10.0.1", default-features = false } -op-revm = { path = "crates/op-revm", package = "op-revm", version = "10.1.0", default-features = false } +revm = { path = "crates/revm", version = "31.0.1", default-features = false } +primitives = { path = "crates/primitives", package = "revm-primitives", version = "21.0.2", default-features = false } +bytecode = { path = "crates/bytecode", package = "revm-bytecode", version = "7.1.1", default-features = false } +database = { path = "crates/database", package = "revm-database", version = "9.0.4", default-features = false } +database-interface = { path = "crates/database/interface", package = "revm-database-interface", version = "8.0.5", default-features = false } +state = { path = "crates/state", package = "revm-state", version = "8.1.1", default-features = false } +interpreter = { path = "crates/interpreter", package = "revm-interpreter", version = "29.0.1", default-features = false } +inspector = { path = "crates/inspector", package = "revm-inspector", version = "12.0.1", default-features = false } +precompile = { path = "crates/precompile", package = "revm-precompile", version = "29.0.1", default-features = false } +statetest-types = { path = "crates/statetest-types", package = "revm-statetest-types", version = "11.0.1", default-features = false } +context = { path = "crates/context", package = "revm-context", version = "11.0.1", default-features = false } +context-interface = { path = "crates/context/interface", package = "revm-context-interface", version = "12.0.1", default-features = false } +handler = { path = "crates/handler", package = "revm-handler", version = "12.0.1", default-features = false } +op-revm = { path = "crates/op-revm", package = "op-revm", version = "12.0.1", 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.2.0", default-features = false } +alloy-primitives = { version = "1.3.1", default-features = false } # alloy in examples, revme or feature flagged. alloy-rlp = { version = "0.3.12", default-features = false } -alloy-sol-types = { version = "1.2.0", default-features = false } -alloy-consensus = { version = "1.0.12", default-features = false } -alloy-eips = { version = "1.0.12", default-features = false } -alloy-provider = { version = "1.0.12", default-features = false } -alloy-signer = { version = "1.0.12", default-features = false } -alloy-signer-local = { version = "1.0.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 } # precompiles @@ -80,14 +80,12 @@ 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.27.0", default-features = false } +rug = { version = "1.28.0", default-features = false } blst = "0.3.15" bn = { package = "substrate-bn", version = "0.6", default-features = false } -c-kzg = { version = "2.1.1", default-features = false } +c-kzg = { version = "2.1.4", default-features = false } k256 = { version = "0.13.4", default-features = false } -libsecp256k1 = { version = "0.7", default-features = false } -kzg-rs = { version = "0.2.7", default-features = false } -secp256k1 = { version = "0.31.0", default-features = false } +secp256k1 = { version = "0.31.1", default-features = false } sha2 = { version = "0.10.9", default-features = false } ripemd = { version = "0.1.3", default-features = false } p256 = { version = "0.13.2", default-features = false } @@ -95,33 +93,35 @@ p256 = { version = "0.13.2", default-features = false } # bytecode bitvec = { version = "1", default-features = false } paste = "1.0" -phf = { version = "0.11", default-features = false } +phf = { version = "0.13", default-features = false } # revme clap = { version = "4", features = ["derive"] } -criterion = { package = "codspeed-criterion-compat", version = "2.10" } +criterion = { package = "codspeed-criterion-compat", version = "3.0" } # serde -serde = { version = "1.0", default-features = false } +serde = { version = "1.0", default-features = false, features = [ + "derive", + "rc", +] } serde_json = { version = "1.0", default-features = false } # misc auto_impl = "1.3.0" -bitflags = { version = "2.9.1", default-features = false } +bitflags = { version = "2.9.3", default-features = false } cfg-if = { version = "1.0", default-features = false } -derive-where = { version = "1.5.0", default-features = false } +derive-where = { version = "1.6.0", default-features = false } rand = "0.9" -tokio = "1.45" +tokio = "1.47" either = { version = "1.15.0", default-features = false } # dev-dependencies -anyhow = "1.0.98" -bincode = { version = "2.0", features = ["serde"] } +anyhow = "1.0.99" eyre = "0.6.12" hash-db = "0.15" -indicatif = "0.17" +indicatif = "0.18" plain_hasher = "0.2" -rstest = "0.25.0" +rstest = "0.26.0" serde_derive = "1.0" thiserror = "2.0" triehash = "0.8" diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index 9b638a86ba..c4445117f8 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -1,6 +1,69 @@ + +# 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 + +# v90 tag ( revm v30.1.0) + +* Removal of some deprecated functions. `into_plain_state`, `regenerate_hash` were deprecated few releases ago. + +# v89 tag ( op-revm) + +No breaking changes + +# v88 tag (revm v30.0.0) + +* `ContextTr`, `EvmTr` gained `all_mut()` and `all()` functions. + * `InspectorEvmTr` got `all_inspector` and `all_mut_inspector` functions. + * For custom Evm, you only need to implement `all_*` functions. +* `InspectorFrame` got changed to allow CustomFrame to be added. + * If there is no need for inspection `fn eth_frame` can return None. +* `kzg-rs` feature and library removed. Default KZG implementation is now c-kzg. + * for no-std variant, arkworks lib is used. +* Opcodes that load account/storage will not load item if there is not enough gas for cold load. + * This is in preparation for BAL. + * Journal functions for loading now have skip_cold_load bool. +* `libsecp256k1` parity library is deprecated and removed. +* `JumpTable` internal representation changed from `BitVec` to `Bytes`. No changes in API. +* `SpecId` enum gained new `Amsterdam` variant and `OpSpecId` gained `Jovian` variant. +* `SELFDESTRUCT` constant renamed to `SELFDESTRUCT_REFUND`. +* `FrameStack::push` and `FrameStack::end_init` marked as `unsafe` as it can cause UB. +* First precompile error is now bubble up with detailed error messages. New `PrecompileError` variants added. +* Batch execution errors now include transaction index. +* `CallInput` now contains bytecode that is going to be executed (Previously it had address). + * This allows skipping double bytecode fetching. +* `InvalidTransaction` enum gained `Str(Cow<'static, str>)` variant for custom error messages. +* `calc_excess_blob_gas` and `calc_excess_blob_gas` removes as they are unused and not accurate for Prague. + # v86 tag (revm v29.0.0) -* `PrecompileWithAddress` is renamed to `Precompile` and it became a struct. +* `PrecompileWithAddress` is renamed to `Precompile` and it became a struct. * `Precompile` contains`PrecompileId`, `Address` and function. * The reason is adding `PrecompileId` as it is needed for fusaka hardfork diff --git a/README.md b/README.md index e9e6d1d697..82bbb64d5c 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ let mut evm = evm.with_inspector(tracer); let out = evm.inspect_tx(tx); ``` -The Evm Framework API is somewhat complex to use, but this document provides a detailed explanation. It enables users to extend logic, incorporate various context types, and offers built-in support for inspection. For a practical example, you can refer to the [op-revm crate](https://github.com/op-rs/op-revm). +The EVM Framework API is somewhat complex to use, but this document provides a detailed explanation. It enables users to extend logic, incorporate various context types, and offers built-in support for inspection. For a practical example, you can refer to the [op-revm crate](https://github.com/op-rs/op-revm). ### Users: @@ -41,7 +41,7 @@ As previously noted, there are several groups of projects that utilize this tech * **Clients**: [Reth](https://github.com/paradigmxyz/reth), [Helios](https://github.com/a16z/helios), [Trin](https://github.com/ethereum/trin),.. * **Tooling**: [Foundry](https://github.com/foundry-rs/foundry/), [Hardhat](https://github.com/NomicFoundation/hardhat),.. * **L2s**: [Optimism](https://github.com/bluealloy/revm/tree/main/crates/op-revm), [Coinbase](https://www.base.org/), [Scroll](https://github.com/scroll-tech/revm),.. -* **zkVM**: [Risc0](https://github.com/risc0/risc0-ethereum), [Succinct](https://github.com/succinctlabs/rsp),.. +* **zkVM**: [Risc0](https://github.com/risc0/risc0-ethereum), [Succinct](https://github.com/succinctlabs/rsp), [Boundless](https://github.com/boundless-xyz/reth).. The full list of projects that use Revm is available in the [awesome-revm](https://bluealloy.github.io/revm/awesome.html) section of the book. @@ -78,4 +78,4 @@ If `gmp` feature flag is used, GPL code gets compiled, if enabled please make su ### Security -For any security questions or findings, please reach out to me directly via email at dragan0rakita@gmail.com or contact me on Keybase under the username @draganrakita. +For any security questions or findings, please reach out to me directly via email at [dragan0rakita@gmail.com](mailto:dragan0rakita@gmail.com) or contact me on Keybase under the username @draganrakita. diff --git a/bins/revme/CHANGELOG.md b/bins/revme/CHANGELOG.md index be4f0350ce..835ead23f5 100644 --- a/bins/revme/CHANGELOG.md +++ b/bins/revme/CHANGELOG.md @@ -7,6 +7,70 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [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 + +- updated the following local packages: revm-primitives, revm-bytecode, revm-context, revm-state, revm-database-interface, revm-context-interface, revm-database, revm-inspector, revm, revm-statetest-types + +## [8.2.0](https://github.com/bluealloy/revm/compare/revme-v8.0.1...revme-v8.2.0) - 2025-10-09 + +## [8.0.1](https://github.com/bluealloy/revm/compare/revme-v8.0.0...revme-v8.0.1) - 2025-10-09 + +### Other + +- use NoOpInspector for inspector benches ([#3060](https://github.com/bluealloy/revm/pull/3060)) + +## [8.0.0](https://github.com/bluealloy/revm/compare/revme-v7.2.3...revme-v8.0.0) - 2025-10-07 + +### Added + +- *(revme)* ef blockchain tests cli ([#2935](https://github.com/bluealloy/revm/pull/2935)) + +### Fixed + +- *(revme)* Insert block hashes in State ([#3024](https://github.com/bluealloy/revm/pull/3024)) +- support 0x prefix in evmrunner hex input ([#2970](https://github.com/bluealloy/revm/pull/2970)) +- *(revme)* Avoid panic on non-UTF filenames in statetest runner ([#2948](https://github.com/bluealloy/revm/pull/2948)) + +### Other + +- changelog update for v87 ([#3056](https://github.com/bluealloy/revm/pull/3056)) +- pretty print state in revme statetest ([#2979](https://github.com/bluealloy/revm/pull/2979)) +- Fix CLI exit code for invalid bytecode input ([#2968](https://github.com/bluealloy/revm/pull/2968)) + +## [7.2.3](https://github.com/bluealloy/revm/compare/revme-v7.2.2...revme-v7.2.3) - 2025-09-23 + +### Other + +- updated the following local packages: revm-context-interface, revm-context, revm-inspector, revm, revm-statetest-types + ## [7.2.2](https://github.com/bluealloy/revm/compare/revme-v7.2.1...revme-v7.2.2) - 2025-08-23 ### Other @@ -38,14 +102,6 @@ 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). -## [Unreleased] - -## [7.2.3](https://github.com/bluealloy/revm/compare/revme-v7.2.2...revme-v7.2.3) - 2025-09-23 - -### Other - -- updated the following local packages: revm-context-interface, revm-context, revm-inspector, revm, revm-statetest-types - ## [7.1.0](https://github.com/bluealloy/revm/compare/revme-v7.0.4...revme-v7.1.0) - 2025-07-23 ### Added diff --git a/bins/revme/Cargo.toml b/bins/revme/Cargo.toml index 6dc9d6ca3c..5ba0504a1d 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 = "7.2.3" +version = "9.0.1" 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"] } +revm = { workspace = true, features = [ + "std", + "c-kzg", + "blst", + "tracer", + "parse", +] } statetest-types.workspace = true + +# criterion criterion.workspace = true # alloy diff --git a/bins/revme/src/cmd.rs b/bins/revme/src/cmd.rs index 600ca8ce4c..2043f92db0 100644 --- a/bins/revme/src/cmd.rs +++ b/bins/revme/src/cmd.rs @@ -1,4 +1,5 @@ pub mod bench; +pub mod blockchaintest; pub mod bytecode; pub mod evmrunner; pub mod statetest; @@ -11,12 +12,18 @@ use clap::Parser; pub enum MainCmd { /// Execute Ethereum state tests. Statetest(statetest::Cmd), + /// Execute Ethereum state tests. + Stest(statetest::Cmd), /// Run arbitrary EVM bytecode. Evm(evmrunner::Cmd), /// Print the structure of an EVM bytecode. Bytecode(bytecode::Cmd), /// Run bench from specified list. Bench(bench::Cmd), + /// Execute Ethereum blockchain tests. + Blockchaintest(blockchaintest::Cmd), + /// Execute Ethereum blockchain tests. + Btest(blockchaintest::Cmd), } #[derive(Debug, thiserror::Error)] @@ -24,6 +31,8 @@ pub enum Error { #[error(transparent)] Statetest(#[from] statetest::Error), #[error(transparent)] + Blockchaintest(#[from] blockchaintest::Error), + #[error(transparent)] EvmRunnerErrors(#[from] evmrunner::Errors), #[error("Custom error: {0}")] Custom(&'static str), @@ -32,14 +41,15 @@ pub enum Error { impl MainCmd { pub fn run(&self) -> Result<(), Error> { match self { - Self::Statetest(cmd) => cmd.run()?, + Self::Statetest(cmd) | Self::Stest(cmd) => cmd.run()?, Self::Evm(cmd) => cmd.run()?, Self::Bytecode(cmd) => { - cmd.run(); + cmd.run()?; } Self::Bench(cmd) => { cmd.run(); } + Self::Blockchaintest(cmd) | Self::Btest(cmd) => cmd.run()?, } Ok(()) } 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..bbeaee7de5 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,12 @@ 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::{StorageKey, StorageValue}, state::{AccountInfo, Bytecode}, Context, ExecuteEvm, MainBuilder, MainContext, }; 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 5445d2fee9..222a7ecc7d 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::CountInspector; + use revm::{ bytecode::Bytecode, + context::TxEnv, + database::{BenchmarkDB, BENCH_CALLER, BENCH_TARGET}, + inspector::NoOpInspector, primitives::{bytes, hex, Bytes, TxKind}, Context, ExecuteEvm, InspectEvm, MainBuilder, MainContext, }; @@ -15,7 +16,7 @@ pub fn run(criterion: &mut Criterion) { .with_db(BenchmarkDB::new_bytecode(bytecode.clone())) .modify_cfg_chained(|c| c.disable_nonce_check = true) .build_mainnet() - .with_inspector(CountInspector::new()); + .with_inspector(NoOpInspector {}); let tx = TxEnv::builder() .caller(BENCH_CALLER) 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 new file mode 100644 index 0000000000..8f60690b3d --- /dev/null +++ b/bins/revme/src/cmd/blockchaintest.rs @@ -0,0 +1,1120 @@ +pub mod post_block; +pub mod pre_block; + +use clap::Parser; + +use revm::{ + 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::AccountInfo, + Context, Database, ExecuteCommitEvm, ExecuteEvm, InspectEvm, MainBuilder, MainContext, +}; +use serde_json::json; +use statetest_types::blockchain::{ + Account, BlockchainTest, BlockchainTestCase, ForkSpec, Withdrawal, +}; +use std::collections::BTreeMap; +use std::fs; +use std::path::{Path, PathBuf}; +use std::time::Instant; +use thiserror::Error; +use walkdir::{DirEntry, WalkDir}; + +/// `blockchaintest` subcommand +#[derive(Parser, Debug)] +pub struct Cmd { + /// Path to folder or file containing the blockchain tests + /// + /// If multiple paths are specified they will be run in sequence. + /// + /// Folders will be searched recursively for files with the extension `.json`. + #[arg(required = true, num_args = 1..)] + paths: Vec, + /// Omit progress output + #[arg(long)] + omit_progress: bool, + /// Keep going after a test failure + #[arg(long, alias = "no-fail-fast")] + keep_going: bool, + /// Print environment information (pre-state, post-state, env) when an error occurs + #[arg(long)] + print_env_on_error: bool, + /// Output results in JSON format + #[arg(long)] + json: bool, +} + +impl Cmd { + /// Runs `blockchaintest` command. + pub fn run(&self) -> Result<(), Error> { + for path in &self.paths { + if !path.exists() { + return Err(Error::PathNotFound(path.clone())); + } + + if !self.json { + println!("\nRunning blockchain tests in {}...", path.display()); + } + let test_files = find_all_json_tests(path); + + if test_files.is_empty() { + return Err(Error::NoJsonFiles(path.clone())); + } + + run_tests( + test_files, + self.omit_progress, + self.keep_going, + self.print_env_on_error, + self.json, + )?; + } + Ok(()) + } +} + +/// 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, + omit_progress: bool, + keep_going: bool, + print_env_on_error: bool, + json_output: bool, +) -> Result<(), Error> { + let mut passed = 0; + let mut failed = 0; + let mut skipped = 0; + let mut failed_paths = Vec::new(); + + let start_time = Instant::now(); + let total_files = test_files.len(); + + for (file_index, file_path) in test_files.into_iter().enumerate() { + let current_file = file_index + 1; + if skip_test(&file_path) { + skipped += 1; + if json_output { + let output = json!({ + "file": file_path.display().to_string(), + "status": "skipped", + "reason": "known_issue" + }); + println!("{}", serde_json::to_string(&output).unwrap()); + } else if !omit_progress { + println!( + "Skipping ({}/{}): {}", + current_file, + total_files, + file_path.display() + ); + } + continue; + } + + let result = run_test_file(&file_path, json_output, print_env_on_error); + + match result { + Ok(test_count) => { + passed += test_count; + if json_output { + // JSON output handled in run_test_file + } else if !omit_progress { + println!( + "✓ ({}/{}) {} ({} tests)", + current_file, + total_files, + file_path.display(), + test_count + ); + } + } + Err(e) => { + failed += 1; + if keep_going { + failed_paths.push(file_path.clone()); + } + if json_output { + let output = json!({ + "file": file_path.display().to_string(), + "error": e.to_string(), + "status": "failed" + }); + println!("{}", serde_json::to_string(&output).unwrap()); + } else if !omit_progress { + eprintln!( + "✗ ({}/{}) {} - {}", + current_file, + total_files, + file_path.display(), + e + ); + } + + if !keep_going { + return Err(e); + } + } + } + } + + let duration = start_time.elapsed(); + + if json_output { + let results = json!({ + "summary": { + "passed": passed, + "failed": failed, + "skipped": skipped, + "duration_secs": duration.as_secs_f64(), + } + }); + println!("{}", serde_json::to_string(&results).unwrap()); + } else { + // Print failed test paths if keep-going was enabled + if keep_going && !failed_paths.is_empty() { + println!("\nFailed test files:"); + for path in &failed_paths { + println!(" {}", path.display()); + } + } + + println!("\nTest results:"); + println!(" Passed: {passed}"); + println!(" Failed: {failed}"); + println!(" Skipped: {skipped}"); + println!(" Time: {:.2}s", duration.as_secs_f64()); + } + + if failed > 0 { + Err(Error::TestsFailed { failed }) + } else { + Ok(()) + } +} + +/// Run tests from a single file +fn run_test_file( + file_path: &Path, + json_output: bool, + print_env_on_error: bool, +) -> Result { + let content = + fs::read_to_string(file_path).map_err(|e| Error::FileRead(file_path.to_path_buf(), e))?; + + let blockchain_test: BlockchainTest = serde_json::from_str(&content) + .map_err(|e| Error::JsonDecode(file_path.to_path_buf(), e))?; + + let mut test_count = 0; + + for (test_name, test_case) in blockchain_test.0 { + if json_output { + // Output test start in JSON format + let output = json!({ + "test": test_name, + "file": file_path.display().to_string(), + "status": "running" + }); + println!("{}", serde_json::to_string(&output).unwrap()); + } else { + println!(" Running: {test_name}"); + } + // Execute the blockchain test + let result = execute_blockchain_test(&test_case, print_env_on_error, json_output); + + match result { + Ok(()) => { + if json_output { + let output = json!({ + "test": test_name, + "file": file_path.display().to_string(), + "status": "passed" + }); + println!("{}", serde_json::to_string(&output).unwrap()); + } + test_count += 1; + } + Err(e) => { + if json_output { + let output = json!({ + "test": test_name, + "file": file_path.display().to_string(), + "status": "failed", + "error": e.to_string() + }); + println!("{}", serde_json::to_string(&output).unwrap()); + } + return Err(Error::TestExecution { + test_name: test_name.clone(), + test_path: file_path.to_path_buf(), + error: e.to_string(), + }); + } + } + } + + Ok(test_count) +} + +/// Debug information captured during test execution +#[derive(Debug, Clone)] +struct DebugInfo { + /// Initial pre-state before any execution + pre_state: HashMap)>, + /// Transaction environment + tx_env: Option, + /// Block environment + block_env: revm::context::block::BlockEnv, + /// Configuration environment + cfg_env: CfgEnv, + /// Block index where error occurred + block_idx: usize, + /// Transaction index where error occurred + tx_idx: usize, + /// Withdrawals in the block + withdrawals: Option>, +} + +impl DebugInfo { + /// Capture current state from the State database + fn capture_committed_state( + state: &State, + ) -> HashMap)> { + 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::default(); + for (key, value) in &plain_account.storage { + storage.insert(*key, *value); + } + committed_state.insert(*address, (plain_account.info.clone(), storage)); + } + } + + committed_state + } +} + +/// Validate post state against expected values +fn validate_post_state( + state: &mut State, + expected_post_state: &BTreeMap, + debug_info: &DebugInfo, + print_env_on_error: bool, +) -> Result<(), TestExecutionError> { + for (address, expected_account) in expected_post_state { + // Load account from final state + let actual_account = state + .load_cache_account(*address) + .map_err(|e| TestExecutionError::Database(format!("Account load failed: {e}")))?; + let info = actual_account + .account + .as_ref() + .map(|a| a.info.clone()) + .unwrap_or_default(); + + // 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), + }); + } + + // 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), + }); + } + + // 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())), + }); + } + } 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(), + }); + } + } + + // 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() { + 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_unexpected[{slot}]"), + expected: "0x0".to_string(), + actual: format!("{actual_value}"), + }); + } + } + } + + // Validate storage slots + for (slot, expected_value) in &expected_account.storage { + let actual_value = state.storage(*address, *slot); + 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}"), + }); + } + } + } + Ok(()) +} + +/// Print comprehensive error information including environment and state comparison +fn print_error_with_state( + debug_info: &DebugInfo, + current_state: &State, + expected_post_state: Option<&BTreeMap>, +) { + eprintln!("\n========== TEST EXECUTION ERROR =========="); + + // Print error location + eprintln!( + "\n📍 Error occurred at block {} transaction {}", + debug_info.block_idx, debug_info.tx_idx + ); + + // Print configuration environment + eprintln!("\n📋 Configuration Environment:"); + eprintln!(" Spec ID: {:?}", debug_info.cfg_env.spec); + eprintln!(" Chain ID: {}", debug_info.cfg_env.chain_id); + eprintln!( + " Limit contract code size: {:?}", + debug_info.cfg_env.limit_contract_code_size + ); + eprintln!( + " Limit contract initcode size: {:?}", + debug_info.cfg_env.limit_contract_initcode_size + ); + + // Print block environment + eprintln!("\n🔨 Block Environment:"); + eprintln!(" Number: {}", debug_info.block_env.number); + eprintln!(" Timestamp: {}", debug_info.block_env.timestamp); + eprintln!(" Gas limit: {}", debug_info.block_env.gas_limit); + eprintln!(" Base fee: {:?}", debug_info.block_env.basefee); + 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 + ); + + // Print withdrawals + if let Some(withdrawals) = &debug_info.withdrawals { + eprintln!(" Withdrawals: {} items", withdrawals.len()); + if !withdrawals.is_empty() { + for (i, withdrawal) in withdrawals.iter().enumerate().take(3) { + eprintln!(" Withdrawal {i}:"); + eprintln!(" Index: {}", withdrawal.index); + eprintln!(" Validator Index: {}", withdrawal.validator_index); + eprintln!(" Address: {:?}", withdrawal.address); + eprintln!( + " Amount: {} Gwei ({:.6} ETH)", + withdrawal.amount, + withdrawal.amount.to::() as f64 / 1_000_000_000.0 + ); + } + if withdrawals.len() > 3 { + eprintln!(" ... and {} more withdrawals", withdrawals.len() - 3); + } + } + } + + // Print transaction environment if available + if let Some(tx_env) = &debug_info.tx_env { + eprintln!("\n📄 Transaction Environment:"); + eprintln!(" Transaction type: {}", tx_env.tx_type); + eprintln!(" Caller: {:?}", tx_env.caller); + eprintln!(" Gas limit: {}", tx_env.gas_limit); + eprintln!(" Gas price: {}", tx_env.gas_price); + eprintln!(" Gas priority fee: {:?}", tx_env.gas_priority_fee); + eprintln!(" Transaction kind: {:?}", tx_env.kind); + eprintln!(" Value: {}", tx_env.value); + eprintln!(" Data length: {} bytes", tx_env.data.len()); + if !tx_env.data.is_empty() { + let preview_len = std::cmp::min(64, tx_env.data.len()); + eprintln!( + " Data preview: 0x{}{}", + hex::encode(&tx_env.data[..preview_len]), + if tx_env.data.len() > 64 { "..." } else { "" } + ); + } + eprintln!(" Nonce: {}", tx_env.nonce); + eprintln!(" Chain ID: {:?}", tx_env.chain_id); + eprintln!(" Access list: {} entries", tx_env.access_list.len()); + if !tx_env.access_list.is_empty() { + for (i, access) in tx_env.access_list.iter().enumerate().take(3) { + eprintln!( + " Access {}: address={:?}, {} storage keys", + i, + access.address, + access.storage_keys.len() + ); + } + if tx_env.access_list.len() > 3 { + eprintln!( + " ... and {} more access list entries", + tx_env.access_list.len() - 3 + ); + } + } + eprintln!(" Blob hashes: {} blobs", tx_env.blob_hashes.len()); + if !tx_env.blob_hashes.is_empty() { + for (i, hash) in tx_env.blob_hashes.iter().enumerate().take(3) { + eprintln!(" Blob {i}: {hash:?}"); + } + if tx_env.blob_hashes.len() > 3 { + eprintln!( + " ... and {} more blob hashes", + tx_env.blob_hashes.len() - 3 + ); + } + } + eprintln!(" Max fee per blob gas: {}", tx_env.max_fee_per_blob_gas); + eprintln!( + " Authorization list: {} items", + tx_env.authorization_list.len() + ); + if !tx_env.authorization_list.is_empty() { + eprintln!(" (EIP-7702 authorizations present)"); + } + } else { + eprintln!( + "\n📄 Transaction Environment: Not available (error occurred before tx creation)" + ); + } + + // Print state comparison + eprintln!("\n💾 Pre-State (Initial):"); + for (address, (info, storage)) in &debug_info.pre_state { + 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()) + ); + if !storage.is_empty() { + eprintln!(" Storage ({} slots):", storage.len()); + for (key, value) in storage.iter().take(5) { + 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 { + 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()) + ); + if !storage.is_empty() { + eprintln!(" Storage ({} slots):", storage.len()); + for (key, value) in storage.iter().take(5) { + eprintln!(" {key:?} => {value:?}"); + } + if storage.len() > 5 { + eprintln!(" ... and {} more slots", storage.len() - 5); + } + } + } + + // Print expected post-state if available + if let Some(expected_post_state) = expected_post_state { + eprintln!("\n✅ Expected Post-State:"); + for (address, account) in expected_post_state { + eprintln!(" Account {address:?}:"); + eprintln!(" Balance: 0x{:x}", account.balance); + eprintln!(" Nonce: {}", account.nonce); + if !account.code.is_empty() { + eprintln!(" Code size: {} bytes", account.code.len()); + } + if !account.storage.is_empty() { + eprintln!(" Storage ({} slots):", account.storage.len()); + for (key, value) in account.storage.iter().take(5) { + eprintln!(" {key:?} => {value:?}"); + } + if account.storage.len() > 5 { + eprintln!(" ... and {} more slots", account.storage.len() - 5); + } + } + } + } + + eprintln!("\n===========================================\n"); +} + +/// Execute a single blockchain test case +fn execute_blockchain_test( + test_case: &BlockchainTestCase, + print_env_on_error: bool, + json_output: bool, +) -> Result<(), TestExecutionError> { + // Skip all transition forks for now. + if matches!( + test_case.network, + ForkSpec::ByzantiumToConstantinopleAt5 + | ForkSpec::ParisToShanghaiAtTime15k + | ForkSpec::ShanghaiToCancunAtTime15k + | ForkSpec::CancunToPragueAtTime15k + | ForkSpec::PragueToOsakaAtTime15k + | ForkSpec::BPO1ToBPO2AtTime15k + ) { + eprintln!("⚠️ Skipping transition fork: {:?}", test_case.network); + return Ok(()); + } + + // Create database with initial state + let mut state = State::builder().build(); + + // Capture pre-state for debug info + let mut pre_state_debug = HashMap::default(); + + // Insert genesis state into database + let genesis_state = test_case.pre.clone().into_genesis_state(); + for (address, account) in genesis_state { + let account_info = AccountInfo { + balance: account.balance, + nonce: account.nonce, + code_hash: revm::primitives::keccak256(&account.code), + code: Some(Bytecode::new_raw(account.code.clone())), + }; + + // Store for debug info + if print_env_on_error { + pre_state_debug.insert(address, (account_info.clone(), account.storage.clone())); + } + + state.insert_account_with_storage(address, account_info, account.storage); + } + + // insert genesis hash + state + .block_hashes + .insert(0, test_case.genesis_block_header.hash); + + // Setup configuration based on fork + let spec_id = fork_to_spec_id(test_case.network); + let mut cfg = CfgEnv::default(); + cfg.spec = spec_id; + + // Genesis block is not used yet. + let mut parent_block_hash = Some(test_case.genesis_block_header.hash); + let mut parent_excess_blob_gas = test_case + .genesis_block_header + .excess_blob_gas + .unwrap_or_default() + .to::(); + let mut block_env = test_case.genesis_block_env(); + + // Process each block in the test + for (block_idx, block) in test_case.blocks.iter().enumerate() { + println!("Run block {block_idx}/{}", test_case.blocks.len()); + + // Check if this block should fail + let should_fail = block.expect_exception.is_some(); + + let transactions = block.transactions.as_deref().unwrap_or_default(); + + // Update block environment for this blockk + + let mut block_hash = None; + let mut beacon_root = None; + let this_excess_blob_gas; + + if let Some(block_header) = block.block_header.as_ref() { + block_hash = Some(block_header.hash); + beacon_root = block_header.parent_beacon_block_root; + block_env = block_header.to_block_env(Some(BlobExcessGasAndPrice::new_with_spec( + parent_excess_blob_gas, + spec_id, + ))); + this_excess_blob_gas = block_header.excess_blob_gas.map(|i| i.to::()); + } else { + this_excess_blob_gas = None; + } + + // Create EVM context for each transaction to ensure fresh state access + let evm_context = Context::mainnet() + .with_block(&block_env) + .with_cfg(&cfg) + .with_db(&mut state); + + // Build and execute with EVM - always use inspector when JSON output is enabled + let mut evm = evm_context.build_mainnet_with_inspector(TracerEip3155::new_stdout()); + + // Pre block system calls + pre_block::pre_block_transition(&mut evm, spec_id, parent_block_hash, beacon_root); + + // Execute each transaction in the block + for (tx_idx, tx) in transactions.iter().enumerate() { + if tx.sender.is_none() { + if print_env_on_error { + let debug_info = DebugInfo { + pre_state: pre_state_debug.clone(), + tx_env: None, + block_env: block_env.clone(), + cfg_env: cfg.clone(), + block_idx, + tx_idx, + withdrawals: block.withdrawals.clone(), + }; + print_error_with_state( + &debug_info, + evm.ctx().db_ref(), + test_case.post_state.as_ref(), + ); + } + if json_output { + let output = json!({ + "block": block_idx, + "tx": tx_idx, + "error": "missing sender", + "status": "skipped" + }); + println!("{}", serde_json::to_string(&output).unwrap()); + } else { + eprintln!("⚠️ Skipping block {block_idx} due to missing sender"); + } + break; // Skip to next block + } + + let tx_env = match tx.to_tx_env() { + Ok(env) => env, + Err(e) => { + if should_fail { + // Expected failure during tx env creation + continue; + } + if print_env_on_error { + let debug_info = DebugInfo { + pre_state: pre_state_debug.clone(), + tx_env: None, + block_env: block_env.clone(), + cfg_env: cfg.clone(), + block_idx, + tx_idx, + withdrawals: block.withdrawals.clone(), + }; + print_error_with_state( + &debug_info, + evm.ctx().db_ref(), + test_case.post_state.as_ref(), + ); + } + if json_output { + let output = json!({ + "block": block_idx, + "tx": tx_idx, + "error": format!("tx env creation error: {e}"), + "status": "skipped" + }); + println!("{}", serde_json::to_string(&output).unwrap()); + } else { + eprintln!( + "⚠️ Skipping block {block_idx} due to transaction env creation error: {e}" + ); + } + break; // Skip to next block + } + }; + + // If JSON output requested, output transaction details + let execution_result = if json_output { + evm.inspect_tx(tx_env.clone()) + } else { + evm.transact(tx_env.clone()) + }; + + match execution_result { + Ok(result) => { + if should_fail { + // Unexpected success - should have failed but didn't + // If not expected to fail, use inspector to trace the transaction + if print_env_on_error { + // Re-run with inspector to get detailed trace + if json_output { + eprintln!("=== Transaction trace (unexpected success) ==="); + } + let _ = evm.inspect_tx(tx_env.clone()); + } + + if print_env_on_error { + let debug_info = DebugInfo { + pre_state: pre_state_debug.clone(), + tx_env: Some(tx_env.clone()), + block_env: block_env.clone(), + cfg_env: cfg.clone(), + block_idx, + tx_idx, + withdrawals: block.withdrawals.clone(), + }; + print_error_with_state( + &debug_info, + evm.ctx().db_ref(), + test_case.post_state.as_ref(), + ); + } + let 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}"), + "gas_used": result.result.gas_used(), + "status": "unexpected_success" + }); + println!("{}", serde_json::to_string(&output).unwrap()); + } else { + eprintln!( + "⚠️ Skipping block {block_idx} due to expected failure: {exception}" + ); + } + break; // Skip to next block + } + evm.commit(result.state); + } + Err(e) => { + if !should_fail { + // Unexpected error - use inspector to trace the transaction + if print_env_on_error { + if json_output { + eprintln!("=== Transaction trace (unexpected failure) ==="); + } + let _ = evm.inspect_tx(tx_env.clone()); + } + + if print_env_on_error { + let debug_info = DebugInfo { + pre_state: pre_state_debug.clone(), + tx_env: Some(tx_env.clone()), + block_env: block_env.clone(), + cfg_env: cfg.clone(), + block_idx, + tx_idx, + withdrawals: block.withdrawals.clone(), + }; + print_error_with_state( + &debug_info, + evm.ctx().db_ref(), + test_case.post_state.as_ref(), + ); + } + if json_output { + let output = json!({ + "block": block_idx, + "tx": tx_idx, + "error": format!("{e:?}"), + "status": "unexpected_failure" + }); + println!("{}", serde_json::to_string(&output).unwrap()); + } else { + eprintln!( + "⚠️ Skipping block {block_idx} due to unexpected failure: {e:?}" + ); + } + break; // Skip to next block + } else if json_output { + // Expected failure + let output = json!({ + "block": block_idx, + "tx": tx_idx, + "error": format!("{e:?}"), + "status": "expected_failure" + }); + println!("{}", serde_json::to_string(&output).unwrap()); + } + } + } + } + + // uncle rewards are not implemented yet + post_block::post_block_transition( + &mut evm, + &block_env, + block.withdrawals.as_deref().unwrap_or_default(), + spec_id, + ); + + // insert present block hash. + state + .block_hashes + .insert(block_env.number.to::(), block_hash.unwrap_or_default()); + + parent_block_hash = block_hash; + if let Some(excess_blob_gas) = this_excess_blob_gas { + parent_excess_blob_gas = excess_blob_gas; + } + + state.merge_transitions(BundleRetention::Reverts); + } + + // Validate post state if present + if let Some(expected_post_state) = &test_case.post_state { + // Create debug info for post-state validation + let debug_info = DebugInfo { + pre_state: pre_state_debug.clone(), + tx_env: None, // Last transaction is done + block_env: block_env.clone(), + cfg_env: cfg.clone(), + block_idx: test_case.blocks.len(), + tx_idx: 0, + withdrawals: test_case.blocks.last().and_then(|b| b.withdrawals.clone()), + }; + validate_post_state( + &mut state, + expected_post_state, + &debug_info, + print_env_on_error, + )?; + } + + Ok(()) +} + +/// Convert ForkSpec to SpecId +fn fork_to_spec_id(fork: ForkSpec) -> SpecId { + match fork { + ForkSpec::Frontier => SpecId::FRONTIER, + ForkSpec::Homestead | ForkSpec::FrontierToHomesteadAt5 => SpecId::HOMESTEAD, + ForkSpec::EIP150 | ForkSpec::HomesteadToDaoAt5 | ForkSpec::HomesteadToEIP150At5 => { + SpecId::TANGERINE + } + ForkSpec::EIP158 => SpecId::SPURIOUS_DRAGON, + ForkSpec::Byzantium + | ForkSpec::EIP158ToByzantiumAt5 + | ForkSpec::ByzantiumToConstantinopleFixAt5 => SpecId::BYZANTIUM, + ForkSpec::Constantinople | ForkSpec::ByzantiumToConstantinopleAt5 => SpecId::PETERSBURG, + ForkSpec::ConstantinopleFix => SpecId::PETERSBURG, + ForkSpec::Istanbul => SpecId::ISTANBUL, + ForkSpec::Berlin => SpecId::BERLIN, + ForkSpec::London | ForkSpec::BerlinToLondonAt5 => SpecId::LONDON, + ForkSpec::Paris | ForkSpec::ParisToShanghaiAtTime15k => SpecId::MERGE, + ForkSpec::Shanghai => SpecId::SHANGHAI, + 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 + } +} + +/// Check if a test should be skipped based on its filename +fn skip_test(path: &Path) -> bool { + let path_str = path.to_str().unwrap_or_default(); + // blobs excess gas calculation is not supported or osaka BPO configuration + if path_str.contains("paris/eip7610_create_collision") + || path_str.contains("cancun/eip4844_blobs") + || path_str.contains("prague/eip7251_consolidations") + || path_str.contains("prague/eip7685_general_purpose_el_requests") + || path_str.contains("prague/eip7002_el_triggerable_withdrawals") + || path_str.contains("osaka/eip7918_blob_reserve_price") + { + return true; + } + + let name = path.file_name().unwrap().to_str().unwrap(); + // Add any problematic tests here that should be skipped + matches!( + name, + // Test check if gas price overflows, we handle this correctly but does not match tests specific exception. + "CreateTransactionHighNonce.json" + + // Test with some storage check. + | "RevertInCreateInInit_Paris.json" + | "RevertInCreateInInit.json" + | "dynamicAccountOverwriteEmpty.json" + | "dynamicAccountOverwriteEmpty_Paris.json" + | "RevertInCreateInInitCreate2Paris.json" + | "create2collisionStorage.json" + | "RevertInCreateInInitCreate2.json" + | "create2collisionStorageParis.json" + | "InitCollision.json" + | "InitCollisionParis.json" + + // Malformed value. + | "ValueOverflow.json" + | "ValueOverflowParis.json" + + // These tests are passing, but they take a lot of time to execute so we are going to skip them. + | "Call50000_sha256.json" + | "static_Call50000_sha256.json" + | "loopMul.json" + | "CALLBlake2f_MaxRounds.json" + // TODO tests not checked, maybe related to parent block hashes as it is currently not supported in test. + | "scenarios.json" + // IT seems that post state is wrong, we properly handle max blob gas and state should stay the same. + | "invalid_tx_max_fee_per_blob_gas.json" + | "correct_increasing_blob_gas_costs.json" + | "correct_decreasing_blob_gas_costs.json" + + // test-fixtures/main/develop/blockchain_tests/prague/eip2935_historical_block_hashes_from_state/block_hashes/block_hashes_history.json + | "block_hashes_history.json" + ) +} + +#[derive(Debug, Error)] +pub enum TestExecutionError { + #[error("Database error: {0}")] + Database(String), + + #[error("Skipped fork: {0}")] + SkippedFork(String), + + #[error("Sender is required")] + SenderRequired, + + #[error("Expected failure at block {block_idx}, tx {tx_idx}: {message}")] + ExpectedFailure { + block_idx: usize, + tx_idx: usize, + message: String, + }, + + #[error("Unexpected failure at block {block_idx}, tx {tx_idx}: {error}")] + UnexpectedFailure { + block_idx: usize, + tx_idx: usize, + error: String, + }, + + #[error("Transaction env creation failed at block {block_idx}, tx {tx_idx}: {error}")] + TransactionEnvCreation { + block_idx: usize, + tx_idx: usize, + error: String, + }, + + #[error("Unexpected revert at block {block_idx}, tx {tx_idx}, gas used: {gas_used}")] + UnexpectedRevert { + block_idx: usize, + tx_idx: usize, + gas_used: u64, + }, + + #[error("Unexpected halt at block {block_idx}, tx {tx_idx}: {reason:?}, gas used: {gas_used}")] + UnexpectedHalt { + block_idx: usize, + tx_idx: usize, + reason: HaltReason, + gas_used: u64, + }, + + #[error( + "Post-state validation failed for {address:?}.{field}: expected {expected}, got {actual}" + )] + PostStateValidation { + address: Address, + field: String, + expected: String, + actual: String, + }, +} + +#[derive(Debug, Error)] +pub enum Error { + #[error("Path not found: {0}")] + PathNotFound(PathBuf), + + #[error("No JSON files found in: {0}")] + NoJsonFiles(PathBuf), + + #[error("Failed to read file {0}: {1}")] + FileRead(PathBuf, std::io::Error), + + #[error("Failed to decode JSON from {0}: {1}")] + JsonDecode(PathBuf, serde_json::Error), + + #[error("Test execution failed for {test_name} in {test_path}: {error}")] + TestExecution { + test_name: String, + test_path: PathBuf, + error: String, + }, + + #[error("Directory traversal error: {0}")] + WalkDir(#[from] walkdir::Error), + + #[error("{failed} tests failed")] + TestsFailed { failed: usize }, +} diff --git a/bins/revme/src/cmd/blockchaintest/post_block.rs b/bins/revme/src/cmd/blockchaintest/post_block.rs new file mode 100644 index 0000000000..f768e389e1 --- /dev/null +++ b/bins/revme/src/cmd/blockchaintest/post_block.rs @@ -0,0 +1,68 @@ +use revm::{ + context::{Block, ContextTr}, + database::State, + handler::EvmTr, + primitives::{hardfork::SpecId, ONE_ETHER, ONE_GWEI}, + Database, SystemCallCommitEvm, +}; +use statetest_types::blockchain::Withdrawal; + +/// Post block transition that includes: +/// * Block and uncle rewards before the Merge/Paris hardfork. +/// * system calls +/// +/// # Note +/// +/// Uncle rewards are not implemented yet. +#[inline] +pub fn post_block_transition< + 'a, + DB: Database + 'a, + EVM: SystemCallCommitEvm + + EvmTr>>, +>( + evm: &mut EVM, + block: impl Block, + withdrawals: &[Withdrawal], + spec: SpecId, +) { + // 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)]); + } + + // withdrawals + if spec.is_enabled_in(SpecId::SHANGHAI) { + for withdrawal in withdrawals { + evm.ctx_mut() + .db_mut() + .increment_balances(vec![( + withdrawal.address, + withdrawal.amount.to::().saturating_mul(ONE_GWEI), + )]) + .expect("Db actions to pass"); + } + } +} + +/// Block reward for a block. +#[inline] +pub const fn block_reward(spec: SpecId, ommers: usize) -> u128 { + if spec.is_enabled_in(SpecId::MERGE) { + return 0; + } + + let reward = if spec.is_enabled_in(SpecId::CONSTANTINOPLE) { + ONE_ETHER * 2 + } else if spec.is_enabled_in(SpecId::BYZANTIUM) { + ONE_ETHER * 3 + } else { + ONE_ETHER * 5 + }; + + reward + (reward >> 5) * ommers as u128 +} diff --git a/bins/revme/src/cmd/blockchaintest/pre_block.rs b/bins/revme/src/cmd/blockchaintest/pre_block.rs new file mode 100644 index 0000000000..90fb922740 --- /dev/null +++ b/bins/revme/src/cmd/blockchaintest/pre_block.rs @@ -0,0 +1,85 @@ +//! Pre block state transition + +use revm::{ + context::{Block, ContextTr}, + database::{Database, State}, + handler::EvmTr, + primitives::{address, hardfork::SpecId, Address, B256}, + SystemCallCommitEvm, +}; + +/// Pre block state transition +/// +/// # Note +/// +/// Contains only withdrawal processing. And it is missing block hash system call. +pub fn pre_block_transition< + 'a, + DB: Database + 'a, + EVM: SystemCallCommitEvm + + EvmTr>>, +>( + evm: &mut EVM, + spec: SpecId, + parent_block_hash: Option, + parent_beacon_block_root: Option, +) { + // skip system calls for block number 0 (Gensis block) + if evm.ctx().block().number() == 0 { + return; + } + + // blockhash system call + if let Some(parent_block_hash) = parent_block_hash { + system_call_eip2935_blockhash(spec, parent_block_hash, evm); + } + + if let Some(parent_beacon_block_root) = parent_beacon_block_root { + system_call_eip4788_beacon_root(spec, parent_beacon_block_root, evm); + } +} + +pub const HISTORY_STORAGE_ADDRESS: Address = address!("0x0000F90827F1C53a10cb7A02335B175320002935"); + +/// 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; + } + + 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_eip4788_beacon_root( + spec: SpecId, + parent_beacon_block_root: B256, + evm: &mut impl SystemCallCommitEvm, +) -> bool { + if !spec.is_enabled_in(SpecId::CANCUN) { + return true; + } + + 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/bytecode.rs b/bins/revme/src/cmd/bytecode.rs index ae5d5c89f8..636eac9ed9 100644 --- a/bins/revme/src/cmd/bytecode.rs +++ b/bins/revme/src/cmd/bytecode.rs @@ -17,18 +17,18 @@ fn trim_decode(input: &str) -> Option { impl Cmd { /// Runs bytecode command. - pub fn run(&self) { + pub fn run(&self) -> Result<(), super::Error> { if let Some(input_bytes) = &self.bytes { let Some(bytes) = trim_decode(input_bytes) else { - eprintln!("Invalid hex string"); - return; + // Fail on invalid hex to propagate a non-zero exit code + return Err(super::Error::Custom("Invalid hex string")); }; if bytes.starts_with(&[0xEF, 0x00]) { - eprintln!( - "EOF bytecode is not supported - EOF has been removed from ethereum plan." - ); - return; + // Fail on EOF bytecode as it's not supported + return Err(super::Error::Custom( + "EOF bytecode is not supported - EOF has been removed from ethereum plan.", + )); } println!("Legacy bytecode:"); @@ -55,5 +55,6 @@ impl Cmd { println!("No bytecode provided. EOF interactive mode has been removed."); println!("Please provide bytecode as a hex string argument."); } + Ok(()) } } diff --git a/bins/revme/src/cmd/evmrunner.rs b/bins/revme/src/cmd/evmrunner.rs index 4e54833cb9..7708f7a69f 100644 --- a/bins/revme/src/cmd/evmrunner.rs +++ b/bins/revme/src/cmd/evmrunner.rs @@ -1,9 +1,9 @@ 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, }; @@ -75,8 +75,9 @@ impl Cmd { unreachable!() }; - let bytecode = hex::decode(bytecode_str.trim()).map_err(|_| Errors::InvalidBytecode)?; - let input = hex::decode(self.input.trim()) + let bytecode = hex::decode(bytecode_str.trim().trim_start_matches("0x")) + .map_err(|_| Errors::InvalidBytecode)?; + let input = hex::decode(self.input.trim().trim_start_matches("0x")) .map_err(|_| Errors::InvalidInput)? .into(); diff --git a/bins/revme/src/cmd/statetest/merkle_trie.rs b/bins/revme/src/cmd/statetest/merkle_trie.rs index c6b6418787..b8af986d7a 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::{EmptyDB, PlainAccount, State}, +}; use triehash::sec_trie_root; pub struct TestValidationResult { diff --git a/bins/revme/src/cmd/statetest/runner.rs b/bins/revme/src/cmd/statetest/runner.rs index f989daa614..2ea90806c8 100644 --- a/bins/revme/src/cmd/statetest/runner.rs +++ b/bins/revme/src/cmd/statetest/runner.rs @@ -1,15 +1,15 @@ 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, }, + database, database_interface::EmptyDB, + inspector::{inspectors::TracerEip3155, InspectCommitEvm}, + primitives::U256, primitives::{hardfork::SpecId, Bytes, B256}, Context, ExecuteCommitEvm, MainBuilder, MainContext, }; @@ -86,7 +86,14 @@ pub fn find_all_json_tests(path: &Path) -> Vec { /// 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 { - let name = path.file_name().unwrap().to_str().unwrap(); + let path_str = path.to_str().unwrap_or_default(); + + // Skip tets that have storage for newly created account. + if path_str.contains("paris/eip7610_create_collision") { + return true; + } + + let name = path.file_name().unwrap().to_str().unwrap_or_default(); matches!( name, @@ -104,6 +111,7 @@ fn skip_test(path: &Path) -> bool { | "create2collisionStorageParis.json" | "InitCollision.json" | "InitCollisionParis.json" + | "test_init_collision_create_opcode.json" // Malformed value. | "ValueOverflow.json" @@ -214,7 +222,7 @@ fn check_evm_execution( expected_output: Option<&Bytes>, test_name: &str, exec_result: &Result, EVMError>, - db: &mut State, + db: &mut database::State, spec: SpecId, print_json_outcome: bool, ) -> Result<(), TestErrorKind> { @@ -464,10 +472,10 @@ fn debug_failed_test(ctx: DebugContext) { println!("\nExecution result: {exec_result:#?}"); println!("\nExpected exception: {:?}", ctx.test.expect_exception); - println!("\nState before: {:#?}", ctx.cache_state); + println!("\nState before:\n{}", ctx.cache_state.pretty_print()); println!( - "\nState after: {:#?}", - evm.ctx.journaled_state.database.cache + "\nState after:\n{}", + evm.ctx.journaled_state.database.cache.pretty_print() ); println!("\nSpecification: {:?}", ctx.cfg.spec); println!("\nTx: {:#?}", ctx.tx); diff --git a/book/src/contact.md b/book/src/contact.md index ff8e628f92..bced995c04 100644 --- a/book/src/contact.md +++ b/book/src/contact.md @@ -3,7 +3,7 @@ The git repository can be found at [https://github.com/bluealloy/revm/](https://github.com/bluealloy/revm/) -For questions please open a github issue or join the public telegram group: [https://t.me/+Ig4WDWOzikA3MzA0](https://t.me/+Ig4WDWOzikA3MzA0) +For questions please open a github issue or join the public [telegram group](https://t.me/+Ig4WDWOzikA3MzA0) ### Licence diff --git a/crates/bytecode/CHANGELOG.md b/crates/bytecode/CHANGELOG.md index 5aabf75f0e..4dc5acdcca 100644 --- a/crates/bytecode/CHANGELOG.md +++ b/crates/bytecode/CHANGELOG.md @@ -7,6 +7,47 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [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 + +- support legacy JumpTable serde format ([#3098](https://github.com/bluealloy/revm/pull/3098)) + +## [7.0.0](https://github.com/bluealloy/revm/compare/revm-bytecode-v6.2.2...revm-bytecode-v7.0.0) - 2025-10-07 + +### Added + +- in JumpTable use Bytes instead of BitVec ([#3014](https://github.com/bluealloy/revm/pull/3014)) + +### Fixed + +- *(bytecode)* exclude MLOAD from modifies_memory and update test ([#3004](https://github.com/bluealloy/revm/pull/3004)) +- Apply spelling corrections from PRs #2926, #2915, #2908 ([#2978](https://github.com/bluealloy/revm/pull/2978)) + +### Other + +- add boundless ([#3043](https://github.com/bluealloy/revm/pull/3043)) +- use offset_from_unsigned ([#2999](https://github.com/bluealloy/revm/pull/2999)) +- add SECURITY.md ([#2956](https://github.com/bluealloy/revm/pull/2956)) + ## [6.2.2](https://github.com/bluealloy/revm/compare/revm-bytecode-v6.2.1...revm-bytecode-v6.2.2) - 2025-08-23 ### Other diff --git a/crates/bytecode/Cargo.toml b/crates/bytecode/Cargo.toml index b35526c1c0..5312108ef8 100644 --- a/crates/bytecode/Cargo.toml +++ b/crates/bytecode/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "revm-bytecode" description = "EVM Bytecodes" -version = "6.2.2" +version = "7.1.1" authors.workspace = true edition.workspace = true keywords.workspace = true @@ -31,10 +31,14 @@ serde = { workspace = true, features = ["derive", "rc"], optional = true } paste = { workspace = true, optional = true } phf = { workspace = true, features = ["macros"], optional = true } +[dev-dependencies] +serde_json = { workspace = true } + [features] default = ["std", "parse"] std = [ "serde?/std", + "serde_json/std", "primitives/std", "bitvec/std", "phf?/std", diff --git a/crates/bytecode/src/bytecode.rs b/crates/bytecode/src/bytecode.rs index a6f09b7aa5..d3af31d5a0 100644 --- a/crates/bytecode/src/bytecode.rs +++ b/crates/bytecode/src/bytecode.rs @@ -8,7 +8,7 @@ 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, B256, KECCAK_EMPTY}; /// Main bytecode structure with all variants. #[derive(Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)] @@ -27,6 +27,13 @@ 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] diff --git a/crates/bytecode/src/legacy/analysis.rs b/crates/bytecode/src/legacy/analysis.rs index 193b188642..39e4a1e1d0 100644 --- a/crates/bytecode/src/legacy/analysis.rs +++ b/crates/bytecode/src/legacy/analysis.rs @@ -25,7 +25,7 @@ pub fn analyze_legacy(bytecode: Bytes) -> (JumpTable, Bytes) { opcode = unsafe { *iterator }; if opcode == opcode::JUMPDEST { // SAFETY: Jumps are max length of the code - unsafe { jumps.set_unchecked(iterator.offset_from(start) as usize, true) } + unsafe { jumps.set_unchecked(iterator.offset_from_unsigned(start), true) } iterator = unsafe { iterator.add(1) }; } else { let push_offset = opcode.wrapping_sub(opcode::PUSH1); diff --git a/crates/bytecode/src/legacy/jump_map.rs b/crates/bytecode/src/legacy/jump_map.rs index b4a99ebd7f..1347b44778 100644 --- a/crates/bytecode/src/legacy/jump_map.rs +++ b/crates/bytecode/src/legacy/jump_map.rs @@ -3,7 +3,7 @@ use core::{ cmp::Ordering, hash::{Hash, Hasher}, }; -use primitives::{hex, OnceLock}; +use primitives::{hex, Bytes, OnceLock}; use std::{fmt::Debug, sync::Arc}; /// A table of valid `jump` destinations. @@ -11,12 +11,12 @@ 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)] pub struct JumpTable { - /// Pointer into `table` to avoid `Arc` overhead on lookup. + /// Cached pointer to table data to avoid Arc overhead on lookup table_ptr: *const u8, /// Number of bits in the table. len: usize, /// Actual bit vec - table: Arc>, + table: Arc, } // SAFETY: BitVec data is immutable through Arc, pointer won't be invalidated @@ -47,13 +47,31 @@ impl Ord for JumpTable { } } +impl Debug for JumpTable { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("JumpTable") + .field("map", &hex::encode(self.table.as_ref())) + .finish() + } +} + +impl Default for JumpTable { + #[inline] + fn default() -> Self { + static DEFAULT: OnceLock = OnceLock::new(); + DEFAULT.get_or_init(|| Self::new(BitVec::default())).clone() + } +} + #[cfg(feature = "serde")] impl serde::Serialize for JumpTable { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { - self.table.serialize(serializer) + let mut bitvec = BitVec::::from_vec(self.table.to_vec()); + bitvec.resize(self.len, false); + bitvec.serialize(serializer) } } @@ -68,40 +86,21 @@ impl<'de> serde::Deserialize<'de> for JumpTable { } } -impl Debug for JumpTable { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct("JumpTable") - .field("map", &hex::encode(self.table.as_raw_slice())) - .finish() - } -} - -impl Default for JumpTable { - #[inline] - fn default() -> Self { - static DEFAULT: OnceLock = OnceLock::new(); - DEFAULT.get_or_init(|| Self::new(BitVec::default())).clone() - } -} - impl JumpTable { /// Create new JumpTable directly from an existing BitVec. + /// + /// Uses [`Self::from_bytes`] internally. + #[inline] pub fn new(jumps: BitVec) -> Self { - let table = Arc::new(jumps); - let table_ptr = table.as_raw_slice().as_ptr(); - let len = table.len(); - - Self { - table, - table_ptr, - len, - } + let bit_len = jumps.len(); + let bytes = jumps.into_vec().into(); + Self::from_bytes(bytes, bit_len) } /// Gets the raw bytes of the jump map. #[inline] pub fn as_slice(&self) -> &[u8] { - self.table.as_raw_slice() + &self.table } /// Gets the length of the jump map. @@ -120,21 +119,48 @@ impl JumpTable { /// /// Bit length represents number of used bits inside slice. /// + /// Uses [`Self::from_bytes`] internally. + /// /// # Panics /// /// Panics if number of bits in slice is less than bit_len. #[inline] pub fn from_slice(slice: &[u8], bit_len: usize) -> Self { + Self::from_bytes(Bytes::from(slice.to_vec()), bit_len) + } + + /// Create new JumpTable directly from an existing Bytes. + /// + /// Bit length represents number of used bits inside slice. + /// + /// Panics if bytes length is less than bit_len * 8. + #[inline] + pub fn from_bytes(bytes: Bytes, bit_len: usize) -> Self { + Self::from_bytes_arc(Arc::new(bytes), bit_len) + } + + /// Create new JumpTable directly from an existing Bytes. + /// + /// Bit length represents number of used bits inside slice. + /// + /// Panics if bytes length is less than bit_len * 8. + #[inline] + pub fn from_bytes_arc(table: Arc, bit_len: usize) -> Self { const BYTE_LEN: usize = 8; assert!( - slice.len() * BYTE_LEN >= bit_len, + table.len() * BYTE_LEN >= bit_len, "slice bit length {} is less than bit_len {}", - slice.len() * BYTE_LEN, + table.len() * BYTE_LEN, bit_len ); - let mut bitvec = BitVec::from_slice(slice); - unsafe { bitvec.set_len(bit_len) }; - Self::new(bitvec) + + let table_ptr = table.as_ptr(); + + Self { + table_ptr, + table, + len: bit_len, + } } /// Checks if `pc` is a valid jump destination. @@ -183,12 +209,60 @@ mod tests { assert!(!jump_table.is_valid(11)); assert!(!jump_table.is_valid(12)); } + + #[test] + #[cfg(feature = "serde")] + fn test_serde_legacy_format() { + let legacy_format = r#" + { + "order": "bitvec::order::Lsb0", + "head": { + "width": 8, + "index": 0 + }, + "bits": 4, + "data": [5] + }"#; + + 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)); + } + + #[test] + #[cfg(feature = "serde")] + fn test_serde_roundtrip() { + let original = JumpTable::from_slice(&[0x0D, 0x06], 13); + + // Serialize to JSON + let serialized = serde_json::to_string(&original).expect("Failed to serialize"); + + // Deserialize from JSON + let deserialized: JumpTable = + serde_json::from_str(&serialized).expect("Failed to deserialize"); + + // Check that the deserialized table matches the original + assert_eq!(original.len, deserialized.len); + assert_eq!(original.table, deserialized.table); + + // Verify functionality is preserved + for i in 0..13 { + assert_eq!( + original.is_valid(i), + deserialized.is_valid(i), + "Mismatch at index {i}" + ); + } + } } #[cfg(test)] mod bench_is_valid { use super::*; - use std::time::Instant; + use std::{sync::Arc, time::Instant}; const ITERATIONS: usize = 1_000_000; const TEST_SIZE: usize = 10_000; diff --git a/crates/bytecode/src/opcode.rs b/crates/bytecode/src/opcode.rs index 20a0aaad55..4f1d4247f7 100644 --- a/crates/bytecode/src/opcode.rs +++ b/crates/bytecode/src/opcode.rs @@ -182,7 +182,6 @@ impl OpCode { matches!( *self, OpCode::EXTCODECOPY - | OpCode::MLOAD | OpCode::MSTORE | OpCode::MSTORE8 | OpCode::MCOPY @@ -769,7 +768,7 @@ 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(ADD).unwrap().modifies_memory()); } diff --git a/crates/context/CHANGELOG.md b/crates/context/CHANGELOG.md index b278b54024..28a866505f 100644 --- a/crates/context/CHANGELOG.md +++ b/crates/context/CHANGELOG.md @@ -7,6 +7,80 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [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 + +- resize short addresses bitvec instead of reallocating ([#3083](https://github.com/bluealloy/revm/pull/3083)) + +## [10.1.0](https://github.com/bluealloy/revm/compare/revm-context-v10.0.0...revm-context-v10.1.0) - 2025-10-09 + +### Other + +- updated the following local packages: revm-database-interface, revm-database, revm-context-interface + +## [10.0.0](https://github.com/bluealloy/revm/compare/revm-context-v9.1.0...revm-context-v10.0.0) - 2025-10-07 + +### Added + +- Support bubbling up first precompile error messages ([#2905](https://github.com/bluealloy/revm/pull/2905)) +- add transaction index to batch execution error handling ([#3000](https://github.com/bluealloy/revm/pull/3000)) +- Add Str(Cow<'static, str>) to InvalidTransaction error enum ([#2998](https://github.com/bluealloy/revm/pull/2998)) +- allow EIP-7623 to be disabled ([#2985](https://github.com/bluealloy/revm/pull/2985)) +- Introduced `all_mut` and `all` functions to ContextTr ([#2992](https://github.com/bluealloy/revm/pull/2992)) +- send bytecode with call input ([#2963](https://github.com/bluealloy/revm/pull/2963)) +- *(op-revm)* Add an option to disable "fee-charge" on `op-revm` ([#2980](https://github.com/bluealloy/revm/pull/2980)) +- *(revme)* ef blockchain tests cli ([#2935](https://github.com/bluealloy/revm/pull/2935)) + +### Fixed + +- Apply spelling corrections from PRs #2926, #2915, #2908 ([#2978](https://github.com/bluealloy/revm/pull/2978)) +- FrameStack mark push/end_init as unsafe ([#2929](https://github.com/bluealloy/revm/pull/2929)) +- skip cold load on oog ([#2903](https://github.com/bluealloy/revm/pull/2903)) + +### Other + +- make precompile error pub ([#3057](https://github.com/bluealloy/revm/pull/3057)) +- changelog update for v87 ([#3056](https://github.com/bluealloy/revm/pull/3056)) +- add boundless ([#3043](https://github.com/bluealloy/revm/pull/3043)) +- helper function gas_balance_spending ([#3030](https://github.com/bluealloy/revm/pull/3030)) +- remove unreachable zero-denominator check in fake_exponential ([#3039](https://github.com/bluealloy/revm/pull/3039)) +- add ensure_enough_balance helper ([#3033](https://github.com/bluealloy/revm/pull/3033)) +- add default impl for tx_local_mut and tx_journal_mut ([#3029](https://github.com/bluealloy/revm/pull/3029)) +- *(op-revm)* propagate optional_fee_charge feature ([#3020](https://github.com/bluealloy/revm/pull/3020)) +- prealloc few frames ([#2965](https://github.com/bluealloy/revm/pull/2965)) +- add SECURITY.md ([#2956](https://github.com/bluealloy/revm/pull/2956)) +- *(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)) + ## [9.1.0](https://github.com/bluealloy/revm/compare/revm-context-v9.0.2...revm-context-v9.1.0) - 2025-09-23 ### Added diff --git a/crates/context/Cargo.toml b/crates/context/Cargo.toml index aec9972079..c6c54c98a9 100644 --- a/crates/context/Cargo.toml +++ b/crates/context/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "revm-context" description = "Revm context crates" -version = "9.1.0" +version = "11.0.1" authors.workspace = true edition.workspace = true keywords.workspace = true @@ -65,6 +65,7 @@ dev = [ "optional_block_gas_limit", "optional_eip3541", "optional_eip3607", + "optional_eip7623", "optional_no_base_fee", "optional_priority_fee_check", "optional_fee_charge", @@ -74,6 +75,7 @@ optional_balance_check = [] optional_block_gas_limit = [] optional_eip3541 = [] optional_eip3607 = [] +optional_eip7623 = [] optional_no_base_fee = [] optional_priority_fee_check = [] optional_fee_charge = [] diff --git a/crates/context/interface/CHANGELOG.md b/crates/context/interface/CHANGELOG.md index 190b2b7910..44f1b1932a 100644 --- a/crates/context/interface/CHANGELOG.md +++ b/crates/context/interface/CHANGELOG.md @@ -7,6 +7,73 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [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 + +- updated the following local packages: revm-primitives, revm-state, revm-database-interface + +## [11.1.0](https://github.com/bluealloy/revm/compare/revm-context-interface-v11.0.0...revm-context-interface-v11.1.0) - 2025-10-09 + +### Other + +- updated the following local packages: revm-database-interface + +## [11.0.0](https://github.com/bluealloy/revm/compare/revm-context-interface-v10.2.0...revm-context-interface-v11.0.0) - 2025-10-07 + +### Added + +- Support bubbling up first precompile error messages ([#2905](https://github.com/bluealloy/revm/pull/2905)) +- add transaction index to batch execution error handling ([#3000](https://github.com/bluealloy/revm/pull/3000)) +- Add Str(Cow<'static, str>) to InvalidTransaction error enum ([#2998](https://github.com/bluealloy/revm/pull/2998)) +- allow EIP-7623 to be disabled ([#2985](https://github.com/bluealloy/revm/pull/2985)) +- Introduced `all_mut` and `all` functions to ContextTr ([#2992](https://github.com/bluealloy/revm/pull/2992)) +- send bytecode with call input ([#2963](https://github.com/bluealloy/revm/pull/2963)) +- *(op-revm)* Add an option to disable "fee-charge" on `op-revm` ([#2980](https://github.com/bluealloy/revm/pull/2980)) +- *(revme)* ef blockchain tests cli ([#2935](https://github.com/bluealloy/revm/pull/2935)) + +### Fixed + +- Apply spelling corrections from PRs #2926, #2915, #2908 ([#2978](https://github.com/bluealloy/revm/pull/2978)) +- FrameStack mark push/end_init as unsafe ([#2929](https://github.com/bluealloy/revm/pull/2929)) +- skip cold load on oog ([#2903](https://github.com/bluealloy/revm/pull/2903)) + +### Other + +- changelog update for v87 ([#3056](https://github.com/bluealloy/revm/pull/3056)) +- add boundless ([#3043](https://github.com/bluealloy/revm/pull/3043)) +- helper function gas_balance_spending ([#3030](https://github.com/bluealloy/revm/pull/3030)) +- remove unreachable zero-denominator check in fake_exponential ([#3039](https://github.com/bluealloy/revm/pull/3039)) +- add ensure_enough_balance helper ([#3033](https://github.com/bluealloy/revm/pull/3033)) +- add default impl for tx_local_mut and tx_journal_mut ([#3029](https://github.com/bluealloy/revm/pull/3029)) +- prealloc few frames ([#2965](https://github.com/bluealloy/revm/pull/2965)) +- add SECURITY.md ([#2956](https://github.com/bluealloy/revm/pull/2956)) +- *(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)) + ## [10.2.0](https://github.com/bluealloy/revm/compare/revm-context-interface-v10.1.0...revm-context-interface-v10.2.0) - 2025-09-23 ### Added diff --git a/crates/context/interface/Cargo.toml b/crates/context/interface/Cargo.toml index d8f6e7f137..4d1d09162d 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 = "10.2.0" +version = "12.0.1" authors.workspace = true edition.workspace = true keywords.workspace = true @@ -30,10 +30,7 @@ auto_impl.workspace = true either.workspace = true # Optional -serde = { version = "1.0", default-features = false, features = [ - "derive", - "rc", -], optional = true } +serde = { workspace = true, optional = true } [features] default = ["std"] diff --git a/crates/context/interface/src/block.rs b/crates/context/interface/src/block.rs index 544c0994bc..b940e32069 100644 --- a/crates/context/interface/src/block.rs +++ b/crates/context/interface/src/block.rs @@ -3,9 +3,7 @@ //! [`Block`] trait is used to retrieve block information required for execution. pub mod blob; -pub use blob::{ - calc_blob_gasprice, calc_excess_blob_gas, calc_excess_blob_gas_osaka, BlobExcessGasAndPrice, -}; +pub use blob::{calc_blob_gasprice, BlobExcessGasAndPrice}; use auto_impl::auto_impl; use primitives::{Address, B256, U256}; @@ -47,8 +45,6 @@ pub trait Block { fn prevrandao(&self) -> Option; /// Excess blob gas and blob gasprice. - /// See also [`calc_excess_blob_gas`] - /// and [`calc_blob_gasprice`]. /// /// Incorporated as part of the Cancun upgrade via [EIP-4844]. /// diff --git a/crates/context/interface/src/block/blob.rs b/crates/context/interface/src/block/blob.rs index bb8e661ef3..b0fb7dfa50 100644 --- a/crates/context/interface/src/block/blob.rs +++ b/crates/context/interface/src/block/blob.rs @@ -3,13 +3,14 @@ //! //! See also [the EIP-4844 helpers](https://eips.ethereum.org/EIPS/eip-4844#helpers). //! -//! [`calc_blob_gasprice`] and [`calc_excess_blob_gas`] are used to calculate the blob gas price and -//! excess blob gas. //! //! [`BlobExcessGasAndPrice`] is used to store the blob gas price and excess blob gas.s use primitives::{ - eip4844::{GAS_PER_BLOB, MIN_BLOB_GASPRICE}, - eip7918, + eip4844::{ + BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN, BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE, + MIN_BLOB_GASPRICE, + }, + hardfork::SpecId, }; /// Structure holding block blob excess gas and it calculates blob fee @@ -31,7 +32,7 @@ pub struct BlobExcessGasAndPrice { impl BlobExcessGasAndPrice { /// Creates a new instance by calculating the blob gas price with [`calc_blob_gasprice`]. /// - /// `excess_blob_gas` is the excess blob gas of the block, it can be calculated with [`calc_excess_blob_gas`]. + /// `excess_blob_gas` is the excess blob gas of the block, it can be calculated with `calc_excess_blob_gas` function from alloy-eips. pub fn new(excess_blob_gas: u64, blob_base_fee_update_fraction: u64) -> Self { let blob_gasprice = calc_blob_gasprice(excess_blob_gas, blob_base_fee_update_fraction); Self { @@ -40,95 +41,19 @@ impl BlobExcessGasAndPrice { } } - /// Calculate this block excess gas and price from the parent excess gas and gas used - /// and the target blob gas per block. - /// - /// These fields will be used to calculate `excess_blob_gas` with [`calc_excess_blob_gas`] func. - #[deprecated( - note = "Use `calc_excess_blob_gas` and `BlobExcessGasAndPrice::new` instead. Only works for forks before Osaka." - )] - pub fn from_parent_and_target( - parent_excess_blob_gas: u64, - parent_blob_gas_used: u64, - parent_target_blob_gas_per_block: u64, - blob_base_fee_update_fraction: u64, - ) -> Self { + /// Creates a new instance by calculating the blob gas price based on the spec. + pub fn new_with_spec(excess_blob_gas: u64, spec: SpecId) -> Self { Self::new( - calc_excess_blob_gas( - parent_excess_blob_gas, - parent_blob_gas_used, - parent_target_blob_gas_per_block, - ), - blob_base_fee_update_fraction, + excess_blob_gas, + if spec.is_enabled_in(SpecId::PRAGUE) { + BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE + } else { + BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN + }, ) } } -/// Calculates the `excess_blob_gas` from the parent header's `blob_gas_used` and `excess_blob_gas`. -/// Uses [`calc_excess_blob_gas_osaka`] internally. -#[inline] -pub fn calc_excess_blob_gas( - parent_excess_blob_gas: u64, - parent_blob_gas_used: u64, - parent_target_blob_gas_per_block: u64, -) -> u64 { - calc_excess_blob_gas_osaka( - parent_excess_blob_gas, - parent_blob_gas_used, - parent_target_blob_gas_per_block, - false, - 0, - 0, - 0, - 0, - 0, - ) -} - -/// Calculates the `excess_blob_gas` from the parent header's `blob_gas_used` and `excess_blob_gas`. -/// -/// See also [the EIP-4844 helpers] -/// (`calc_excess_blob_gas`). -/// -/// [EIP-7918: Blob base fee bounded by execution cost](https://eips.ethereum.org/EIPS/eip-7918) -/// -/// `blob_base_cost` is introduced in EIP-7918 in Osaka fork. All fields after is_osaka input are not needed before Osaka. -#[allow(clippy::too_many_arguments)] -#[inline] -pub fn calc_excess_blob_gas_osaka( - parent_excess_blob_gas: u64, - parent_blob_gas_used: u64, - parent_target_blob_gas_per_block: u64, - is_osaka: bool, - parent_base_fee_per_gas: u64, - parent_blob_base_fee_per_gas: u64, - parent_blob_base_fee_update_fraction: u64, - max_blob_count: u64, - target_blob_count: u64, -) -> u64 { - let excess_and_used = parent_excess_blob_gas.saturating_add(parent_blob_gas_used); - - if is_osaka { - if excess_and_used < parent_target_blob_gas_per_block { - return 0; - } - - if (eip7918::BLOB_BASE_COST.saturating_mul(parent_base_fee_per_gas) as u128) - > (GAS_PER_BLOB as u128).saturating_mul(get_base_fee_per_blob_gas( - parent_blob_base_fee_per_gas, - parent_blob_base_fee_update_fraction, - )) - { - return excess_and_used.saturating_add( - parent_blob_gas_used.saturating_mul(max_blob_count - target_blob_count) - / max_blob_count, - ); - } - } - - excess_and_used.saturating_sub(parent_target_blob_gas_per_block) -} - /// Calculates the blob gas price from the header's excess blob gas field. /// /// See also [the EIP-4844 helpers](https://eips.ethereum.org/EIPS/eip-4844#helpers) @@ -154,10 +79,6 @@ pub fn get_base_fee_per_blob_gas(excess_blob_gas: u64, blob_base_fee_update_frac /// /// See also [the EIP-4844 helpers](https://eips.ethereum.org/EIPS/eip-4844#helpers) /// (`fake_exponential`). -/// -/// # Panics -/// -/// This function panics if `denominator` is zero. #[inline] pub fn fake_exponential(factor: u64, numerator: u64, denominator: u64) -> u128 { assert_ne!(denominator, 0, "attempt to divide by zero"); @@ -181,92 +102,7 @@ pub fn fake_exponential(factor: u64, numerator: u64, denominator: u64) -> u128 { #[cfg(test)] mod tests { use super::*; - use primitives::eip4844::{ - self, BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN, GAS_PER_BLOB, - TARGET_BLOB_GAS_PER_BLOCK_CANCUN as TARGET_BLOB_GAS_PER_BLOCK, - }; - - // https://github.com/ethereum/go-ethereum/blob/28857080d732857030eda80c69b9ba2c8926f221/consensus/misc/eip4844/eip4844_test.go#L27 - #[test] - fn test_calc_excess_blob_gas() { - for t @ &(excess, blobs, expected) in &[ - // The excess blob gas should not increase from zero if the used blob - // slots are below - or equal - to the target. - (0, 0, 0), - (0, 1, 0), - (0, TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB, 0), - // If the target blob gas is exceeded, the excessBlobGas should increase - // by however much it was overshot - ( - 0, - (TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) + 1, - GAS_PER_BLOB, - ), - ( - 1, - (TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) + 1, - GAS_PER_BLOB + 1, - ), - ( - 1, - (TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) + 2, - 2 * GAS_PER_BLOB + 1, - ), - // The excess blob gas should decrease by however much the target was - // under-shot, capped at zero. - ( - TARGET_BLOB_GAS_PER_BLOCK, - TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB, - TARGET_BLOB_GAS_PER_BLOCK, - ), - ( - TARGET_BLOB_GAS_PER_BLOCK, - (TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) - 1, - TARGET_BLOB_GAS_PER_BLOCK - GAS_PER_BLOB, - ), - ( - TARGET_BLOB_GAS_PER_BLOCK, - (TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) - 2, - TARGET_BLOB_GAS_PER_BLOCK - (2 * GAS_PER_BLOB), - ), - ( - GAS_PER_BLOB - 1, - (TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) - 1, - 0, - ), - ] { - let actual = calc_excess_blob_gas( - excess, - blobs * GAS_PER_BLOB, - eip4844::TARGET_BLOB_GAS_PER_BLOCK_CANCUN, - ); - assert_eq!(actual, expected, "test: {t:?}"); - } - } - - // https://github.com/ethereum/go-ethereum/blob/28857080d732857030eda80c69b9ba2c8926f221/consensus/misc/eip4844/eip4844_test.go#L60 - #[test] - fn test_calc_blob_fee_cancun() { - let blob_fee_vectors = &[ - (0, 1), - (2314057, 1), - (2314058, 2), - (10 * 1024 * 1024, 23), - // `calc_blob_gasprice` approximates `e ** (excess_blob_gas / BLOB_BASE_FEE_UPDATE_FRACTION)` using Taylor expansion - // - // to roughly find where boundaries will be hit: - // 2 ** bits = e ** (excess_blob_gas / BLOB_BASE_FEE_UPDATE_FRACTION) - // excess_blob_gas = ln(2 ** bits) * BLOB_BASE_FEE_UPDATE_FRACTION - (148099578, 18446739238971471609), // output is just below the overflow - (148099579, 18446744762204311910), // output is just after the overflow - (161087488, 902580055246494526580), - ]; - - for &(excess, expected) in blob_fee_vectors { - let actual = calc_blob_gasprice(excess, BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN); - assert_eq!(actual, expected, "test: {excess}"); - } - } + use primitives::eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN; // https://github.com/ethereum/go-ethereum/blob/28857080d732857030eda80c69b9ba2c8926f221/consensus/misc/eip4844/eip4844_test.go#L78 #[test] diff --git a/crates/context/interface/src/cfg.rs b/crates/context/interface/src/cfg.rs index 2466e4e5d3..1571a93c47 100644 --- a/crates/context/interface/src/cfg.rs +++ b/crates/context/interface/src/cfg.rs @@ -44,6 +44,9 @@ pub trait Cfg { /// Returns whether the EIP-3541 (disallowing new contracts with 0xEF prefix) is disabled. fn is_eip3541_disabled(&self) -> bool; + /// Returns whether the EIP-7623 (increased calldata cost) is disabled. + fn is_eip7623_disabled(&self) -> bool; + /// Returns whether the balance check is disabled. fn is_balance_check_disabled(&self) -> bool; @@ -62,6 +65,9 @@ pub trait Cfg { /// Returns whether the fee charge is disabled. fn is_fee_charge_disabled(&self) -> bool; + /// Returns the limit in bytes for the memory buffer. + fn memory_limit(&self) -> u64; + /// Returns whether the EIP-7702 is enabled. fn is_eip7702_enabled(&self) -> bool; diff --git a/crates/context/interface/src/context.rs b/crates/context/interface/src/context.rs index 0be6f53439..114235cb6e 100644 --- a/crates/context/interface/src/context.rs +++ b/crates/context/interface/src/context.rs @@ -1,3 +1,4 @@ +#![allow(clippy::type_complexity)] //! Context trait and related types. pub use crate::journaled_state::StateLoad; use crate::{ @@ -31,52 +32,126 @@ pub trait ContextTr: Host { /// Local context type type Local: LocalContextTr; + /// Get all contexts + fn all( + &self, + ) -> ( + &Self::Block, + &Self::Tx, + &Self::Cfg, + &Self::Db, + &Self::Journal, + &Self::Chain, + &Self::Local, + ); + + /// Get all contexts mutably + fn all_mut( + &mut self, + ) -> ( + &Self::Block, + &Self::Tx, + &Self::Cfg, + &mut Self::Journal, + &mut Self::Chain, + &mut Self::Local, + ); + /// Get the transaction - fn tx(&self) -> &Self::Tx; + fn tx(&self) -> &Self::Tx { + let (_, tx, _, _, _, _, _) = self.all(); + tx + } /// Get the block - fn block(&self) -> &Self::Block; + fn block(&self) -> &Self::Block { + let (block, _, _, _, _, _, _) = self.all(); + block + } /// Get the configuration - fn cfg(&self) -> &Self::Cfg; + fn cfg(&self) -> &Self::Cfg { + let (_, _, cfg, _, _, _, _) = self.all(); + cfg + } /// Get the journal - fn journal(&self) -> &Self::Journal; + fn journal(&self) -> &Self::Journal { + let (_, _, _, _, journal, _, _) = self.all(); + journal + } /// Get the journal mutably - fn journal_mut(&mut self) -> &mut Self::Journal; + fn journal_mut(&mut self) -> &mut Self::Journal { + let (_, _, _, journal, _, _) = self.all_mut(); + journal + } /// Get the journal reference fn journal_ref(&self) -> &Self::Journal { self.journal() } /// Get the database - fn db(&self) -> &Self::Db; + fn db(&self) -> &Self::Db { + let (_, _, _, db, _, _, _) = self.all(); + db + } /// Get the database mutably - fn db_mut(&mut self) -> &mut Self::Db; + fn db_mut(&mut self) -> &mut Self::Db { + let db = self.journal_mut().db_mut(); + db + } /// Get the database reference fn db_ref(&self) -> &Self::Db { self.db() } /// Get the chain - fn chain(&self) -> &Self::Chain; + fn chain(&self) -> &Self::Chain { + let (_, _, _, _, _, chain, _) = self.all(); + chain + } /// Get the chain mutably - fn chain_mut(&mut self) -> &mut Self::Chain; + fn chain_mut(&mut self) -> &mut Self::Chain { + let (_, _, _, _, chain, _) = self.all_mut(); + chain + } /// Get the chain reference fn chain_ref(&self) -> &Self::Chain { self.chain() } /// Get the local context - fn local(&self) -> &Self::Local; + fn local(&self) -> &Self::Local { + let (_, _, _, _, _, _, local) = self.all(); + local + } /// Get the local context mutably - fn local_mut(&mut self) -> &mut Self::Local; + fn local_mut(&mut self) -> &mut Self::Local { + let (_, _, _, _, _, local) = self.all_mut(); + local + } /// Get the local context reference fn local_ref(&self) -> &Self::Local { self.local() } /// Get the error fn error(&mut self) -> &mut Result<(), ContextError<::Error>>; + /// Get the transaction and journal. It is used to efficiently load access list /// into journal without copying them from transaction. - fn tx_journal_mut(&mut self) -> (&Self::Tx, &mut Self::Journal); + fn tx_journal_mut(&mut self) -> (&Self::Tx, &mut Self::Journal) { + let (_, tx, _, journal, _, _) = self.all_mut(); + (tx, journal) + } + + /// Get the transaction, configuration and mutable journal. + fn tx_block_cfg_journal_mut( + &mut self, + ) -> (&Self::Tx, &Self::Block, &Self::Cfg, &mut Self::Journal) { + let (block, tx, cfg, journal, _, _) = self.all_mut(); + (tx, block, cfg, journal) + } + /// Get the transaction and local context. It is used to efficiently load initcode /// into local context without copying them from transaction. - fn tx_local_mut(&mut self) -> (&Self::Tx, &mut Self::Local); + fn tx_local_mut(&mut self) -> (&Self::Tx, &mut Self::Local) { + let (_, tx, _, _, _, local) = self.all_mut(); + (tx, local) + } } /// Inner Context error used for Interpreter to set error without returning it from instruction @@ -116,38 +191,38 @@ pub struct SStoreResult { impl SStoreResult { /// Returns `true` if the new value is equal to the present value. #[inline] - pub fn is_new_eq_present(&self) -> bool { - self.new_value == self.present_value + pub const fn is_new_eq_present(&self) -> bool { + self.new_value.const_eq(&self.present_value) } /// Returns `true` if the original value is equal to the present value. #[inline] - pub fn is_original_eq_present(&self) -> bool { - self.original_value == self.present_value + pub const fn is_original_eq_present(&self) -> bool { + self.original_value.const_eq(&self.present_value) } /// Returns `true` if the original value is equal to the new value. #[inline] - pub fn is_original_eq_new(&self) -> bool { - self.original_value == self.new_value + pub const fn is_original_eq_new(&self) -> bool { + self.original_value.const_eq(&self.new_value) } /// Returns `true` if the original value is zero. #[inline] - pub fn is_original_zero(&self) -> bool { - self.original_value.is_zero() + pub const fn is_original_zero(&self) -> bool { + self.original_value.const_is_zero() } /// Returns `true` if the present value is zero. #[inline] - pub fn is_present_zero(&self) -> bool { - self.present_value.is_zero() + pub const fn is_present_zero(&self) -> bool { + self.present_value.const_is_zero() } /// Returns `true` if the new value is zero. #[inline] - pub fn is_new_zero(&self) -> bool { - self.new_value.is_zero() + pub const fn is_new_zero(&self) -> bool { + self.new_value.const_is_zero() } } diff --git a/crates/context/interface/src/host.rs b/crates/context/interface/src/host.rs index 636a5eb4dc..608edb53da 100644 --- a/crates/context/interface/src/host.rs +++ b/crates/context/interface/src/host.rs @@ -2,10 +2,21 @@ use crate::{ context::{SStoreResult, SelfDestructResult, StateLoad}, - journaled_state::AccountLoad, + journaled_state::{AccountInfoLoad, AccountLoad}, }; use auto_impl::auto_impl; use primitives::{Address, Bytes, Log, StorageKey, StorageValue, B256, U256}; +use state::Bytecode; + +/// Error that can happen when loading account info. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum LoadError { + /// Database error. + DBError, + /// Cold load skipped. + ColdLoadSkipped, +} /// Host trait with all methods that are needed by the Interpreter. /// @@ -65,28 +76,126 @@ pub trait Host { /// Log, calls `ContextTr::journal_mut().log(log)` fn log(&mut self, log: Log); + + /// Sstore with optional fetch from database. Return none if the value is cold or if there is db error. + fn sstore_skip_cold_load( + &mut self, + address: Address, + key: StorageKey, + value: StorageValue, + skip_cold_load: bool, + ) -> Result, LoadError>; + /// Sstore, calls `ContextTr::journal_mut().sstore(address, key, value)` fn sstore( &mut self, address: Address, key: StorageKey, value: StorageValue, - ) -> Option>; + ) -> Option> { + self.sstore_skip_cold_load(address, key, value, false).ok() + } + + /// Sload with optional fetch from database. Return none if the value is cold or if there is db error. + fn sload_skip_cold_load( + &mut self, + address: Address, + key: StorageKey, + skip_cold_load: bool, + ) -> Result, LoadError>; /// Sload, calls `ContextTr::journal_mut().sload(address, key)` - fn sload(&mut self, address: Address, key: StorageKey) -> Option>; + fn sload(&mut self, address: Address, key: StorageKey) -> Option> { + self.sload_skip_cold_load(address, key, false).ok() + } + /// Tstore, calls `ContextTr::journal_mut().tstore(address, key, value)` fn tstore(&mut self, address: Address, key: StorageKey, value: StorageValue); + /// Tload, calls `ContextTr::journal_mut().tload(address, key)` fn tload(&mut self, address: Address, key: StorageKey) -> StorageValue; + + /// Main function to load account info. + /// + /// If load_code is true, it will load the code fetching it from the database if not done before. + /// + /// If skip_cold_load is true, it will not load the account if it is cold. This is needed to short circuit + /// the load if there is not enough gas. + /// + /// Returns AccountInfo, is_cold and is_empty. + fn load_account_info_skip_cold_load( + &mut self, + address: Address, + load_code: bool, + skip_cold_load: bool, + ) -> Result, LoadError>; + /// Balance, calls `ContextTr::journal_mut().load_account(address)` - fn balance(&mut self, address: Address) -> Option>; + #[inline] + fn balance(&mut self, address: Address) -> Option> { + self.load_account_info_skip_cold_load(address, false, false) + .ok() + .map(|load| load.into_state_load(|i| i.balance)) + } + /// Load account delegated, calls `ContextTr::journal_mut().load_account_delegated(address)` - fn load_account_delegated(&mut self, address: Address) -> Option>; - /// Load account code, calls `ContextTr::journal_mut().load_account_code(address)` - fn load_account_code(&mut self, address: Address) -> Option>; - /// Load account code hash, calls `ContextTr::journal_mut().code_hash(address)` - fn load_account_code_hash(&mut self, address: Address) -> Option>; + #[inline] + fn load_account_delegated(&mut self, address: Address) -> Option> { + let account = self + .load_account_info_skip_cold_load(address, true, false) + .ok()?; + + let mut account_load = StateLoad::new( + AccountLoad { + is_delegate_account_cold: None, + is_empty: account.is_empty, + }, + account.is_cold, + ); + + // load delegate code if account is EIP-7702 + if let Some(Bytecode::Eip7702(code)) = &account.code { + let address = code.address(); + let delegate_account = self + .load_account_info_skip_cold_load(address, true, false) + .ok()?; + account_load.data.is_delegate_account_cold = Some(delegate_account.is_cold); + account_load.data.is_empty = delegate_account.is_empty; + } + + Some(account_load) + } + + /// Load account code, calls [`Host::load_account_info_skip_cold_load`] with `load_code` set to false. + #[inline] + fn load_account_code(&mut self, address: Address) -> Option> { + self.load_account_info_skip_cold_load(address, true, false) + .ok() + .map(|load| { + load.into_state_load(|i| { + i.code + .as_ref() + .map(|b| b.original_bytes()) + .unwrap_or_default() + }) + }) + } + + /// Load account code hash, calls [`Host::load_account_info_skip_cold_load`] with `load_code` set to false. + #[inline] + fn load_account_code_hash(&mut self, address: Address) -> Option> { + self.load_account_info_skip_cold_load(address, false, false) + .ok() + .map(|load| { + load.into_state_load(|i| { + if i.is_empty() { + B256::ZERO + } else { + i.code_hash + } + }) + }) + } } /// Dummy host that implements [`Host`] trait and returns all default values. @@ -160,38 +269,37 @@ impl Host for DummyHost { fn log(&mut self, _log: Log) {} - fn sstore( - &mut self, - _address: Address, - _key: StorageKey, - _value: StorageValue, - ) -> Option> { - None - } - - fn sload(&mut self, _address: Address, _key: StorageKey) -> Option> { - None - } - fn tstore(&mut self, _address: Address, _key: StorageKey, _value: StorageValue) {} fn tload(&mut self, _address: Address, _key: StorageKey) -> StorageValue { StorageValue::ZERO } - fn balance(&mut self, _address: Address) -> Option> { - None - } - - fn load_account_delegated(&mut self, _address: Address) -> Option> { - None + fn load_account_info_skip_cold_load( + &mut self, + _address: Address, + _load_code: bool, + _skip_cold_load: bool, + ) -> Result, LoadError> { + Err(LoadError::DBError) } - fn load_account_code(&mut self, _address: Address) -> Option> { - None + fn sstore_skip_cold_load( + &mut self, + _address: Address, + _key: StorageKey, + _value: StorageValue, + _skip_cold_load: bool, + ) -> Result, LoadError> { + Err(LoadError::DBError) } - fn load_account_code_hash(&mut self, _address: Address) -> Option> { - None + fn sload_skip_cold_load( + &mut self, + _address: Address, + _key: StorageKey, + _skip_cold_load: bool, + ) -> Result, LoadError> { + Err(LoadError::DBError) } } diff --git a/crates/context/interface/src/journaled_state.rs b/crates/context/interface/src/journaled_state.rs index 7c981a6605..80a252256e 100644 --- a/crates/context/interface/src/journaled_state.rs +++ b/crates/context/interface/src/journaled_state.rs @@ -1,12 +1,20 @@ //! Journaled state trait [`JournalTr`] and related types. -use crate::context::{SStoreResult, SelfDestructResult}; + +pub mod account; +pub mod entry; + +use crate::{ + context::{SStoreResult, SelfDestructResult}, + host::LoadError, + journaled_state::{account::JournaledAccount, entry::JournalEntryTr}, +}; 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, Bytecode}; -use std::vec::Vec; +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 { @@ -14,6 +22,8 @@ pub trait JournalTr { type Database: Database; /// State type that is returned by the journal after finalization. type State; + /// Journal Entry type that is used in the journal. + type JournalEntry: JournalEntryTr; /// Creates new Journaled state. /// @@ -33,7 +43,19 @@ pub trait JournalTr { &mut self, address: Address, key: StorageKey, - ) -> Result, ::Error>; + ) -> Result, ::Error> { + // unwrapping is safe as we only can get DBError + self.sload_skip_cold_load(address, key, false) + .map_err(JournalLoadError::unwrap_db_error) + } + + /// Loads the storage value from Journal state. + fn sload_skip_cold_load( + &mut self, + _address: Address, + _key: StorageKey, + _skip_cold_load: bool, + ) -> Result, JournalLoadError<::Error>>; /// Stores the storage value in Journal state. fn sstore( @@ -41,7 +63,20 @@ pub trait JournalTr { address: Address, key: StorageKey, value: StorageValue, - ) -> Result, ::Error>; + ) -> Result, ::Error> { + // unwrapping is safe as we only can get DBError + self.sstore_skip_cold_load(address, key, value, false) + .map_err(JournalLoadError::unwrap_db_error) + } + + /// Stores the storage value in Journal state. + fn sstore_skip_cold_load( + &mut self, + _address: Address, + _key: StorageKey, + _value: StorageValue, + _skip_cold_load: bool, + ) -> Result, JournalLoadError<::Error>>; /// Loads transient storage value. fn tload(&mut self, address: Address, key: StorageKey) -> StorageValue; @@ -59,20 +94,8 @@ pub trait JournalTr { target: Address, ) -> Result, ::Error>; - /// Warms the account and storage. - fn warm_account_and_storage( - &mut self, - address: Address, - storage_keys: impl IntoIterator, - ) -> Result<(), ::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); @@ -97,6 +120,14 @@ pub trait JournalTr { balance: U256, ) -> Result, ::Error>; + /// Transfers the balance from one account to another. Assume form and to are loaded. + fn transfer_loaded( + &mut self, + from: Address, + to: Address, + balance: U256, + ) -> Option; + /// Increments the balance of the account. fn caller_accounting_journal_entry( &mut self, @@ -119,13 +150,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( @@ -134,6 +175,40 @@ pub trait JournalTr { address: Address, ) -> Result, ::Error>; + /// Loads the journaled account. + #[inline] + fn load_account_mut( + &mut self, + address: Address, + ) -> Result< + StateLoad>, + ::Error, + > { + self.load_account_mut_optional_code(address, false) + } + + /// Loads the journaled account. + #[inline] + fn load_account_with_code_mut( + &mut self, + address: Address, + ) -> Result< + StateLoad>, + ::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< + StateLoad>, + ::Error, + >; + /// Sets bytecode with hash. Assume that account is warm. fn set_code_with_hash(&mut self, address: Address, code: Bytecode, hash: B256); @@ -152,10 +227,9 @@ 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(); - let code = code.original_bytes(); + let code = a.info.code.as_ref().unwrap().original_bytes(); Ok(StateLoad::new(code, a.is_cold)) } @@ -165,15 +239,11 @@ 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)); } - // SAFETY: Safe to unwrap as load_code will insert code if it is empty. - let _code = acc.info.code.as_ref().unwrap(); - let hash = acc.info.code_hash; - Ok(StateLoad::new(hash, acc.is_cold)) } @@ -218,6 +288,82 @@ pub trait JournalTr { /// Clear current journal resetting it to initial state and return changes state. fn finalize(&mut self) -> Self::State; + + /// Loads the account info from Journal state. + fn load_account_info_skip_cold_load( + &mut self, + _address: Address, + _load_code: bool, + _skip_cold_load: bool, + ) -> Result, JournalLoadError<::Error>>; +} + +/// Error that can happen when loading account info. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum JournalLoadError { + /// Database error. + DBError(E), + /// Cold load skipped. + ColdLoadSkipped, +} + +impl JournalLoadError { + /// Returns true if the error is a database error. + #[inline] + pub fn is_db_error(&self) -> bool { + matches!(self, JournalLoadError::DBError(_)) + } + + /// Returns true if the error is a cold load skipped. + #[inline] + pub fn is_cold_load_skipped(&self) -> bool { + matches!(self, JournalLoadError::ColdLoadSkipped) + } + + /// Takes the error if it is a database error. + #[inline] + pub fn take_db_error(self) -> Option { + if let JournalLoadError::DBError(e) = self { + Some(e) + } else { + None + } + } + + /// Unwraps the error if it is a database error. + #[inline] + pub fn unwrap_db_error(self) -> E { + if let JournalLoadError::DBError(e) = self { + e + } else { + panic!("Expected DBError"); + } + } + + /// Converts the error to a load error. + #[inline] + pub fn into_parts(self) -> (LoadError, Option) { + match self { + JournalLoadError::DBError(e) => (LoadError::DBError, Some(e)), + JournalLoadError::ColdLoadSkipped => (LoadError::ColdLoadSkipped, None), + } + } +} + +impl From for JournalLoadError { + fn from(e: E) -> Self { + JournalLoadError::DBError(e) + } +} + +impl From> for LoadError { + fn from(e: JournalLoadError) -> Self { + match e { + JournalLoadError::DBError(_) => LoadError::DBError, + JournalLoadError::ColdLoadSkipped => LoadError::ColdLoadSkipped, + } + } } /// Transfer and creation result @@ -267,6 +413,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 } } @@ -274,6 +421,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, @@ -291,3 +439,44 @@ pub struct AccountLoad { /// Is account empty, if `true` account is not created pub is_empty: bool, } + +/// Result of the account load from Journal state +#[derive(Clone, Debug, Default, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct AccountInfoLoad<'a> { + /// Account info + pub account: Cow<'a, AccountInfo>, + /// Is account cold loaded + pub is_cold: bool, + /// Is account empty, if `true` account is not created + pub is_empty: bool, +} + +impl<'a> AccountInfoLoad<'a> { + /// Creates new [`AccountInfoLoad`] with the given account info, cold load status and empty status. + pub fn new(account: &'a AccountInfo, is_cold: bool, is_empty: bool) -> Self { + Self { + account: Cow::Borrowed(account), + is_cold, + is_empty, + } + } + + /// 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. + pub fn into_state_load(self, f: F) -> StateLoad + where + F: FnOnce(Cow<'a, AccountInfo>) -> O, + { + StateLoad::new(f(self.account), self.is_cold) + } +} + +impl<'a> Deref for AccountInfoLoad<'a> { + type Target = AccountInfo; + + fn deref(&self) -> &Self::Target { + &self.account + } +} 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..4d42dd307e --- /dev/null +++ b/crates/context/interface/src/journaled_state/account.rs @@ -0,0 +1,185 @@ +//! 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 super::entry::JournalEntryTr; +use core::ops::Deref; +use primitives::{Address, B256, KECCAK_EMPTY, U256}; +use state::{Account, Bytecode}; +use std::vec::Vec; + +/// 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, ENTRY: JournalEntryTr> { + /// Address of the account. + address: Address, + /// Mutable account. + account: &'a mut Account, + /// Journal entries. + journal_entries: &'a mut Vec, +} + +impl<'a, ENTRY: JournalEntryTr> JournaledAccount<'a, ENTRY> { + /// Consumes the journaled account and returns the mutable account. + #[inline] + pub fn into_account_ref(self) -> &'a Account { + self.account + } + + /// Creates a new journaled account. + #[inline] + pub fn new( + address: Address, + account: &'a mut Account, + journal_entries: &'a mut Vec, + ) -> Self { + Self { + address, + account, + journal_entries, + } + } + + /// Returns the balance of the account. + #[inline] + pub fn balance(&self) -> &U256 { + &self.account.info.balance + } + + /// Returns the nonce of the account. + #[inline] + pub fn nonce(&self) -> u64 { + self.account.info.nonce + } + + /// Returns the code hash of the account. + #[inline] + pub fn code_hash(&self) -> &B256 { + &self.account.info.code_hash + } + + /// Returns the code of the account. + #[inline] + pub fn code(&self) -> Option<&Bytecode> { + self.account.info.code.as_ref() + } + + /// Touches the account. + #[inline] + pub fn touch(&mut self) { + if !self.account.status.is_touched() { + self.account.mark_touch(); + self.journal_entries + .push(ENTRY::account_touched(self.address)); + } + } + + /// 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] + pub 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] + pub 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] + pub 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] + pub 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_changed(self.address)); + true + } + + /// Sets the code of the account. + /// + /// Touches the account in all cases. + #[inline] + pub 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] + pub 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] + pub 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(); + } +} + +impl<'a, ENTRY: JournalEntryTr> Deref for JournaledAccount<'a, ENTRY> { + type Target = Account; + + fn deref(&self) -> &Self::Target { + self.account + } +} diff --git a/crates/context/src/journal/entry.rs b/crates/context/interface/src/journaled_state/entry.rs similarity index 100% rename from crates/context/src/journal/entry.rs rename to crates/context/interface/src/journaled_state/entry.rs diff --git a/crates/context/interface/src/local.rs b/crates/context/interface/src/local.rs index 56c4832842..a38da903b9 100644 --- a/crates/context/interface/src/local.rs +++ b/crates/context/interface/src/local.rs @@ -3,7 +3,7 @@ use core::{ cell::{Ref, RefCell}, ops::Range, }; -use std::{rc::Rc, vec::Vec}; +use std::{rc::Rc, string::String, vec::Vec}; /// Non-empty, item-pooling Vec. #[derive(Debug, Clone)] @@ -18,11 +18,24 @@ impl Default for FrameStack { } } +impl FrameStack { + /// Creates a new stack with preallocated items by calling `T::default()` `len` times. + /// Index will still be `None` until `end_init` is called. + pub fn new_prealloc(len: usize) -> Self { + let mut stack = Vec::with_capacity(len); + for _ in 0..len { + stack.push(T::default()); + } + Self { stack, index: None } + } +} + impl FrameStack { /// Creates a new, empty stack. It must be initialized with init before use. pub fn new() -> Self { + // Init N amount of frames to allocate the stack. Self { - stack: Vec::with_capacity(4), + stack: Vec::with_capacity(8), index: None, } } @@ -32,14 +45,18 @@ impl FrameStack { pub fn start_init(&mut self) -> OutFrame<'_, T> { self.index = None; if self.stack.is_empty() { - self.stack.reserve(1); + self.stack.reserve(8); } self.out_frame_at(0) } /// Finishes initialization. + /// + /// # Safety + /// + /// This method is unsafe because it assumes that the `token` is initialized from this FrameStack object. #[inline] - pub fn end_init(&mut self, token: FrameToken) { + pub unsafe fn end_init(&mut self, token: FrameToken) { token.assert(); if self.stack.is_empty() { unsafe { self.stack.set_len(1) }; @@ -54,15 +71,25 @@ impl FrameStack { } /// Increments the index. + /// + /// # Safety + /// + /// This method is unsafe because it assumes that the `token` is obtained from `get_next` and + /// that `end_init` is called to initialize the FrameStack. #[inline] - pub fn push(&mut self, token: FrameToken) { + pub unsafe fn push(&mut self, token: FrameToken) { token.assert(); let index = self.index.as_mut().unwrap(); - if *index + 1 == self.stack.len() { + *index += 1; + // capacity of stack is incremented in `get_next` + debug_assert!( + *index < self.stack.capacity(), + "Stack capacity is not enough for index" + ); + // If the index is the last one, we need to increase the length. + if *index == self.stack.len() { unsafe { self.stack.set_len(self.stack.len() + 1) }; - self.stack.reserve(1); } - *index += 1; } /// Clears the stack by setting the index to 0. @@ -81,13 +108,20 @@ impl FrameStack { /// Returns the current item. #[inline] pub fn get(&mut self) -> &mut T { - debug_assert!(self.stack.capacity() > self.index.unwrap() + 1); + debug_assert!( + self.stack.capacity() > self.index.unwrap(), + "Stack capacity is not enough for index" + ); unsafe { &mut *self.stack.as_mut_ptr().add(self.index.unwrap()) } } /// Get next uninitialized item. #[inline] pub fn get_next(&mut self) -> OutFrame<'_, T> { + if self.index.unwrap() + 1 == self.stack.capacity() { + // allocate 8 more items + self.stack.reserve(8); + } self.out_frame_at(self.index.unwrap() + 1) } @@ -193,6 +227,17 @@ pub trait LocalContextTr { /// Clear the local context. fn clear(&mut self); + + /// Set the error message for a precompile error, if any. + /// + /// This is used to bubble up precompile error messages when the + /// transaction directly targets a precompile (depth == 1). + fn set_precompile_error_context(&mut self, _output: String); + + /// Take and clear the precompile error context, if present. + /// + /// Returns `Some(String)` if a precompile error message was recorded. + fn take_precompile_error_context(&mut self) -> Option; } #[cfg(test)] @@ -201,11 +246,13 @@ mod tests { #[test] fn frame_stack() { - let mut stack = FrameStack::new(); + let mut stack = FrameStack::new_prealloc(1); let mut frame = stack.start_init(); - frame.get(|| 1); + // it is already initialized to zero. + *frame.get(|| 2) += 1; + let token = frame.consume(); - stack.end_init(token); + unsafe { stack.end_init(token) }; assert_eq!(stack.index(), Some(0)); assert_eq!(stack.stack.len(), 1); @@ -216,7 +263,7 @@ mod tests { assert!(!b.init); assert_eq!(b.get(|| 2), &mut 2); let token = b.consume(); // TODO: remove - stack.push(token); + unsafe { stack.push(token) }; assert_eq!(stack.index(), Some(1)); assert_eq!(stack.stack.len(), 2); diff --git a/crates/context/interface/src/result.rs b/crates/context/interface/src/result.rs index 340da3a72b..2bbb99583c 100644 --- a/crates/context/interface/src/result.rs +++ b/crates/context/interface/src/result.rs @@ -12,7 +12,7 @@ use core::fmt::{self, Debug}; use database_interface::DBErrorMarker; use primitives::{Address, Bytes, Log, U256}; use state::EvmState; -use std::{boxed::Box, string::String, vec::Vec}; +use std::{borrow::Cow, boxed::Box, string::String, vec::Vec}; /// Trait for the halt reason. pub trait HaltReasonTr: Clone + Debug + PartialEq + Eq + From {} @@ -400,7 +400,12 @@ pub enum InvalidTransaction { /// `blob_hashes`/`blob_versioned_hashes` is not supported for blocks before the Cancun hardfork. BlobVersionedHashesNotSupported, /// Block `blob_gas_price` is greater than tx-specified `max_fee_per_blob_gas` after Cancun. - BlobGasPriceGreaterThanMax, + BlobGasPriceGreaterThanMax { + /// Block `blob_gas_price`. + block_blob_gas_price: u128, + /// Tx-specified `max_fee_per_blob_gas`. + tx_max_fee_per_blob_gas: u128, + }, /// There should be at least one blob in Blob transaction. EmptyBlobs, /// Blob transaction can't be a create transaction. @@ -434,6 +439,8 @@ pub enum InvalidTransaction { Eip7873NotSupported, /// EIP-7873 initcode transaction should have `to` address. Eip7873MissingTarget, + /// Custom string error for flexible error handling. + Str(Cow<'static, str>), } impl TransactionError for InvalidTransaction {} @@ -506,8 +513,14 @@ impl fmt::Display for InvalidTransaction { Self::BlobVersionedHashesNotSupported => { write!(f, "blob versioned hashes not supported") } - Self::BlobGasPriceGreaterThanMax => { - write!(f, "blob gas price is greater than max fee per blob gas") + Self::BlobGasPriceGreaterThanMax { + block_blob_gas_price, + tx_max_fee_per_blob_gas, + } => { + write!( + f, + "blob gas price ({block_blob_gas_price}) is greater than max fee per blob gas ({tx_max_fee_per_blob_gas})" + ) } Self::EmptyBlobs => write!(f, "empty blobs"), Self::BlobCreateTransaction => write!(f, "blob create transaction"), @@ -528,6 +541,7 @@ impl fmt::Display for InvalidTransaction { Self::Eip7873MissingTarget => { write!(f, "Eip7873 initcode transaction should have `to` address") } + Self::Str(msg) => f.write_str(msg), } } } @@ -568,7 +582,7 @@ pub enum SuccessReason { /// Indicates that the EVM has experienced an exceptional halt. /// /// This causes execution to immediately end with all gas being consumed. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum HaltReason { /// Out of gas error. @@ -591,6 +605,8 @@ pub enum HaltReason { CreateCollision, /// Precompile error. PrecompileError, + /// Precompile error with message from context. + PrecompileErrorWithContext(String), /// Nonce overflow. NonceOverflow, /// Create init code size exceeds limit (runtime). @@ -631,3 +647,63 @@ pub enum OutOfGasError { /// When performing SSTORE the gasleft is less than or equal to 2300 ReentrancySentry, } + +/// Error that includes transaction index for batch transaction processing. +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct TransactionIndexedError { + /// The original error that occurred. + pub error: Error, + /// The index of the transaction that failed. + pub transaction_index: usize, +} + +impl TransactionIndexedError { + /// Create a new `TransactionIndexedError` with the given error and transaction index. + #[must_use] + pub fn new(error: Error, transaction_index: usize) -> Self { + Self { + error, + transaction_index, + } + } + + /// Get a reference to the underlying error. + pub fn error(&self) -> &Error { + &self.error + } + + /// Convert into the underlying error. + #[must_use] + pub fn into_error(self) -> Error { + self.error + } +} + +impl fmt::Display for TransactionIndexedError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "transaction {} failed: {}", + self.transaction_index, self.error + ) + } +} + +impl core::error::Error for TransactionIndexedError { + fn source(&self) -> Option<&(dyn core::error::Error + 'static)> { + Some(&self.error) + } +} + +impl From<&'static str> for InvalidTransaction { + fn from(s: &'static str) -> Self { + Self::Str(Cow::Borrowed(s)) + } +} + +impl From for InvalidTransaction { + fn from(s: String) -> Self { + Self::Str(Cow::Owned(s)) + } +} diff --git a/crates/context/interface/src/transaction.rs b/crates/context/interface/src/transaction.rs index 474d3e6ff7..a1cd0a3097 100644 --- a/crates/context/interface/src/transaction.rs +++ b/crates/context/interface/src/transaction.rs @@ -18,6 +18,7 @@ use auto_impl::auto_impl; use core::cmp::min; use core::fmt::Debug; use primitives::{eip4844::GAS_PER_BLOB, Address, Bytes, TxKind, B256, U256}; +use std::boxed::Box; /// Transaction validity error types. pub trait TransactionError: Debug + core::error::Error {} @@ -179,9 +180,27 @@ pub trait Transaction { Ok(max_balance_spending) } + /// Checks if the caller has enough balance to cover the maximum balance spending of this transaction. + /// + /// Internally calls [`Self::max_balance_spending`] and checks if the balance is enough. + #[inline] + fn ensure_enough_balance(&self, balance: U256) -> Result<(), InvalidTransaction> { + let max_balance_spending = self.max_balance_spending()?; + if max_balance_spending > balance { + return Err(InvalidTransaction::LackOfFundForMaxFee { + fee: Box::new(max_balance_spending), + balance: Box::new(balance), + }); + } + Ok(()) + } + /// Returns the effective balance that is going to be spent that depends on base_fee /// Multiplication for gas are done in u128 type (saturated) and value is added as U256 type. /// + /// It is calculated as `tx.effective_gas_price * tx.gas_limit + tx.value`. Additionally adding + /// `blob_price * tx.total_blob_gas` blob fee if transaction is EIP-4844. + /// /// # Reason /// /// This is done for performance reasons and it is known to be safe as there is no more that u128::MAX value of eth in existence. @@ -189,6 +208,7 @@ pub trait Transaction { /// This is always strictly less than [`Self::max_balance_spending`]. /// /// Return U256 or error if all values overflow U256 number. + #[inline] fn effective_balance_spending( &self, base_fee: u128, @@ -210,4 +230,18 @@ pub trait Transaction { Ok(effective_balance_spending) } + + /// Returns the effective balance calculated with [`Self::effective_balance_spending`] but without the value. + /// + /// Effective balance is always strictly less than [`Self::max_balance_spending`]. + /// + /// This functions returns `tx.effective_gas_price * tx.gas_limit + blob_price * tx.total_blob_gas`. + #[inline] + fn gas_balance_spending( + &self, + base_fee: u128, + blob_price: u128, + ) -> Result { + Ok(self.effective_balance_spending(base_fee, blob_price)? - self.value()) + } } diff --git a/crates/context/src/block.rs b/crates/context/src/block.rs index 7676030f31..0799f82ffc 100644 --- a/crates/context/src/block.rs +++ b/crates/context/src/block.rs @@ -35,8 +35,6 @@ pub struct BlockEnv { pub prevrandao: Option, /// Excess blob gas and blob gasprice /// - /// See also [`calc_excess_blob_gas`][context_interface::block::calc_excess_blob_gas] - /// and [`calc_blob_gasprice`][context_interface::block::blob::calc_blob_gasprice]. /// /// Incorporated as part of the Cancun upgrade via [EIP-4844]. /// diff --git a/crates/context/src/cfg.rs b/crates/context/src/cfg.rs index 7ee337ba55..4abbf1a99f 100644 --- a/crates/context/src/cfg.rs +++ b/crates/context/src/cfg.rs @@ -92,6 +92,14 @@ pub struct CfgEnv { /// By default, it is set to `false`. #[cfg(feature = "optional_eip3607")] pub disable_eip3607: bool, + /// EIP-7623 increases calldata cost. + /// + /// This EIP can be considered irrelevant in the context of an EVM-compatible L2 rollup, + /// if it does not make use of blobs. + /// + /// By default, it is set to `false`. + #[cfg(feature = "optional_eip7623")] + pub disable_eip7623: bool, /// Disables base fee checks for EIP-1559 transactions /// /// This is useful for testing method calls with zero gas price. @@ -171,6 +179,8 @@ impl CfgEnv { disable_eip3541: false, #[cfg(feature = "optional_eip3607")] disable_eip3607: false, + #[cfg(feature = "optional_eip7623")] + disable_eip7623: false, #[cfg(feature = "optional_no_base_fee")] disable_base_fee: false, #[cfg(feature = "optional_priority_fee_check")] @@ -224,6 +234,8 @@ impl CfgEnv { disable_eip3541: self.disable_eip3541, #[cfg(feature = "optional_eip3607")] disable_eip3607: self.disable_eip3607, + #[cfg(feature = "optional_eip7623")] + disable_eip7623: self.disable_eip7623, #[cfg(feature = "optional_no_base_fee")] disable_base_fee: self.disable_base_fee, #[cfg(feature = "optional_priority_fee_check")] @@ -267,6 +279,13 @@ impl CfgEnv { self } + /// Sets the disable eip7623 flag. + #[cfg(feature = "optional_eip7623")] + pub fn with_disable_eip7623(mut self, disable: bool) -> Self { + self.disable_eip7623 = disable; + self + } + /// Enables EIP-7702. #[cfg(feature = "enable_eip7702")] pub fn enable_eip_7702(mut self) -> CfgEnv { @@ -349,6 +368,16 @@ impl + Copy> Cfg for CfgEnv { } } + fn is_eip7623_disabled(&self) -> bool { + cfg_if::cfg_if! { + if #[cfg(feature = "optional_eip7623")] { + self.disable_eip7623 + } else { + false + } + } + } + fn is_balance_check_disabled(&self) -> bool { cfg_if::cfg_if! { if #[cfg(feature = "optional_balance_check")] { @@ -404,6 +433,16 @@ impl + Copy> Cfg for CfgEnv { } } + fn memory_limit(&self) -> u64 { + cfg_if::cfg_if! { + if #[cfg(feature = "memory_limit")] { + self.memory_limit + } else { + u64::MAX + } + } + } + fn is_eip7702_enabled(&self) -> bool { cfg_if::cfg_if! { if #[cfg(feature = "enable_eip7702")] { diff --git a/crates/context/src/context.rs b/crates/context/src/context.rs index 1745d57493..477618e011 100644 --- a/crates/context/src/context.rs +++ b/crates/context/src/context.rs @@ -2,12 +2,13 @@ use crate::{block::BlockEnv, cfg::CfgEnv, journal::Journal, tx::TxEnv, LocalContext}; use context_interface::{ context::{ContextError, ContextSetters, SStoreResult, SelfDestructResult, StateLoad}, - journaled_state::AccountLoad, + host::LoadError, + journaled_state::AccountInfoLoad, Block, Cfg, ContextTr, Host, JournalTr, LocalContextTr, Transaction, TransactionType, }; use database_interface::{Database, DatabaseRef, EmptyDB, WrapDatabaseRef}; use derive_where::derive_where; -use primitives::{hardfork::SpecId, Address, Bytes, Log, StorageKey, StorageValue, B256, U256}; +use primitives::{hardfork::SpecId, 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)] @@ -55,79 +56,53 @@ impl< type Local = LOCAL; #[inline] - fn tx(&self) -> &Self::Tx { - &self.tx + fn all( + &self, + ) -> ( + &Self::Block, + &Self::Tx, + &Self::Cfg, + &Self::Db, + &Self::Journal, + &Self::Chain, + &Self::Local, + ) { + let block = &self.block; + let tx = &self.tx; + let cfg = &self.cfg; + let db = self.journaled_state.db(); + let journal = &self.journaled_state; + let chain = &self.chain; + let local = &self.local; + + (block, tx, cfg, db, journal, chain, local) } #[inline] - fn block(&self) -> &Self::Block { - &self.block - } - - #[inline] - fn cfg(&self) -> &Self::Cfg { - &self.cfg - } - - #[inline] - fn journal(&self) -> &Self::Journal { - &self.journaled_state - } - - #[inline] - fn journal_mut(&mut self) -> &mut Self::Journal { - &mut self.journaled_state - } - - #[inline] - fn journal_ref(&self) -> &Self::Journal { - &self.journaled_state - } - - #[inline] - fn db(&self) -> &Self::Db { - self.journaled_state.db() - } - - #[inline] - fn db_mut(&mut self) -> &mut Self::Db { - self.journaled_state.db_mut() - } - - #[inline] - fn chain(&self) -> &Self::Chain { - &self.chain - } - - #[inline] - fn chain_mut(&mut self) -> &mut Self::Chain { - &mut self.chain - } - - #[inline] - fn local(&self) -> &Self::Local { - &self.local - } - - #[inline] - fn local_mut(&mut self) -> &mut Self::Local { - &mut self.local + fn all_mut( + &mut self, + ) -> ( + &Self::Block, + &Self::Tx, + &Self::Cfg, + &mut Self::Journal, + &mut Self::Chain, + &mut Self::Local, + ) { + let block = &self.block; + let tx = &self.tx; + let cfg = &self.cfg; + let journal = &mut self.journaled_state; + let chain = &mut self.chain; + let local = &mut self.local; + + (block, tx, cfg, journal, chain, local) } #[inline] fn error(&mut self) -> &mut Result<(), ContextError<::Error>> { &mut self.error } - - #[inline] - fn tx_journal_mut(&mut self) -> (&Self::Tx, &mut Self::Journal) { - (&self.tx, &mut self.journaled_state) - } - - #[inline] - fn tx_local_mut(&mut self) -> (&Self::Tx, &mut Self::Local) { - (&self.tx, &mut self.local) - } } impl< @@ -530,74 +505,6 @@ impl< /* Journal */ - fn load_account_delegated(&mut self, address: Address) -> Option> { - let is_eip7702_enabled = self.cfg().is_eip7702_enabled(); - self.journal_mut() - .load_account_delegated(is_eip7702_enabled, address) - .map_err(|e| { - *self.error() = Err(e.into()); - }) - .ok() - } - - /// Gets balance of `address` and if the account is cold. - fn balance(&mut self, address: Address) -> Option> { - self.journal_mut() - .load_account(address) - .map(|acc| acc.map(|a| a.info.balance)) - .map_err(|e| { - *self.error() = Err(e.into()); - }) - .ok() - } - - /// Gets code of `address` and if the account is cold. - fn load_account_code(&mut self, address: Address) -> Option> { - self.journal_mut() - .code(address) - .map_err(|e| { - *self.error() = Err(e.into()); - }) - .ok() - } - - /// Gets code hash of `address` and if the account is cold. - fn load_account_code_hash(&mut self, address: Address) -> Option> { - self.journal_mut() - .code_hash(address) - .map_err(|e| { - *self.error() = Err(e.into()); - }) - .ok() - } - - /// Gets storage value of `address` at `index` and if the account is cold. - fn sload(&mut self, address: Address, index: StorageKey) -> Option> { - self.journal_mut() - .sload(address, index) - .map_err(|e| { - *self.error() = Err(e.into()); - }) - .ok() - } - - /// Sets storage value of account address at index. - /// - /// Returns [`StateLoad`] with [`SStoreResult`] that contains original/new/old storage value. - fn sstore( - &mut self, - address: Address, - index: StorageKey, - value: StorageValue, - ) -> Option> { - self.journal_mut() - .sstore(address, index, value) - .map_err(|e| { - *self.error() = Err(e.into()); - }) - .ok() - } - /// Gets the transient storage value of `address` at `index`. fn tload(&mut self, address: Address, index: StorageKey) -> StorageValue { self.journal_mut().tload(address, index) @@ -614,6 +521,7 @@ impl< } /// Marks `address` to be deleted, with funds transferred to `target`. + #[inline] fn selfdestruct( &mut self, address: Address, @@ -626,4 +534,62 @@ impl< }) .ok() } + + #[inline] + fn sstore_skip_cold_load( + &mut self, + address: Address, + key: StorageKey, + value: StorageValue, + skip_cold_load: bool, + ) -> Result, LoadError> { + self.journal_mut() + .sstore_skip_cold_load(address, key, value, skip_cold_load) + .map_err(|e| { + let (ret, err) = e.into_parts(); + if let Some(err) = err { + *self.error() = Err(err.into()); + } + ret + }) + } + + #[inline] + fn sload_skip_cold_load( + &mut self, + address: Address, + key: StorageKey, + skip_cold_load: bool, + ) -> Result, LoadError> { + self.journal_mut() + .sload_skip_cold_load(address, key, skip_cold_load) + .map_err(|e| { + let (ret, err) = e.into_parts(); + if let Some(err) = err { + *self.error() = Err(err.into()); + } + ret + }) + } + + #[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) { + Ok(a) => Ok(a), + Err(e) => { + let (ret, err) = e.into_parts(); + if let Some(err) = err { + *error = Err(err.into()); + } + Err(ret) + } + } + } } diff --git a/crates/context/src/evm.rs b/crates/context/src/evm.rs index aed5313950..786505fdb7 100644 --- a/crates/context/src/evm.rs +++ b/crates/context/src/evm.rs @@ -22,7 +22,7 @@ pub struct Evm { pub frame_stack: FrameStack, } -impl Evm { +impl Evm { /// Create a new EVM instance with a given context, instruction set, and precompile provider. /// /// Inspector will be set to `()`. @@ -32,12 +32,12 @@ impl Evm { inspector: (), instruction, precompiles, - frame_stack: FrameStack::new(), + frame_stack: FrameStack::new_prealloc(8), } } } -impl Evm { +impl Evm { /// Create a new EVM instance with a given context, inspector, instruction set, and precompile provider. pub fn new_with_inspector(ctx: CTX, inspector: INSP, instruction: I, precompiles: P) -> Self { Evm { @@ -45,7 +45,7 @@ impl Evm { inspector, instruction, precompiles, - frame_stack: FrameStack::new(), + frame_stack: FrameStack::new_prealloc(8), } } } diff --git a/crates/context/src/journal.rs b/crates/context/src/journal.rs index e6f63accfd..4fe371e1fb 100644 --- a/crates/context/src/journal.rs +++ b/crates/context/src/journal.rs @@ -2,21 +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::{AccountLoad, JournalCheckpoint, JournalTr, TransferError}, + journaled_state::{ + 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; @@ -88,6 +92,7 @@ impl Journal { impl JournalTr for Journal { type Database = DB; type State = EvmState; + type JournalEntry = ENTRY; fn new(database: DB) -> Journal { Self { @@ -109,7 +114,9 @@ impl JournalTr for Journal { address: Address, key: StorageKey, ) -> Result, ::Error> { - self.inner.sload(&mut self.database, address, key) + self.inner + .sload(&mut self.database, address, key, false) + .map_err(JournalLoadError::unwrap_db_error) } fn sstore( @@ -118,7 +125,9 @@ impl JournalTr for Journal { key: StorageKey, value: StorageValue, ) -> Result, ::Error> { - self.inner.sstore(&mut self.database, address, key, value) + self.inner + .sstore(&mut self.database, address, key, value, false) + .map_err(JournalLoadError::unwrap_db_error) } fn tload(&mut self, address: Address, key: StorageKey) -> StorageValue { @@ -141,6 +150,11 @@ impl JournalTr for Journal { self.inner.selfdestruct(&mut self.database, address, target) } + #[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) { self.inner.warm_addresses.set_coinbase(address); } @@ -162,17 +176,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)?; - Ok(()) - } - #[inline] fn set_spec_id(&mut self, spec_id: SpecId) { self.inner.spec = spec_id; @@ -188,6 +191,16 @@ impl JournalTr for Journal { self.inner.transfer(&mut self.database, from, to, balance) } + #[inline] + fn transfer_loaded( + &mut self, + from: Address, + to: Address, + balance: U256, + ) -> Option { + self.inner.transfer_loaded(from, to, balance) + } + #[inline] fn touch_account(&mut self, address: Address) { self.inner.touch(address); @@ -222,15 +235,29 @@ 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_optional_code( &mut self, address: Address, - ) -> Result, DB::Error> { + load_code: bool, + ) -> Result< + StateLoad>, + ::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) } @@ -297,4 +324,44 @@ impl JournalTr for Journal { fn finalize(&mut self) -> Self::State { self.inner.finalize() } + + #[inline] + fn sload_skip_cold_load( + &mut self, + address: Address, + key: StorageKey, + skip_cold_load: bool, + ) -> Result, JournalLoadError<::Error>> + { + self.inner + .sload(&mut self.database, address, key, skip_cold_load) + } + + #[inline] + fn sstore_skip_cold_load( + &mut self, + address: Address, + key: StorageKey, + value: StorageValue, + skip_cold_load: bool, + ) -> Result, JournalLoadError<::Error>> + { + self.inner + .sstore(&mut self.database, address, key, value, skip_cold_load) + } + + #[inline] + fn load_account_info_skip_cold_load( + &mut self, + address: Address, + load_code: bool, + skip_cold_load: bool, + ) -> Result, JournalLoadError<::Error>> { + let spec = self.inner.spec; + self.inner + .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 3230216692..ff0fd2f733 100644 --- a/crates/context/src/journal/inner.rs +++ b/crates/context/src/journal/inner.rs @@ -1,11 +1,13 @@ //! 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, TransferError}, + journaled_state::{ + account::JournaledAccount, + entry::{JournalEntryTr, SelfdestructionRevertStatus}, + }, + journaled_state::{AccountLoad, JournalCheckpoint, JournalLoadError, TransferError}, }; use core::mem; use database_interface::Database; @@ -117,7 +119,7 @@ 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 +149,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 +173,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(); @@ -293,19 +295,8 @@ 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(()) } @@ -316,48 +307,69 @@ impl JournalInner { } /// Transfers balance from two accounts. Returns error if sender balance is not enough. + /// + /// # Panics + /// + /// Panics if from or to are not loaded. #[inline] - pub fn transfer( + pub fn transfer_loaded( &mut self, - db: &mut DB, from: Address, to: Address, balance: U256, - ) -> Result, DB::Error> { + ) -> Option { + if from == to { + let from_balance = self.state.get_mut(&to).unwrap().info.balance; + // Check if from balance is enough to transfer the balance. + if balance > from_balance { + return Some(TransferError::OutOfFunds); + } + return None; + } + 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); + Self::touch_account(&mut self.journal, to, self.state.get_mut(&to).unwrap()); + return 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)); + return Some(TransferError::OutOfFunds); }; *from_balance = from_balance_decr; // add balance to - let to_account = &mut self.state.get_mut(&to).unwrap(); + let to_account = 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 { - return Ok(Some(TransferError::OverflowPayment)); + // Overflow of U256 balance is not possible to happen on mainnet. We don't bother to return funds from from_acc. + return Some(TransferError::OverflowPayment); }; *to_balance = to_balance_incr; - // Overflow of U256 balance is not possible to happen on mainnet. We don't bother to return funds from from_acc. + // add journal entry self.journal .push(ENTRY::balance_transfer(from, to, balance)); - Ok(None) + None + } + + /// Transfers balance from two accounts. Returns error if sender balance is not enough. + #[inline] + pub fn transfer( + &mut self, + db: &mut DB, + from: Address, + to: Address, + balance: U256, + ) -> Result, DB::Error> { + self.load_account(db, from)?; + self.load_account(db, to)?; + Ok(self.transfer_loaded(from, to, balance)) } /// Creates account or returns false if collision is detected. @@ -386,14 +398,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; @@ -453,7 +457,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. @@ -462,7 +466,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 @@ -564,8 +568,9 @@ impl JournalInner { &mut self, db: &mut DB, address: Address, - ) -> Result, DB::Error> { - self.load_account_optional(db, address, false, []) + ) -> Result, DB::Error> { + self.load_account_optional(db, address, false, false) + .map_err(JournalLoadError::unwrap_db_error) } /// Loads account into memory. If account is EIP-7702 type it will additionally @@ -584,7 +589,9 @@ impl JournalInner { ) -> Result, DB::Error> { 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, [])?; + let account = self + .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); let mut account_load = StateLoad::new( @@ -598,7 +605,9 @@ impl JournalInner { // load delegate code if account is EIP-7702 if let Some(Bytecode::Eip7702(code)) = &account.info.code { let address = code.address(); - let delegate_account = self.load_account(db, address)?; + let delegate_account = self + .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); } @@ -616,25 +625,63 @@ impl JournalInner { &mut self, db: &mut DB, address: Address, - ) -> Result, DB::Error> { - self.load_account_optional(db, address, true, []) + ) -> Result, DB::Error> { + self.load_account_optional(db, address, true, false) + .map_err(JournalLoadError::unwrap_db_error) } - /// Loads account. If account is already loaded it will be marked as warm. + /// Loads account into memory. If account is already loaded it will be marked as warm. #[inline] pub fn load_account_optional( &mut self, db: &mut DB, address: Address, load_code: bool, - storage_keys: impl IntoIterator, - ) -> Result, DB::Error> { + skip_cold_load: bool, + ) -> Result, JournalLoadError> { + let load = self.load_account_mut_optional_code(db, address, load_code, skip_cold_load)?; + Ok(load.map(|i| i.into_account_ref())) + } + + /// Loads account into memory. If account is already loaded it will be marked as warm. + #[inline] + pub fn load_account_mut( + &mut self, + db: &mut DB, + address: Address, + ) -> Result>, DB::Error> { + self.load_account_mut_optional_code(db, address, false, 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_mut_optional_code( + &mut self, + db: &mut DB, + address: Address, + load_code: bool, + skip_cold_load: bool, + ) -> Result>, JournalLoadError> { let load = match self.state.entry(address) { Entry::Occupied(entry) => { let account = entry.into_mut(); - let is_cold = account.mark_warm_with_transaction_id(self.transaction_id); - // if it is colad loaded we need to clear local flags that can interact with selfdestruct + + // 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; + + // mark it warm. + account.mark_warm_with_transaction_id(self.transaction_id); + // if it is cold loaded and we have selfdestructed locally it means that // account was selfdestructed in previous transaction and we need to clear its information and storage. if account.is_selfdestructed_locally() { @@ -650,15 +697,19 @@ impl JournalInner { } } 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); + + // 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() } else { Account::new_not_existing(self.transaction_id) }; - // 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); - StateLoad { data: vac.insert(account), is_cold, @@ -670,29 +721,18 @@ impl JournalInner { if load.is_cold { self.journal.push(ENTRY::account_warmed(address)); } - if load_code { + + if load_code && load.data.info.code.is_none() { 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); - } + let code = if info.code_hash == KECCAK_EMPTY { + Bytecode::default() + } else { + db.code_by_hash(info.code_hash)? + }; + info.code = Some(code); } - for storage_key in storage_keys.into_iter() { - sload_with_account( - load.data, - db, - &mut self.journal, - self.transaction_id, - address, - storage_key, - )?; - } - Ok(load) + Ok(load.map(|i| JournaledAccount::new(address, i, &mut self.journal))) } /// Loads storage slot. @@ -706,18 +746,48 @@ impl JournalInner { db: &mut DB, address: Address, key: StorageKey, - ) -> Result, DB::Error> { + 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, - ) + + 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(self.transaction_id); + if skip_cold_load && is_cold { + return Err(JournalLoadError::ColdLoadSkipped); + } + slot.mark_warm_with_transaction_id(self.transaction_id); + (slot.present_value, is_cold) + } + Entry::Vacant(vac) => { + // is storage cold + let is_cold = !self.warm_addresses.is_storage_warm(&address, &key); + + 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 { + db.storage(address, key)? + }; + vac.insert(EvmStorageSlot::new(value, self.transaction_id)); + + (value, is_cold) + } + }; + + if is_cold { + // add it to journal as cold loaded. + self.journal.push(ENTRY::storage_warmed(address, key)); + } + + Ok(StateLoad::new(value, is_cold)) } /// Stores storage slot. @@ -732,9 +802,10 @@ impl JournalInner { address: Address, key: StorageKey, new: StorageValue, - ) -> Result, DB::Error> { + skip_cold_load: bool, + ) -> Result, JournalLoadError> { // assume that acc exists and load the slot. - let present = self.sload(db, address, key)?; + 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. @@ -820,41 +891,46 @@ 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, -) -> Result, DB::Error> { - let is_newly_created = account.is_created(); - let (value, is_cold) = match account.storage.entry(key) { - Entry::Occupied(occ) => { - let slot = occ.into_mut(); - let is_cold = slot.mark_warm_with_transaction_id(transaction_id); - (slot.present_value, is_cold) - } - Entry::Vacant(vac) => { - // 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()), + }; + 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(&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 6e72b725f7..ac5f1a6a40 100644 --- a/crates/context/src/journal/warm_addresses.rs +++ b/crates/context/src/journal/warm_addresses.rs @@ -2,10 +2,18 @@ //! //! It is used to optimize access to precompile addresses. -use bitvec::{bitvec, order::Lsb0, vec::BitVec}; -use primitives::{short_address, Address, HashSet, SHORT_ADDRESS_CAP}; +use bitvec::{bitvec, vec::BitVec}; +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 +23,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 { @@ -32,9 +42,10 @@ impl WarmAddresses { pub fn new() -> Self { Self { precompile_set: HashSet::default(), - precompile_short_addresses: BitVec::new(), - all_short_addresses: true, + precompile_short_addresses: bitvec![0; SHORT_ADDRESS_CAP], + precompile_all_short_addresses: true, coinbase: None, + access_list: HashMap::default(), } } @@ -53,8 +64,7 @@ impl WarmAddresses { /// Set the precompile addresses and short addresses. #[inline] pub fn set_precompile_addresses(&mut self, addresses: HashSet
) { - // short address is always smaller than SHORT_ADDRESS_CAP - self.precompile_short_addresses = bitvec![usize, Lsb0; 0; SHORT_ADDRESS_CAP]; + self.precompile_short_addresses.fill(false); let mut all_short_addresses = true; for address in addresses.iter() { @@ -65,7 +75,7 @@ impl WarmAddresses { } } - self.all_short_addresses = all_short_addresses; + self.precompile_all_short_addresses = all_short_addresses; self.precompile_set = addresses; } @@ -75,12 +85,25 @@ 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; + } + /// 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 { @@ -89,6 +112,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; @@ -99,13 +127,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. @@ -124,7 +161,11 @@ mod tests { fn test_initialization() { let warm_addresses = WarmAddresses::new(); assert!(warm_addresses.precompile_set.is_empty()); - assert!(warm_addresses.precompile_short_addresses.is_empty()); + assert_eq!( + warm_addresses.precompile_short_addresses.len(), + SHORT_ADDRESS_CAP + ); + assert!(!warm_addresses.precompile_short_addresses.any()); assert!(warm_addresses.coinbase.is_none()); // Test Default trait @@ -143,7 +184,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 47017e1978..55b321ff88 100644 --- a/crates/context/src/local.rs +++ b/crates/context/src/local.rs @@ -1,19 +1,22 @@ //! Local context that is filled by execution. use context_interface::LocalContextTr; use core::cell::RefCell; -use std::{rc::Rc, vec::Vec}; +use std::{rc::Rc, string::String, vec::Vec}; /// Local context that is filled by execution. #[derive(Clone, Debug)] pub struct LocalContext { /// Interpreter shared memory buffer. A reused memory buffer for calls. pub shared_memory_buffer: Rc>>, + /// Optional precompile error message to bubble up. + pub precompile_error_message: Option, } impl Default for LocalContext { fn default() -> Self { Self { shared_memory_buffer: Rc::new(RefCell::new(Vec::with_capacity(1024 * 4))), + precompile_error_message: None, } } } @@ -22,11 +25,20 @@ impl LocalContextTr for LocalContext { fn clear(&mut self) { // Sets len to 0 but it will not shrink to drop the capacity. unsafe { self.shared_memory_buffer.borrow_mut().set_len(0) }; + self.precompile_error_message = None; } fn shared_memory_buffer(&self) -> &Rc>> { &self.shared_memory_buffer } + + fn set_precompile_error_context(&mut self, output: String) { + self.precompile_error_message = Some(output); + } + + fn take_precompile_error_context(&mut self) -> Option { + self.precompile_error_message.take() + } } impl LocalContext { diff --git a/crates/database/CHANGELOG.md b/crates/database/CHANGELOG.md index a5dcf17337..bb3667b381 100644 --- a/crates/database/CHANGELOG.md +++ b/crates/database/CHANGELOG.md @@ -7,6 +7,52 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [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 + +- updated the following local packages: revm-primitives, revm-bytecode, revm-state, revm-database-interface + +## [9.0.0](https://github.com/bluealloy/revm/compare/revm-database-v8.0.0...revm-database-v9.0.0) - 2025-10-09 + +### Other + +- *(database)* optimize BTreeMap lookup in BundleState::build() ([#3068](https://github.com/bluealloy/revm/pull/3068)) +- *(database)* remove unnecessary Send+Sync bounds from TryDatabaseCommit for Arc ([#3063](https://github.com/bluealloy/revm/pull/3063)) +- remove deprecated methods ([#3050](https://github.com/bluealloy/revm/pull/3050)) + +## [8.0.0](https://github.com/bluealloy/revm/compare/revm-database-v7.0.5...revm-database-v8.0.0) - 2025-10-07 + +### Fixed + +- Apply spelling corrections from PRs #2926, #2915, #2908 ([#2978](https://github.com/bluealloy/revm/pull/2978)) + +### Other + +- add boundless ([#3043](https://github.com/bluealloy/revm/pull/3043)) +- *(database)* extract duplicate test balance constants ([#3017](https://github.com/bluealloy/revm/pull/3017)) +- pretty print state in revme statetest ([#2979](https://github.com/bluealloy/revm/pull/2979)) +- *(database)* avoid panic by conditionally using block_in_place ([#2927](https://github.com/bluealloy/revm/pull/2927)) +- add SECURITY.md ([#2956](https://github.com/bluealloy/revm/pull/2956)) + ## [7.0.5](https://github.com/bluealloy/revm/compare/revm-database-v7.0.4...revm-database-v7.0.5) - 2025-08-23 ### Other diff --git a/crates/database/Cargo.toml b/crates/database/Cargo.toml index 099e5c6caf..16ab375e6c 100644 --- a/crates/database/Cargo.toml +++ b/crates/database/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "revm-database" description = "Revm Database implementations" -version = "7.0.5" +version = "9.0.4" authors.workspace = true edition.workspace = true keywords.workspace = true diff --git a/crates/database/interface/CHANGELOG.md b/crates/database/interface/CHANGELOG.md index e308ef5bf3..7568716ac2 100644 --- a/crates/database/interface/CHANGELOG.md +++ b/crates/database/interface/CHANGELOG.md @@ -7,6 +7,49 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [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 + +- updated the following local packages: revm-primitives, revm-state + +## [8.0.1](https://github.com/bluealloy/revm/compare/revm-database-interface-v8.0.0...revm-database-interface-v8.0.1) - 2025-10-09 + +### Other + +- *(database)* remove unnecessary Send+Sync bounds from TryDatabaseCommit for Arc ([#3063](https://github.com/bluealloy/revm/pull/3063)) + +## [8.0.0](https://github.com/bluealloy/revm/compare/revm-database-interface-v7.0.5...revm-database-interface-v8.0.0) - 2025-10-07 + +### Fixed + +- Apply spelling corrections from PRs #2926, #2915, #2908 ([#2978](https://github.com/bluealloy/revm/pull/2978)) + +### Other + +- add boundless ([#3043](https://github.com/bluealloy/revm/pull/3043)) +- *(database)* extract duplicate test balance constants ([#3017](https://github.com/bluealloy/revm/pull/3017)) +- *(database)* avoid panic by conditionally using block_in_place ([#2927](https://github.com/bluealloy/revm/pull/2927)) +- add SECURITY.md ([#2956](https://github.com/bluealloy/revm/pull/2956)) + ## [7.0.5](https://github.com/bluealloy/revm/compare/revm-database-interface-v7.0.4...revm-database-interface-v7.0.5) - 2025-08-23 ### Other diff --git a/crates/database/interface/Cargo.toml b/crates/database/interface/Cargo.toml index b6e19704e0..f553d82806 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 = "7.0.5" +version = "8.0.5" authors.workspace = true edition.workspace = true keywords.workspace = true diff --git a/crates/database/interface/src/async_db.rs b/crates/database/interface/src/async_db.rs index c30831e8ca..5e42d0c9c1 100644 --- a/crates/database/interface/src/async_db.rs +++ b/crates/database/interface/src/async_db.rs @@ -190,7 +190,23 @@ impl HandleOrRuntime { F::Output: Send, { match self { - Self::Handle(handle) => tokio::task::block_in_place(move || handle.block_on(f)), + 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 { + tokio::task::block_in_place(move || handle.block_on(f)) + } else { + handle.block_on(f) + } + } Self::Runtime(rt) => rt.block_on(f), } } diff --git a/crates/database/interface/src/lib.rs b/crates/database/interface/src/lib.rs index ab816aad16..76e816b3e2 100644 --- a/crates/database/interface/src/lib.rs +++ b/crates/database/interface/src/lib.rs @@ -17,14 +17,16 @@ use std::string::String; pub const FFADDRESS: Address = address!("0xffffffffffffffffffffffffffffffffffffffff"); /// BENCH_TARGET address pub const BENCH_TARGET: Address = FFADDRESS; +/// Common test balance used for benchmark addresses +pub const TEST_BALANCE: U256 = U256::from_limbs([10_000_000_000_000_000, 0, 0, 0]); /// BENCH_TARGET_BALANCE balance -pub const BENCH_TARGET_BALANCE: U256 = U256::from_limbs([10_000_000_000_000_000, 0, 0, 0]); +pub const BENCH_TARGET_BALANCE: U256 = TEST_BALANCE; /// Address with all `0xee..ee` in it. Used for testing. pub const EEADDRESS: Address = address!("0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"); /// BENCH_CALLER address pub const BENCH_CALLER: Address = EEADDRESS; /// BENCH_CALLER_BALANCE balance -pub const BENCH_CALLER_BALANCE: U256 = U256::from_limbs([10_000_000_000_000_000, 0, 0, 0]); +pub const BENCH_CALLER_BALANCE: U256 = TEST_BALANCE; #[cfg(feature = "asyncdb")] pub mod async_db; diff --git a/crates/database/interface/src/try_commit.rs b/crates/database/interface/src/try_commit.rs index 9a7595adcf..71595d186b 100644 --- a/crates/database/interface/src/try_commit.rs +++ b/crates/database/interface/src/try_commit.rs @@ -46,7 +46,7 @@ impl Error for ArcUpgradeError {} impl TryDatabaseCommit for Arc where - Db: DatabaseCommit + Send + Sync, + Db: DatabaseCommit, { type Error = ArcUpgradeError; diff --git a/crates/database/src/states/bundle_state.rs b/crates/database/src/states/bundle_state.rs index f4b0a6cceb..79e9ad6b1f 100644 --- a/crates/database/src/states/bundle_state.rs +++ b/crates/database/src/states/bundle_state.rs @@ -303,12 +303,9 @@ impl BundleBuilder { wipe_storage: false, }; - if reverts_map.contains_key(&block_number) { + if let Some(vec) = reverts_map.get_mut(&block_number) { reverts_size += account_revert.size_hint(); - reverts_map - .entry(block_number) - .or_insert_with(Vec::new) - .push((address, account_revert)); + vec.push((address, account_revert)); } }); @@ -659,12 +656,6 @@ impl BundleState { } } - /// Converts the bundle state into a [`StateChangeset`]. - #[deprecated = "Use `to_plain_state` instead"] - pub fn into_plain_state(self, is_value_known: OriginalValuesKnown) -> StateChangeset { - self.to_plain_state(is_value_known) - } - /// Generates a [`StateChangeset`] and [`PlainStateReverts`] from the bundle /// state. pub fn to_plain_state_and_reverts( diff --git a/crates/database/src/states/cache.rs b/crates/database/src/states/cache.rs index 9105f974cd..7a345cc3bc 100644 --- a/crates/database/src/states/cache.rs +++ b/crates/database/src/states/cache.rs @@ -99,6 +99,69 @@ impl CacheState { transitions } + /// Pretty print the cache state for debugging purposes. + #[cfg(feature = "std")] + pub fn pretty_print(&self) -> String { + let mut output = String::new(); + output.push_str("CacheState:\n"); + output.push_str(&format!( + " (state_clear_enabled: {}, ", + self.has_state_clear + )); + output.push_str(&format!("accounts: {} total)\n", self.accounts.len())); + + // Sort accounts by address for consistent output + let mut accounts: Vec<_> = self.accounts.iter().collect(); + accounts.sort_by_key(|(addr, _)| *addr); + + let mut contracts = self.contracts.clone(); + + for (address, account) in accounts { + output.push_str(&format!(" [{address}]:\n")); + output.push_str(&format!(" status: {:?}\n", account.status)); + + if let Some(plain_account) = &account.account { + let code_hash = plain_account.info.code_hash; + output.push_str(&format!(" balance: {}\n", plain_account.info.balance)); + output.push_str(&format!(" nonce: {}\n", plain_account.info.nonce)); + output.push_str(&format!(" code_hash: {code_hash}\n")); + + if let Some(code) = &plain_account.info.code { + if !code.is_empty() { + contracts.insert(code_hash, code.clone()); + } + } + + if !plain_account.storage.is_empty() { + output.push_str(&format!( + " storage: {} slots\n", + plain_account.storage.len() + )); + // Sort storage by key for consistent output + let mut storage: Vec<_> = plain_account.storage.iter().collect(); + storage.sort_by_key(|(key, _)| *key); + + for (key, value) in storage.iter() { + output.push_str(&format!(" [{key:#x}]: {value:#x}\n")); + } + } + } else { + output.push_str(" account: None (destroyed or non-existent)\n"); + } + } + + if !contracts.is_empty() { + output.push_str(&format!(" contracts: {} total\n", contracts.len())); + for (hash, bytecode) in contracts.iter() { + let len = bytecode.len(); + output.push_str(&format!(" [{hash}]: {len} bytes\n")); + } + } + + output.push_str("}\n"); + output + } + /// Applies updated account state to the cached account. /// /// Returns account transition if applicable. diff --git a/crates/ee-tests/Cargo.toml b/crates/ee-tests/Cargo.toml index fee90fdd51..accac39a90 100644 --- a/crates/ee-tests/Cargo.toml +++ b/crates/ee-tests/Cargo.toml @@ -14,8 +14,8 @@ rust-version.workspace = true workspace = true [dependencies] -serde = { version = "1.0", features = ["derive"] } -serde_json = { version = "1.0", features = ["preserve_order"] } +serde.workspace = true +serde_json = { workspace = true, features = ["preserve_order"] } revm = { workspace = true, features = ["serde"] } op-revm = { workspace = true, features = ["serde"] } diff --git a/crates/ee-tests/src/op_revm_tests.rs b/crates/ee-tests/src/op_revm_tests.rs index c98e3d57b1..3995bd727c 100644 --- a/crates/ee-tests/src/op_revm_tests.rs +++ b/crates/ee-tests/src/op_revm_tests.rs @@ -261,9 +261,9 @@ fn test_halted_tx_call_bn254_pair_granite() { assert!(matches!( output.result, ExecutionResult::Halt { - reason: OpHaltReason::Base(HaltReason::PrecompileError), + reason: OpHaltReason::Base(HaltReason::PrecompileErrorWithContext(ref msg)), .. - } + } if msg == "bn254 invalid pair length" )); compare_or_save_op_testdata("test_halted_tx_call_bn254_pair_granite.json", &output); @@ -331,9 +331,9 @@ fn test_halted_tx_call_bls12_381_g1_add_input_wrong_size() { assert!(matches!( output.result, ExecutionResult::Halt { - reason: OpHaltReason::Base(HaltReason::PrecompileError), + reason: OpHaltReason::Base(HaltReason::PrecompileErrorWithContext(ref msg)), .. - } + } if msg == "bls12-381 g1 add input length error" )); compare_or_save_op_testdata( @@ -431,9 +431,9 @@ fn test_halted_tx_call_bls12_381_g1_msm_input_wrong_size() { assert!(matches!( output.result, ExecutionResult::Halt { - reason: OpHaltReason::Base(HaltReason::PrecompileError), + reason: OpHaltReason::Base(HaltReason::PrecompileErrorWithContext(ref msg)), .. - } + } if msg == "bls12-381 g1 msm input length error" )); compare_or_save_op_testdata( @@ -511,9 +511,9 @@ fn test_halted_tx_call_bls12_381_g1_msm_wrong_input_layout() { assert!(matches!( output.result, ExecutionResult::Halt { - reason: OpHaltReason::Base(HaltReason::PrecompileError), + reason: OpHaltReason::Base(HaltReason::PrecompileErrorWithContext(ref msg)), .. - } + } if msg == "bls12-381 fp 64 top bytes of input are not zero" )); compare_or_save_op_testdata( @@ -585,9 +585,9 @@ fn test_halted_tx_call_bls12_381_g2_add_input_wrong_size() { assert!(matches!( output.result, ExecutionResult::Halt { - reason: OpHaltReason::Base(HaltReason::PrecompileError), + reason: OpHaltReason::Base(HaltReason::PrecompileErrorWithContext(ref msg)), .. - } + } if msg == "bls12-381 g2 add input length error" )); compare_or_save_op_testdata( @@ -685,9 +685,9 @@ fn test_halted_tx_call_bls12_381_g2_msm_input_wrong_size() { assert!(matches!( output.result, ExecutionResult::Halt { - reason: OpHaltReason::Base(HaltReason::PrecompileError), + reason: OpHaltReason::Base(HaltReason::PrecompileErrorWithContext(ref msg)), .. - } + } if msg == "bls12-381 g2 msm input length error" )); compare_or_save_op_testdata( @@ -765,9 +765,9 @@ fn test_halted_tx_call_bls12_381_g2_msm_wrong_input_layout() { assert!(matches!( output.result, ExecutionResult::Halt { - reason: OpHaltReason::Base(HaltReason::PrecompileError), + reason: OpHaltReason::Base(HaltReason::PrecompileErrorWithContext(ref msg)), .. - } + } if msg == "bls12-381 fp 64 top bytes of input are not zero" )); compare_or_save_op_testdata( @@ -860,9 +860,9 @@ fn test_halted_tx_call_bls12_381_pairing_input_wrong_size() { assert!(matches!( output.result, ExecutionResult::Halt { - reason: OpHaltReason::Base(HaltReason::PrecompileError), + reason: OpHaltReason::Base(HaltReason::PrecompileErrorWithContext(ref msg)), .. - } + } if msg == "bls12-381 pairing input length error" )); compare_or_save_op_testdata( @@ -937,9 +937,9 @@ fn test_tx_call_bls12_381_pairing_wrong_input_layout() { assert!(matches!( output.result, ExecutionResult::Halt { - reason: OpHaltReason::Base(HaltReason::PrecompileError), + reason: OpHaltReason::Base(HaltReason::PrecompileErrorWithContext(ref msg)), .. - } + } if msg == "bls12-381 fp 64 top bytes of input are not zero" )); compare_or_save_op_testdata( @@ -1043,9 +1043,9 @@ fn test_halted_tx_call_bls12_381_map_fp_to_g1_input_wrong_size() { assert!(matches!( output.result, ExecutionResult::Halt { - reason: OpHaltReason::Base(HaltReason::PrecompileError), + reason: OpHaltReason::Base(HaltReason::PrecompileErrorWithContext(ref msg)), .. - } + } if msg == "bls12-381 map fp to g1 input length error" )); compare_or_save_op_testdata( @@ -1149,9 +1149,9 @@ fn test_halted_tx_call_bls12_381_map_fp2_to_g2_input_wrong_size() { assert!(matches!( output.result, ExecutionResult::Halt { - reason: OpHaltReason::Base(HaltReason::PrecompileError), + reason: OpHaltReason::Base(HaltReason::PrecompileErrorWithContext(ref msg)), .. - } + } if msg == "bls12-381 map fp2 to g2 input length error" )); compare_or_save_op_testdata( 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 91cea8ca54..9ab987baba 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 @@ -3,7 +3,9 @@ "Halt": { "gas_used": 21375, "reason": { - "Base": "PrecompileError" + "Base": { + "PrecompileErrorWithContext": "bls12-381 g1 add input length error" + } } } }, 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 5fdee80453..5e9e175426 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 @@ -3,7 +3,9 @@ "Halt": { "gas_used": 35560, "reason": { - "Base": "PrecompileError" + "Base": { + "PrecompileErrorWithContext": "bls12-381 g1 msm input length error" + } } } }, 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 5fdee80453..d825d723b8 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 @@ -3,7 +3,9 @@ "Halt": { "gas_used": 35560, "reason": { - "Base": "PrecompileError" + "Base": { + "PrecompileErrorWithContext": "bls12-381 fp 64 top bytes of input are not zero" + } } } }, 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 b2970ea9cb..58c506307a 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 @@ -3,7 +3,9 @@ "Halt": { "gas_used": 21600, "reason": { - "Base": "PrecompileError" + "Base": { + "PrecompileErrorWithContext": "bls12-381 g2 add input length error" + } } } }, 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 4ef0718ec2..bae68df493 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 @@ -3,7 +3,9 @@ "Halt": { "gas_used": 48108, "reason": { - "Base": "PrecompileError" + "Base": { + "PrecompileErrorWithContext": "bls12-381 g2 msm input length error" + } } } }, 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 4ef0718ec2..87cb378c40 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 @@ -3,7 +3,9 @@ "Halt": { "gas_used": 48108, "reason": { - "Base": "PrecompileError" + "Base": { + "PrecompileErrorWithContext": "bls12-381 fp 64 top bytes of input are not zero" + } } } }, 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 311e0b6a77..25718c3549 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 @@ -3,7 +3,9 @@ "Halt": { "gas_used": 46848, "reason": { - "Base": "PrecompileError" + "Base": { + "PrecompileErrorWithContext": "bls12-381 map fp2 to g2 input length error" + } } } }, 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 1b768b6653..250bcb34c5 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 @@ -3,7 +3,9 @@ "Halt": { "gas_used": 27524, "reason": { - "Base": "PrecompileError" + "Base": { + "PrecompileErrorWithContext": "bls12-381 map fp to g1 input length error" + } } } }, 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 b650e8b8f1..c550b7a9c7 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 @@ -3,7 +3,9 @@ "Halt": { "gas_used": 97444, "reason": { - "Base": "PrecompileError" + "Base": { + "PrecompileErrorWithContext": "bls12-381 pairing input length error" + } } } }, 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 b650e8b8f1..fadbf7cb6f 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 @@ -3,7 +3,9 @@ "Halt": { "gas_used": 97444, "reason": { - "Base": "PrecompileError" + "Base": { + "PrecompileErrorWithContext": "bls12-381 fp 64 top bytes of input are not zero" + } } } }, 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 85591e9afa..b457155b93 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 @@ -3,7 +3,9 @@ "Halt": { "gas_used": 1824024, "reason": { - "Base": "PrecompileError" + "Base": { + "PrecompileErrorWithContext": "bn254 invalid pair length" + } } } }, diff --git a/crates/ee-tests/tests/op_revm_testdata/test_system_call.json b/crates/ee-tests/tests/op_revm_testdata/test_system_call.json deleted file mode 100644 index eb8f4e1a10..0000000000 --- a/crates/ee-tests/tests/op_revm_testdata/test_system_call.json +++ /dev/null @@ -1,165 +0,0 @@ -{ - "result": { - "Success": { - "gas_refunded": 0, - "gas_used": 21020, - "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 - }, - "status": "Touched | LoadedAsNotExisting", - "storage": {}, - "transaction_id": 1 - }, - "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 - }, - "status": "Touched | LoadedAsNotExisting", - "storage": {}, - "transaction_id": 1 - }, - "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 - }, - "status": "Touched | LoadedAsNotExisting", - "storage": {}, - "transaction_id": 1 - }, - "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 - }, - "status": "Touched | LoadedAsNotExisting", - "storage": {}, - "transaction_id": 1 - }, - "0xfffffffffffffffffffffffffffffffffffffffe": { - "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": 1 - }, - "status": "Touched | LoadedAsNotExisting", - "storage": {}, - "transaction_id": 1 - }, - "0xffffffffffffffffffffffffffffffffffffffff": { - "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": 1 - } - } -} \ No newline at end of file diff --git a/crates/handler/CHANGELOG.md b/crates/handler/CHANGELOG.md index 239dc2be43..d9b52f852a 100644 --- a/crates/handler/CHANGELOG.md +++ b/crates/handler/CHANGELOG.md @@ -7,6 +7,87 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [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 + +- resize short addresses bitvec instead of reallocating ([#3083](https://github.com/bluealloy/revm/pull/3083)) +- *(handler)* extract duplicate gas price validation ([#3045](https://github.com/bluealloy/revm/pull/3045)) + +## [11.1.0](https://github.com/bluealloy/revm/compare/revm-handler-v11.0.0...revm-handler-v11.1.0) - 2025-10-09 + +### Other + +- helper calculate_caller_fee ([#3040](https://github.com/bluealloy/revm/pull/3040)) + +## [11.0.0](https://github.com/bluealloy/revm/compare/revm-handler-v10.0.1...revm-handler-v11.0.0) - 2025-10-07 + +### Added + +- Support bubbling up first precompile error messages ([#2905](https://github.com/bluealloy/revm/pull/2905)) +- in JumpTable use Bytes instead of BitVec ([#3014](https://github.com/bluealloy/revm/pull/3014)) +- add transaction index to batch execution error handling ([#3000](https://github.com/bluealloy/revm/pull/3000)) +- allow EIP-7623 to be disabled ([#2985](https://github.com/bluealloy/revm/pull/2985)) +- send bytecode with call input ([#2963](https://github.com/bluealloy/revm/pull/2963)) +- *(revme)* ef blockchain tests cli ([#2935](https://github.com/bluealloy/revm/pull/2935)) + +### Fixed + +- Apply spelling corrections from PRs #2926, #2915, #2908 ([#2978](https://github.com/bluealloy/revm/pull/2978)) +- interpreter_result_mut should return mutable reference ([#2941](https://github.com/bluealloy/revm/pull/2941)) +- FrameStack mark push/end_init as unsafe ([#2929](https://github.com/bluealloy/revm/pull/2929)) + +### Other + +- changelog update for v87 ([#3056](https://github.com/bluealloy/revm/pull/3056)) +- add boundless ([#3043](https://github.com/bluealloy/revm/pull/3043)) +- helper function gas_balance_spending ([#3030](https://github.com/bluealloy/revm/pull/3030)) +- helper caller_initial_modification added ([#3032](https://github.com/bluealloy/revm/pull/3032)) +- Frame use From in place of Into ([#3036](https://github.com/bluealloy/revm/pull/3036)) +- EvmTr and InspectorEvmTr receive all/all_mut fn ([#3037](https://github.com/bluealloy/revm/pull/3037)) +- add ensure_enough_balance helper ([#3033](https://github.com/bluealloy/revm/pull/3033)) +- prealloc few frames ([#2965](https://github.com/bluealloy/revm/pull/2965)) +- Fix infinite recursion in EthPrecompiles PrecompileProvider methods ([#2962](https://github.com/bluealloy/revm/pull/2962)) +- add SECURITY.md ([#2956](https://github.com/bluealloy/revm/pull/2956)) +- update `EthFrame::invalid` visibility ([#2947](https://github.com/bluealloy/revm/pull/2947)) +- remove unused generic from validate_tx_env and fix call site ([#2946](https://github.com/bluealloy/revm/pull/2946)) +- cargo update ([#2930](https://github.com/bluealloy/revm/pull/2930)) +- *(handler)* provide `&CallInputs`to`PrecompileProvider::run` ([#2921](https://github.com/bluealloy/revm/pull/2921)) + ## [10.0.1](https://github.com/bluealloy/revm/compare/revm-handler-v10.0.0...revm-handler-v10.0.1) - 2025-09-23 ### Other diff --git a/crates/handler/Cargo.toml b/crates/handler/Cargo.toml index 5110a6f009..c2727e0b45 100644 --- a/crates/handler/Cargo.toml +++ b/crates/handler/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "revm-handler" description = "Revm handler crates" -version = "10.0.1" +version = "12.0.1" authors.workspace = true edition.workspace = true keywords.workspace = true @@ -32,14 +32,10 @@ auto_impl.workspace = true derive-where.workspace = true # Optional -serde = { version = "1.0", default-features = false, features = [ - "derive", - "rc", -], optional = true } +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 @@ -48,7 +44,6 @@ alloy-signer-local.workspace = true default = ["std"] std = [ "serde?/std", - "alloy-eip7702/std", "bytecode/std", "context/std", "context-interface/std", @@ -64,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/src/api.rs b/crates/handler/src/api.rs index 020d03e4df..a2faacf186 100644 --- a/crates/handler/src/api.rs +++ b/crates/handler/src/api.rs @@ -4,7 +4,7 @@ use crate::{ use context::{ result::{ EVMError, ExecResultAndState, ExecutionResult, HaltReason, InvalidTransaction, - ResultAndState, ResultVecAndState, + ResultAndState, ResultVecAndState, TransactionIndexedError, }, Block, ContextSetters, ContextTr, Database, Evm, JournalTr, Transaction, }; @@ -13,6 +13,10 @@ use interpreter::{interpreter::EthInterpreter, InterpreterResult}; use state::EvmState; use std::vec::Vec; +/// Type alias for the result of transact_many_finalize to reduce type complexity. +type TransactManyFinalizeResult = + Result, TransactionIndexedError>; + /// Execute EVM transactions. Main trait for transaction execution. pub trait ExecuteEvm { /// Output of transaction execution. @@ -79,19 +83,22 @@ pub trait ExecuteEvm { /// /// # Outcome of Error /// - /// If any transaction fails, the journal is finalized and the last error is returned. - /// - /// TODO add tx index to the error. + /// If any transaction fails, the journal is finalized and the error is returned with the + /// transaction index that failed. #[inline] fn transact_many( &mut self, txs: impl Iterator, - ) -> Result, Self::Error> { + ) -> Result, TransactionIndexedError> { let mut outputs = Vec::new(); - for tx in txs { - outputs.push(self.transact_one(tx).inspect_err(|_| { - let _ = self.finalize(); - })?); + for (index, tx) in txs.enumerate() { + outputs.push( + self.transact_one(tx) + .inspect_err(|_| { + let _ = self.finalize(); + }) + .map_err(|error| TransactionIndexedError::new(error, index))?, + ); } Ok(outputs) } @@ -103,7 +110,7 @@ pub trait ExecuteEvm { fn transact_many_finalize( &mut self, txs: impl Iterator, - ) -> Result, Self::Error> { + ) -> TransactManyFinalizeResult { // on error transact_multi will clear the journal let result = self.transact_many(txs)?; let state = self.finalize(); @@ -145,7 +152,7 @@ pub trait ExecuteCommitEvm: ExecuteEvm { fn transact_many_commit( &mut self, txs: impl Iterator, - ) -> Result, Self::Error> { + ) -> Result, TransactionIndexedError> { let outputs = self.transact_many(txs)?; self.commit_inner(); Ok(outputs) diff --git a/crates/handler/src/evm.rs b/crates/handler/src/evm.rs index 72a11e984e..0ad3130f0c 100644 --- a/crates/handler/src/evm.rs +++ b/crates/handler/src/evm.rs @@ -20,9 +20,9 @@ pub type FrameInitResult<'a, F> = ItemOrResult<&'a mut F, ::FrameR #[auto_impl(&mut, Box)] pub trait FrameTr { /// The result type returned when a frame completes execution. - type FrameResult: Into; + type FrameResult: From; /// The initialization type used to create a new frame. - type FrameInit: Into; + type FrameInit: From; } /// A trait that integrates context, instruction set, and precompiles to create an EVM struct. @@ -39,27 +39,70 @@ pub trait EvmTr { /// The type containing the frame type Frame: FrameTr; + /// Returns a tuple of references to the context, the frame and the instructions. + #[allow(clippy::type_complexity)] + fn all( + &self, + ) -> ( + &Self::Context, + &Self::Instructions, + &Self::Precompiles, + &FrameStack, + ); + + /// Returns a tuple of mutable references to the context, the frame and the instructions. + #[allow(clippy::type_complexity)] + fn all_mut( + &mut self, + ) -> ( + &mut Self::Context, + &mut Self::Instructions, + &mut Self::Precompiles, + &mut FrameStack, + ); + /// Returns a mutable reference to the execution context - fn ctx(&mut self) -> &mut Self::Context; + #[inline] + fn ctx(&mut self) -> &mut Self::Context { + let (ctx, _, _, _) = self.all_mut(); + ctx + } /// Returns a mutable reference to the execution context + #[inline] fn ctx_mut(&mut self) -> &mut Self::Context { self.ctx() } /// Returns an immutable reference to the execution context - fn ctx_ref(&self) -> &Self::Context; + #[inline] + fn ctx_ref(&self) -> &Self::Context { + let (ctx, _, _, _) = self.all(); + ctx + } /// Returns mutable references to both the context and instruction set. /// This enables atomic access to both components when needed. - fn ctx_instructions(&mut self) -> (&mut Self::Context, &mut Self::Instructions); + #[inline] + fn ctx_instructions(&mut self) -> (&mut Self::Context, &mut Self::Instructions) { + let (ctx, instructions, _, _) = self.all_mut(); + (ctx, instructions) + } /// Returns mutable references to both the context and precompiles. /// This enables atomic access to both components when needed. - fn ctx_precompiles(&mut self) -> (&mut Self::Context, &mut Self::Precompiles); + #[inline] + fn ctx_precompiles(&mut self) -> (&mut Self::Context, &mut Self::Precompiles) { + let (ctx, _, precompiles, _) = self.all_mut(); + (ctx, precompiles) + } /// Returns a mutable reference to the frame stack. - fn frame_stack(&mut self) -> &mut FrameStack; + #[inline] + fn frame_stack(&mut self) -> &mut FrameStack { + let (_, _, _, frame_stack) = self.all_mut(); + frame_stack + } /// Initializes the frame for the given frame input. Frame is pushed to the frame stack. fn frame_init( @@ -94,18 +137,35 @@ where type Frame = EthFrame; #[inline] - fn ctx(&mut self) -> &mut Self::Context { - &mut self.ctx - } - - #[inline] - fn ctx_ref(&self) -> &Self::Context { - &self.ctx + fn all( + &self, + ) -> ( + &Self::Context, + &Self::Instructions, + &Self::Precompiles, + &FrameStack, + ) { + let ctx = &self.ctx; + let instructions = &self.instruction; + let precompiles = &self.precompiles; + let frame_stack = &self.frame_stack; + (ctx, instructions, precompiles, frame_stack) } #[inline] - fn frame_stack(&mut self) -> &mut FrameStack { - &mut self.frame_stack + fn all_mut( + &mut self, + ) -> ( + &mut Self::Context, + &mut Self::Instructions, + &mut Self::Precompiles, + &mut FrameStack, + ) { + let ctx = &mut self.ctx; + let instructions = &mut self.instruction; + let precompiles = &mut self.precompiles; + let frame_stack = &mut self.frame_stack; + (ctx, instructions, precompiles, frame_stack) } /// Initializes the frame for the given frame input. Frame is pushed to the frame stack. @@ -127,9 +187,9 @@ where Ok(res.map_frame(|token| { if is_first_init { - self.frame_stack.end_init(token); + unsafe { self.frame_stack.end_init(token) }; } else { - self.frame_stack.push(token); + unsafe { self.frame_stack.push(token) }; } self.frame_stack.get() })) @@ -170,14 +230,4 @@ where .return_result::<_, ContextDbError>(&mut self.ctx, result)?; Ok(None) } - - #[inline] - fn ctx_instructions(&mut self) -> (&mut Self::Context, &mut Self::Instructions) { - (&mut self.ctx, &mut self.instruction) - } - - #[inline] - fn ctx_precompiles(&mut self) -> (&mut Self::Context, &mut Self::Precompiles) { - (&mut self.ctx, &mut self.precompiles) - } } diff --git a/crates/handler/src/execution.rs b/crates/handler/src/execution.rs index 14cc194a2f..e79f9d4b4c 100644 --- a/crates/handler/src/execution.rs +++ b/crates/handler/src/execution.rs @@ -2,25 +2,35 @@ use context_interface::Transaction; use interpreter::{ CallInput, CallInputs, CallScheme, CallValue, CreateInputs, CreateScheme, FrameInput, }; -use primitives::TxKind; +use primitives::{TxKind, B256}; +use state::Bytecode; use std::boxed::Box; /// Creates the first [`FrameInput`] from the transaction, spec and gas limit. -pub fn create_init_frame(tx: &impl Transaction, gas_limit: u64) -> FrameInput { +#[inline] +pub fn create_init_frame( + tx: &impl Transaction, + bytecode: Option<(Bytecode, B256)>, + gas_limit: u64, +) -> FrameInput { let input = tx.input().clone(); match tx.kind() { - TxKind::Call(target_address) => FrameInput::Call(Box::new(CallInputs { - input: CallInput::Bytes(input), - gas_limit, - target_address, - bytecode_address: target_address, - caller: tx.caller(), - value: CallValue::Transfer(tx.value()), - scheme: CallScheme::Call, - is_static: false, - return_memory_offset: 0..0, - })), + TxKind::Call(target_address) => { + 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, + known_bytecode, + caller: tx.caller(), + value: CallValue::Transfer(tx.value()), + scheme: CallScheme::Call, + is_static: false, + return_memory_offset: 0..0, + })) + } TxKind::Create => FrameInput::Create(Box::new(CreateInputs { caller: tx.caller(), scheme: CreateScheme::Create, diff --git a/crates/handler/src/frame.rs b/crates/handler/src/frame.rs index 8b413365fb..db10e07f75 100644 --- a/crates/handler/src/frame.rs +++ b/crates/handler/src/frame.rs @@ -68,7 +68,8 @@ impl Default for EthFrame { } impl EthFrame { - fn invalid() -> Self { + /// Creates an new invalid [`EthFrame`]. + pub fn invalid() -> Self { Self::do_default(Interpreter::invalid()) } @@ -162,12 +163,6 @@ impl EthFrame { return return_result(InstructionResult::CallTooDeep); } - // Make account warm and loaded. - let is_eip7702_enabled = ctx.cfg().is_eip7702_enabled(); - let _ = ctx - .journal_mut() - .load_account_delegated(is_eip7702_enabled, inputs.bytecode_address)?; - // Create subroutine checkpoint let checkpoint = ctx.journal_mut().checkpoint(); @@ -177,7 +172,7 @@ impl EthFrame { // Target will get touched even if balance transferred is zero. if let Some(i) = ctx.journal_mut() - .transfer(inputs.caller, inputs.target_address, value)? + .transfer_loaded(inputs.caller, inputs.target_address, value) { ctx.journal_mut().checkpoint_revert(checkpoint); return return_result(i.into()); @@ -194,16 +189,7 @@ impl EthFrame { let is_static = inputs.is_static; let gas_limit = inputs.gas_limit; - if let Some(result) = precompiles - .run( - ctx, - &inputs.bytecode_address, - &interpreter_input, - is_static, - gas_limit, - ) - .map_err(ERROR::from_string)? - { + if let Some(result) = precompiles.run(ctx, &inputs).map_err(ERROR::from_string)? { if result.result.is_ok() { ctx.journal_mut().checkpoint_commit(); } else { @@ -215,21 +201,20 @@ impl EthFrame { }))); } - let account = ctx - .journal_mut() - .load_account_code(inputs.bytecode_address)?; - - let mut code_hash = account.info.code_hash(); - let mut bytecode = account.info.code.clone().unwrap_or_default(); - - if let Bytecode::Eip7702(eip7702_bytecode) = bytecode { - let account = &ctx + // 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_code(eip7702_bytecode.delegated_address)? - .info; - bytecode = account.code.clone().unwrap_or_default(); - code_hash = account.code_hash(); - } + .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 +230,7 @@ impl EthFrame { FrameInput::Call(inputs), depth, memory, - ExtBytecode::new_with_hash(bytecode, code_hash), + ExtBytecode::new_with_hash(bytecode, bytecode_hash), interpreter_input, is_static, ctx.cfg().spec().into(), @@ -284,29 +269,20 @@ impl EthFrame { return return_error(InstructionResult::CallTooDeep); } - // Prague EOF - // TODO(EOF) - // if spec.is_enabled_in(OSAKA) && inputs.init_code.starts_with(&EOF_MAGIC_BYTES) { - // return return_error(InstructionResult::CreateInitCodeStartingEF00); - // } - // Fetch balance of caller. - let caller_info = &mut context.journal_mut().load_account(inputs.caller)?.data.info; + let mut caller_info = context.journal_mut().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; diff --git a/crates/handler/src/frame_data.rs b/crates/handler/src/frame_data.rs index 2c1d6b5243..fab41ca813 100644 --- a/crates/handler/src/frame_data.rs +++ b/crates/handler/src/frame_data.rs @@ -91,7 +91,7 @@ impl FrameResult { /// Returns mutable reference to interpreter result. #[inline] - pub fn interpreter_result_mut(&mut self) -> &InterpreterResult { + pub fn interpreter_result_mut(&mut self) -> &mut InterpreterResult { match self { FrameResult::Call(outcome) => &mut outcome.result, FrameResult::Create(outcome) => &mut outcome.result, diff --git a/crates/handler/src/handler.rs b/crates/handler/src/handler.rs index 541407ff21..299fa1204b 100644 --- a/crates/handler/src/handler.rs +++ b/crates/handler/src/handler.rs @@ -13,6 +13,7 @@ use context_interface::{ use interpreter::interpreter_action::FrameInit; use interpreter::{Gas, InitialAndFloorGas, SharedMemory}; use primitives::U256; +use state::Bytecode; /// Trait for errors that can occur during EVM execution. /// @@ -223,6 +224,7 @@ pub trait Handler { // Calculate final refund and add EIP-7702 refund to gas. self.refund(evm, exec_result, eip7702_gas_refund); // Ensure gas floor is met and minimum floor gas is spent. + // if `cfg.is_eip7623_disabled` is true, floor gas will be set to zero self.eip7623_check_gas_floor(evm, exec_result, init_and_floor_gas); // Return unused gas to caller self.reimburse_caller(evm, exec_result)?; @@ -253,6 +255,7 @@ pub trait Handler { validation::validate_initial_tx_gas( ctx.tx(), ctx.cfg().spec().into(), + ctx.cfg().is_eip7623_disabled(), ctx.cfg().is_eip7702_enabled(), ctx.cfg().is_eip7623_enabled(), ) @@ -296,13 +299,35 @@ pub trait Handler { evm: &mut Self::Evm, gas_limit: u64, ) -> Result { - let memory = - SharedMemory::new_with_buffer(evm.ctx().local().shared_memory_buffer().clone()); - let ctx = evm.ctx_ref(); + let ctx = evm.ctx_mut(); + 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_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_with_code(delegated_address)?.info; + Some(( + account.code.clone().unwrap_or_default(), + account.code_hash(), + )) + } else { + Some(( + account.code.clone().unwrap_or_default(), + account.code_hash(), + )) + } + } else { + None + }; + Ok(FrameInit { depth: 0, memory, - frame_input: execution::create_init_frame(ctx.tx(), gas_limit), + frame_input: execution::create_init_frame(tx, bytecode, gas_limit), }) } @@ -435,7 +460,7 @@ pub trait Handler { match core::mem::replace(evm.ctx().error(), Ok(())) { Err(ContextError::Db(e)) => return Err(e.into()), Err(ContextError::Custom(e)) => return Err(Self::Error::from_string(e)), - Ok(_) => (), + Ok(()) => (), } let exec_result = post_execution::output(evm.ctx(), result); diff --git a/crates/handler/src/mainnet_builder.rs b/crates/handler/src/mainnet_builder.rs index 4e53287c93..69e981c530 100644 --- a/crates/handler/src/mainnet_builder.rs +++ b/crates/handler/src/mainnet_builder.rs @@ -41,7 +41,7 @@ where inspector: (), instruction: EthInstructions::default(), precompiles: EthPrecompiles::default(), - frame_stack: FrameStack::new(), + frame_stack: FrameStack::new_prealloc(8), } } @@ -54,7 +54,7 @@ where inspector, instruction: EthInstructions::default(), precompiles: EthPrecompiles::default(), - frame_stack: FrameStack::new(), + frame_stack: FrameStack::new_prealloc(8), } } } diff --git a/crates/handler/src/post_execution.rs b/crates/handler/src/post_execution.rs index 69f5d453f3..bd0471d717 100644 --- a/crates/handler/src/post_execution.rs +++ b/crates/handler/src/post_execution.rs @@ -1,8 +1,8 @@ use crate::FrameResult; use context_interface::{ journaled_state::JournalTr, - result::{ExecutionResult, HaltReasonTr}, - Block, Cfg, ContextTr, Database, Transaction, + result::{ExecutionResult, HaltReason, HaltReasonTr}, + Block, Cfg, ContextTr, Database, LocalContextTr, Transaction, }; use interpreter::{Gas, InitialAndFloorGas, SuccessOrHalt}; use primitives::{hardfork::SpecId, U256}; @@ -39,12 +39,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 +58,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(()) } @@ -106,7 +108,21 @@ pub fn output, HALTREASON: HaltReasonTr>( gas_used, output: output.into_data(), }, - SuccessOrHalt::Halt(reason) => ExecutionResult::Halt { reason, gas_used }, + SuccessOrHalt::Halt(reason) => { + // Bubble up precompile errors from context when available + if matches!( + instruction_result.result, + interpreter::InstructionResult::PrecompileError + ) { + if let Some(message) = context.local_mut().take_precompile_error_context() { + return ExecutionResult::Halt { + reason: HALTREASON::from(HaltReason::PrecompileErrorWithContext(message)), + gas_used, + }; + } + } + ExecutionResult::Halt { reason, gas_used } + } // Only two internal return flags. flag @ (SuccessOrHalt::FatalExternalError | SuccessOrHalt::Internal(_)) => { panic!( diff --git a/crates/handler/src/pre_execution.rs b/crates/handler/src/pre_execution.rs index 0c9bf87b9e..346b79ffc3 100644 --- a/crates/handler/src/pre_execution.rs +++ b/crates/handler/src/pre_execution.rs @@ -13,10 +13,9 @@ use context_interface::{ Block, Cfg, Database, }; use core::cmp::Ordering; -use primitives::StorageKey; -use primitives::{eip7702, hardfork::SpecId, KECCAK_EMPTY, U256}; +use primitives::{eip7702, hardfork::SpecId, U256}; +use primitives::{Address, HashMap, HashSet, StorageKey}; use state::AccountInfo; -use std::boxed::Box; /// Loads and warms accounts for execution, including precompiles and access list. pub fn load_accounts< @@ -54,22 +53,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, @@ -106,73 +121,62 @@ pub fn validate_account_nonce_and_code( Ok(()) } -/// Validates caller state and deducts transaction costs from the caller's balance. +/// Check maximum possible fee and deduct the effective fee. +/// +/// Returns new balance. #[inline] -pub fn validate_against_state_and_deduct_caller< - CTX: ContextTr, - ERROR: From + From<::Error>, ->( - context: &mut CTX, -) -> Result<(), ERROR> { - let basefee = context.block().basefee() as u128; - let blob_price = context.block().blob_gasprice().unwrap_or_default(); - let is_balance_check_disabled = context.cfg().is_balance_check_disabled(); - let is_eip3607_disabled = context.cfg().is_eip3607_disabled(); - let is_nonce_check_disabled = context.cfg().is_nonce_check_disabled(); - - let (tx, journal) = context.tx_journal_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(), - is_eip3607_disabled, - is_nonce_check_disabled, - )?; - - let max_balance_spending = tx.max_balance_spending()?; - - // Check if account has enough balance for `gas_limit * max_fee`` and value transfer. - // Transfer will be done inside `*_inner` functions. - if max_balance_spending > caller_account.info.balance && !is_balance_check_disabled { - return Err(InvalidTransaction::LackOfFundForMaxFee { - fee: Box::new(max_balance_spending), - balance: Box::new(caller_account.info.balance), - } - .into()); +pub fn calculate_caller_fee( + balance: U256, + tx: impl Transaction, + block: impl Block, + cfg: impl Cfg, +) -> Result { + let basefee = block.basefee() as u128; + let blob_price = block.blob_gasprice().unwrap_or_default(); + let is_balance_check_disabled = cfg.is_balance_check_disabled(); + + if !is_balance_check_disabled { + tx.ensure_enough_balance(balance)?; } let effective_balance_spending = tx .effective_balance_spending(basefee, blob_price) .expect("effective balance is always smaller than max balance so it can't overflow"); - // subtracting max balance spending with value that is going to be deducted later in the call. let gas_balance_spending = effective_balance_spending - tx.value(); - let mut new_balance = caller_account - .info - .balance - .saturating_sub(gas_balance_spending); + // new balance + let mut new_balance = balance.saturating_sub(gas_balance_spending); if is_balance_check_disabled { // Make sure the caller's balance is at least the value of the transaction. new_balance = new_balance.max(tx.value()); } - let old_balance = caller_account.info.balance; - // Touch account so we know it is changed. - caller_account.mark_touch(); - caller_account.info.balance = new_balance; + Ok(new_balance) +} - // Bump the nonce for calls. Nonce for CREATE will be bumped in `make_create_frame`. +/// Validates caller state and deducts transaction costs from the caller's balance. +#[inline] +pub fn validate_against_state_and_deduct_caller< + CTX: ContextTr, + ERROR: From + From<::Error>, +>( + context: &mut CTX, +) -> Result<(), ERROR> { + let (block, tx, cfg, journal, _, _) = context.all_mut(); + + // Load caller's account. + let mut caller = journal.load_account_with_code_mut(tx.caller())?.data; + + validate_account_nonce_and_code_with_components(&caller.info, tx, cfg)?; + + let new_balance = calculate_caller_fee(*caller.balance(), tx, block, cfg)?; + + caller.set_balance(new_balance); if tx.kind().is_call() { - // Nonce is already checked - caller_account.info.nonce = caller_account.info.nonce.saturating_add(1); + caller.bump_nonce(); } - - journal.caller_accounting_journal_entry(tx.caller(), old_balance, tx.kind().is_call()); Ok(()) } @@ -214,7 +218,7 @@ 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)?; // 5. Verify the code of `authority` is either empty or already delegated. if let Some(bytecode) = &authority_acc.info.code { @@ -237,20 +241,8 @@ pub fn apply_eip7702_auth_list< // 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 eb75bf6135..b6224c6a4e 100644 --- a/crates/handler/src/precompile_provider.rs +++ b/crates/handler/src/precompile_provider.rs @@ -1,12 +1,12 @@ use auto_impl::auto_impl; use context::{Cfg, LocalContextTr}; -use context_interface::ContextTr; -use interpreter::{CallInput, Gas, InputsImpl, InstructionResult, InterpreterResult}; +use context_interface::{ContextTr, JournalTr}; +use interpreter::{CallInput, CallInputs, Gas, InstructionResult, InterpreterResult}; use precompile::PrecompileError; use precompile::{PrecompileSpecId, Precompiles}; use primitives::{hardfork::SpecId, Address, Bytes}; use std::boxed::Box; -use std::string::String; +use std::string::{String, ToString}; /// Provider for precompiled contracts in the EVM. #[auto_impl(&mut, Box)] @@ -16,17 +16,14 @@ pub trait PrecompileProvider { /// Sets the spec id and returns true if the spec id was changed. Initial call to set_spec will always return true. /// - /// Returned booling will determine if precompile addresses should be injected into the journal. + /// Returns `true` if precompile addresses should be injected into the journal. fn set_spec(&mut self, spec: ::Spec) -> bool; /// Run the precompile. fn run( &mut self, context: &mut CTX, - address: &Address, - inputs: &InputsImpl, - is_static: bool, - gas_limit: u64, + inputs: &CallInputs, ) -> Result, String>; /// Get the warm addresses. @@ -93,35 +90,35 @@ impl PrecompileProvider for EthPrecompiles { fn run( &mut self, context: &mut CTX, - address: &Address, - inputs: &InputsImpl, - _is_static: bool, - gas_limit: u64, + inputs: &CallInputs, ) -> Result, String> { - let Some(precompile) = self.precompiles.get(address) else { + let Some(precompile) = self.precompiles.get(&inputs.bytecode_address) else { return Ok(None); }; let mut result = InterpreterResult { result: InstructionResult::Return, - gas: Gas::new(gas_limit), + gas: Gas::new(inputs.gas_limit), output: Bytes::new(), }; - let r; - let input_bytes = match &inputs.input { - CallInput::SharedBuffer(range) => { - if let Some(slice) = context.local().shared_memory_buffer_slice(range.clone()) { - r = slice; - r.as_ref() - } else { - &[] + let exec_result = { + let r; + let input_bytes = match &inputs.input { + CallInput::SharedBuffer(range) => { + if let Some(slice) = context.local().shared_memory_buffer_slice(range.clone()) { + r = slice; + r.as_ref() + } else { + &[] + } } - } - CallInput::Bytes(bytes) => bytes.0.iter().as_slice(), + CallInput::Bytes(bytes) => bytes.0.iter().as_slice(), + }; + precompile.execute(input_bytes, inputs.gas_limit) }; - match precompile.execute(input_bytes, gas_limit) { + match exec_result { Ok(output) => { let underflow = result.gas.record_cost(output.gas_used); assert!(underflow, "Gas underflow is not possible"); @@ -139,16 +136,24 @@ impl PrecompileProvider for EthPrecompiles { } else { InstructionResult::PrecompileError }; + // If this is a top-level precompile call (depth == 1), persist the error message + // into the local context so it can be returned as output in the final result. + // Only do this for non-OOG errors (OOG is a distinct halt reason without output). + if !e.is_oog() && context.journal().depth() == 1 { + context + .local_mut() + .set_precompile_error_context(e.to_string()); + } } } Ok(Some(result)) } fn warm_addresses(&self) -> Box> { - self.warm_addresses() + Self::warm_addresses(self) } fn contains(&self, address: &Address) -> bool { - self.contains(address) + Self::contains(self, address) } } diff --git a/crates/handler/src/validation.rs b/crates/handler/src/validation.rs index 5962f03716..0f53144edd 100644 --- a/crates/handler/src/validation.rs +++ b/crates/handler/src/validation.rs @@ -20,7 +20,22 @@ pub fn validate_env + From(context, spec).map_err(Into::into) + validate_tx_env::(context, spec).map_err(Into::into) +} + +/// Validate legacy transaction gas price against basefee. +#[inline] +pub fn validate_legacy_gas_price( + gas_price: u128, + base_fee: Option, +) -> Result<(), InvalidTransaction> { + // Gas price must be at least the basefee. + if let Some(base_fee) = base_fee { + if gas_price < base_fee { + return Err(InvalidTransaction::GasPriceLessThanBasefee); + } + } + Ok(()) } /// Validate transaction that has EIP-1559 priority fee @@ -55,7 +70,10 @@ pub fn validate_eip4844_tx( ) -> Result<(), InvalidTransaction> { // Ensure that the user was willing to at least pay the current blob gasprice if block_blob_gas_price > max_blob_fee { - return Err(InvalidTransaction::BlobGasPriceGreaterThanMax); + return Err(InvalidTransaction::BlobGasPriceGreaterThanMax { + block_blob_gas_price, + tx_max_fee_per_blob_gas: max_blob_fee, + }); } // There must be at least one blob @@ -84,7 +102,7 @@ pub fn validate_eip4844_tx( } /// Validate transaction against block and configuration for mainnet. -pub fn validate_tx_env( +pub fn validate_tx_env( context: CTX, spec_id: SpecId, ) -> Result<(), InvalidTransaction> { @@ -126,25 +144,14 @@ pub fn validate_tx_env( match tx_type { TransactionType::Legacy => { - // Gas price must be at least the basefee. - if let Some(base_fee) = base_fee { - if tx.gas_price() < base_fee { - return Err(InvalidTransaction::GasPriceLessThanBasefee); - } - } + validate_legacy_gas_price(tx.gas_price(), base_fee)?; } TransactionType::Eip2930 => { // Enabled in BERLIN hardfork if !spec_id.is_enabled_in(SpecId::BERLIN) { return Err(InvalidTransaction::Eip2930NotSupported); } - - // Gas price must be at least the basefee. - if let Some(base_fee) = base_fee { - if tx.gas_price() < base_fee { - return Err(InvalidTransaction::GasPriceLessThanBasefee); - } - } + validate_legacy_gas_price(tx.gas_price(), base_fee)?; } TransactionType::Eip1559 => { if !spec_id.is_enabled_in(SpecId::LONDON) { @@ -221,12 +228,17 @@ pub fn validate_tx_env( pub fn validate_initial_tx_gas( tx: impl Transaction, spec: SpecId, + is_eip7623_disabled: bool, is_eip7702_enabled: bool, is_eip7623_enabled: bool, ) -> Result { - let gas = + let mut gas = gas::calculate_initial_tx_gas_for_tx(&tx, spec, is_eip7702_enabled, is_eip7623_enabled); + if is_eip7623_disabled { + gas.floor_gas = 0 + } + // Additional check to see if limit is big enough to cover initial gas. if gas.initial_gas > tx.gas_limit() { return Err(InvalidTransaction::CallGasCostMoreThanGasLimit { @@ -250,14 +262,15 @@ pub fn validate_initial_tx_gas( #[cfg(test)] mod tests { - use crate::{ExecuteCommitEvm, MainBuilder, MainContext}; + use crate::{api::ExecuteEvm, ExecuteCommitEvm, MainBuilder, MainContext}; use bytecode::opcode; use context::{ result::{EVMError, ExecutionResult, HaltReason, InvalidTransaction, Output}, - Context, TxEnv, + Context, ContextTr, TxEnv, }; use database::{CacheDB, EmptyDB}; - use primitives::{address, eip3860, eip7907, hardfork::SpecId, Bytes, TxKind}; + use primitives::{address, eip3860, eip7907, hardfork::SpecId, Bytes, TxKind, B256}; + use state::{AccountInfo, Bytecode}; fn deploy_contract( bytecode: Bytes, @@ -550,4 +563,138 @@ mod tests { _ => panic!("execution result is not Success"), } } + + #[test] + fn test_transact_many_with_transaction_index_error() { + use context::result::TransactionIndexedError; + + let ctx = Context::mainnet().with_db(CacheDB::::default()); + let mut evm = ctx.build_mainnet(); + + // Create a transaction that will fail (invalid gas limit) + let invalid_tx = TxEnv::builder() + .gas_limit(0) // This will cause a validation error + .build() + .unwrap(); + + // Create a valid transaction + let valid_tx = TxEnv::builder().gas_limit(100000).build().unwrap(); + + // Test that the first transaction fails with index 0 + let result = evm.transact_many([invalid_tx.clone()].into_iter()); + assert!(matches!( + result, + Err(TransactionIndexedError { + transaction_index: 0, + .. + }) + )); + + // Test that the second transaction fails with index 1 + let result = evm.transact_many([valid_tx, invalid_tx].into_iter()); + assert!(matches!( + result, + Err(TransactionIndexedError { + transaction_index: 1, + .. + }) + )); + } + + #[test] + fn test_transact_many_success() { + use primitives::{address, U256}; + + let ctx = Context::mainnet().with_db(CacheDB::::default()); + let mut evm = ctx.build_mainnet(); + + // Add balance to the caller account + let caller = address!("0x0000000000000000000000000000000000000001"); + evm.db_mut().insert_account_info( + caller, + AccountInfo::new( + U256::from(1000000000000000000u64), + 0, + B256::ZERO, + Bytecode::new(), + ), + ); + + // Create valid transactions with proper data + let tx1 = TxEnv::builder() + .caller(caller) + .gas_limit(100000) + .gas_price(20_000_000_000u128) + .nonce(0) + .build() + .unwrap(); + + let tx2 = TxEnv::builder() + .caller(caller) + .gas_limit(100000) + .gas_price(20_000_000_000u128) + .nonce(1) + .build() + .unwrap(); + + // Test that all transactions succeed + let result = evm.transact_many([tx1, tx2].into_iter()); + if let Err(e) = &result { + println!("Error: {e:?}"); + } + let outputs = result.expect("All transactions should succeed"); + assert_eq!(outputs.len(), 2); + } + + #[test] + fn test_transact_many_finalize_with_error() { + use context::result::TransactionIndexedError; + + let ctx = Context::mainnet().with_db(CacheDB::::default()); + let mut evm = ctx.build_mainnet(); + + // Create transactions where the second one fails + let valid_tx = TxEnv::builder().gas_limit(100000).build().unwrap(); + + let invalid_tx = TxEnv::builder() + .gas_limit(0) // This will cause a validation error + .build() + .unwrap(); + + // Test that transact_many_finalize returns the error with correct index + let result = evm.transact_many_finalize([valid_tx, invalid_tx].into_iter()); + assert!(matches!( + result, + Err(TransactionIndexedError { + transaction_index: 1, + .. + }) + )); + } + + #[test] + fn test_transact_many_commit_with_error() { + use context::result::TransactionIndexedError; + + let ctx = Context::mainnet().with_db(CacheDB::::default()); + let mut evm = ctx.build_mainnet(); + + // Create transactions where the first one fails + let invalid_tx = TxEnv::builder() + .gas_limit(0) // This will cause a validation error + .build() + .unwrap(); + + let valid_tx = TxEnv::builder().gas_limit(100000).build().unwrap(); + + // Test that transact_many_commit returns the error with correct index + let result = evm.transact_many_commit([invalid_tx, valid_tx].into_iter()); + assert!(matches!( + result, + Err(TransactionIndexedError { + transaction_index: 0, + .. + }) + )); + } } diff --git a/crates/inspector/CHANGELOG.md b/crates/inspector/CHANGELOG.md index 75541a53db..6a5b8b5263 100644 --- a/crates/inspector/CHANGELOG.md +++ b/crates/inspector/CHANGELOG.md @@ -7,6 +7,64 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [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 + +- updated the following local packages: revm-primitives, revm-context, revm-interpreter, revm-handler, revm-state, revm-database-interface, revm-database + +## [11.1.0](https://github.com/bluealloy/revm/compare/revm-inspector-v11.0.0...revm-inspector-v11.1.0) - 2025-10-09 + +### Other + +- use Self::default in fn new for CountInspector ([#3065](https://github.com/bluealloy/revm/pull/3065)) + +## [11.0.0](https://github.com/bluealloy/revm/compare/revm-inspector-v10.0.1...revm-inspector-v11.0.0) - 2025-10-07 + +### Added + +- detach EthFrame from InspectorEvmTr ([#3038](https://github.com/bluealloy/revm/pull/3038)) +- *(revme)* ef blockchain tests cli ([#2935](https://github.com/bluealloy/revm/pull/2935)) + +### Fixed + +- *(inspector)* remove redundant pc()/opcode() calls in step_end ([#3023](https://github.com/bluealloy/revm/pull/3023)) +- Apply spelling corrections from PRs #2926, #2915, #2908 ([#2978](https://github.com/bluealloy/revm/pull/2978)) + +### Other + +- changelog update for v87 ([#3056](https://github.com/bluealloy/revm/pull/3056)) +- add boundless ([#3043](https://github.com/bluealloy/revm/pull/3043)) +- EvmTr and InspectorEvmTr receive all/all_mut fn ([#3037](https://github.com/bluealloy/revm/pull/3037)) +- *(inspector)* remove unnecessary mutable borrow in GasInspector::step_end ([#3035](https://github.com/bluealloy/revm/pull/3035)) +- add SECURITY.md ([#2956](https://github.com/bluealloy/revm/pull/2956)) +- *(inspector)* remove dead skip flag ([#2951](https://github.com/bluealloy/revm/pull/2951)) + ## [10.0.1](https://github.com/bluealloy/revm/compare/revm-inspector-v10.0.0...revm-inspector-v10.0.1) - 2025-09-23 ### Other diff --git a/crates/inspector/Cargo.toml b/crates/inspector/Cargo.toml index 63c5334bf7..ac7a1bef4a 100644 --- a/crates/inspector/Cargo.toml +++ b/crates/inspector/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "revm-inspector" description = "Revm inspector interface" -version = "10.0.1" +version = "12.0.1" 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/src/count_inspector.rs b/crates/inspector/src/count_inspector.rs index a80d09af62..37a99832c5 100644 --- a/crates/inspector/src/count_inspector.rs +++ b/crates/inspector/src/count_inspector.rs @@ -31,18 +31,7 @@ pub struct CountInspector { impl CountInspector { /// Create a new CountInspector. pub fn new() -> Self { - Self { - opcode_counts: HashMap::default(), - 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, - } + Self::default() } /// Get the count for a specific opcode. diff --git a/crates/inspector/src/eip3155.rs b/crates/inspector/src/eip3155.rs index a81ebfbc9b..841e12eede 100644 --- a/crates/inspector/src/eip3155.rs +++ b/crates/inspector/src/eip3155.rs @@ -23,7 +23,6 @@ pub struct TracerEip3155 { gas: u64, refunded: i64, mem_size: usize, - skip: bool, include_memory: bool, memory: Option, } @@ -39,7 +38,6 @@ impl std::fmt::Debug for TracerEip3155 { .field("gas", &self.gas) .field("refunded", &self.refunded) .field("mem_size", &self.mem_size) - .field("skip", &self.skip) .field("include_memory", &self.include_memory) .field("memory", &self.memory) .finish() @@ -54,6 +52,11 @@ struct Output<'a> { // Required fields: /// Program counter pc: u64, + /// Depth of the call stack + depth: u64, + /// Name of the operation + #[serde(default, skip_serializing_if = "Option::is_none")] + op_name: Option<&'static str>, /// OpCode op: u8, /// Gas left before executing this operation @@ -64,8 +67,6 @@ struct Output<'a> { gas_cost: u64, /// Array of all values on the stack stack: &'a [U256], - /// Depth of the call stack - depth: u64, /// Data returned by the function call return_data: &'static str, /// Amount of **global** gas refunded @@ -76,9 +77,6 @@ struct Output<'a> { mem_size: u64, // Optional fields: - /// Name of the operation - #[serde(default, skip_serializing_if = "Option::is_none")] - op_name: Option<&'static str>, /// Description of an error (should contain revert reason if supported) #[serde(default, skip_serializing_if = "Option::is_none")] error: Option, @@ -143,7 +141,6 @@ impl TracerEip3155 { gas: 0, refunded: 0, mem_size: 0, - skip: false, } } @@ -176,7 +173,6 @@ impl TracerEip3155 { gas, refunded, mem_size, - skip, .. } = self; *gas_inspector = GasInspector::new(); @@ -186,7 +182,6 @@ impl TracerEip3155 { *gas = 0; *refunded = 0; *mem_size = 0; - *skip = false; } fn print_summary(&mut self, result: &InterpreterResult, context: &mut impl ContextTr) { @@ -249,12 +244,7 @@ where } fn step_end(&mut self, interp: &mut Interpreter, context: &mut CTX) { - self.gas_inspector.step_end(&mut interp.gas); - if self.skip { - self.skip = false; - return; - } - + self.gas_inspector.step_end(&interp.gas); let value = Output { pc: self.pc, op: self.opcode, diff --git a/crates/inspector/src/either.rs b/crates/inspector/src/either.rs index 7b4da7249b..239d76bb38 100644 --- a/crates/inspector/src/either.rs +++ b/crates/inspector/src/either.rs @@ -101,8 +101,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/gas.rs b/crates/inspector/src/gas.rs index db286d4ca1..87b2d045ea 100644 --- a/crates/inspector/src/gas.rs +++ b/crates/inspector/src/gas.rs @@ -50,7 +50,7 @@ impl GasInspector { /// calculate last gas cost and remaining gas. #[inline] - pub fn step_end(&mut self, gas: &mut Gas) { + pub fn step_end(&mut self, gas: &Gas) { let remaining = gas.remaining(); self.last_gas_cost = self.gas_remaining.saturating_sub(remaining); self.gas_remaining = remaining; @@ -109,9 +109,7 @@ mod tests { } fn step_end(&mut self, interp: &mut Interpreter, _context: &mut CTX) { - interp.bytecode.pc(); - interp.bytecode.opcode(); - self.gas_inspector.step_end(&mut interp.gas); + self.gas_inspector.step_end(&interp.gas); self.gas_remaining_steps .push((self.pc, self.gas_inspector.gas_remaining())); } diff --git a/crates/inspector/src/mainnet_inspect.rs b/crates/inspector/src/mainnet_inspect.rs index 6b78bf7440..a14ea75c55 100644 --- a/crates/inspector/src/mainnet_inspect.rs +++ b/crates/inspector/src/mainnet_inspect.rs @@ -2,7 +2,7 @@ use crate::{ inspect::{InspectCommitEvm, InspectEvm, InspectSystemCallEvm}, Inspector, InspectorEvmTr, InspectorHandler, JournalExt, }; -use context::{ContextSetters, ContextTr, Evm, JournalTr}; +use context::{ContextSetters, ContextTr, Evm, FrameStack, JournalTr}; use database_interface::DatabaseCommit; use handler::{ instructions::InstructionProvider, system_call::SystemCallTx, EthFrame, EvmTr, EvmTrError, @@ -95,33 +95,36 @@ where { type Inspector = INSP; - fn inspector(&mut self) -> &mut Self::Inspector { - &mut self.inspector - } - - fn ctx_inspector(&mut self) -> (&mut Self::Context, &mut Self::Inspector) { - (&mut self.ctx, &mut self.inspector) - } - - fn ctx_inspector_frame( - &mut self, - ) -> (&mut Self::Context, &mut Self::Inspector, &mut Self::Frame) { - (&mut self.ctx, &mut self.inspector, self.frame_stack.get()) + fn all_inspector( + &self, + ) -> ( + &Self::Context, + &Self::Instructions, + &Self::Precompiles, + &FrameStack, + &Self::Inspector, + ) { + let ctx = &self.ctx; + let frame = &self.frame_stack; + let instructions = &self.instruction; + let precompiles = &self.precompiles; + let inspector = &self.inspector; + (ctx, instructions, precompiles, frame, inspector) } - - fn ctx_inspector_frame_instructions( + fn all_mut_inspector( &mut self, ) -> ( &mut Self::Context, - &mut Self::Inspector, - &mut Self::Frame, &mut Self::Instructions, + &mut Self::Precompiles, + &mut FrameStack, + &mut Self::Inspector, ) { - ( - &mut self.ctx, - &mut self.inspector, - self.frame_stack.get(), - &mut self.instruction, - ) + let ctx = &mut self.ctx; + let frame = &mut self.frame_stack; + let instructions = &mut self.instruction; + let precompiles = &mut self.precompiles; + let inspector = &mut self.inspector; + (ctx, instructions, precompiles, frame, inspector) } } diff --git a/crates/inspector/src/traits.rs b/crates/inspector/src/traits.rs index 9fc098f377..bcd395acfc 100644 --- a/crates/inspector/src/traits.rs +++ b/crates/inspector/src/traits.rs @@ -1,10 +1,10 @@ -use context::ContextTr; +use context::{ContextTr, FrameStack}; use handler::{ evm::{ContextDbError, FrameInitResult, FrameTr}, instructions::InstructionProvider, - EthFrame, EvmTr, FrameInitOrResult, ItemOrResult, + EthFrame, EvmTr, FrameInitOrResult, FrameResult, ItemOrResult, }; -use interpreter::{interpreter::EthInterpreter, FrameInput, Interpreter, InterpreterTypes}; +use interpreter::{interpreter::EthInterpreter, interpreter_action::FrameInit, InterpreterTypes}; use crate::{ handler::{frame_end, frame_start}, @@ -18,7 +18,7 @@ use crate::{ /// It is used inside [`crate::InspectorHandler`] to extend evm with support for inspection. pub trait InspectorEvmTr: EvmTr< - Frame = EthFrame, + Frame: InspectorFrame, Instructions: InstructionProvider, Context: ContextTr, > @@ -26,20 +26,57 @@ pub trait InspectorEvmTr: /// The inspector type used for EVM execution inspection. type Inspector: Inspector; + /// Returns a tuple of mutable references to the context, the inspector, the frame and the instructions. + /// + /// This is one of two functions that need to be implemented for Evm. Second one is `all_mut`. + #[allow(clippy::type_complexity)] + fn all_inspector( + &self, + ) -> ( + &Self::Context, + &Self::Instructions, + &Self::Precompiles, + &FrameStack, + &Self::Inspector, + ); + + /// Returns a tuple of mutable references to the context, the inspector, the frame and the instructions. + /// + /// This is one of two functions that need to be implemented for Evm. Second one is `all`. + #[allow(clippy::type_complexity)] + fn all_mut_inspector( + &mut self, + ) -> ( + &mut Self::Context, + &mut Self::Instructions, + &mut Self::Precompiles, + &mut FrameStack, + &mut Self::Inspector, + ); + /// Returns a mutable reference to the inspector. - fn inspector(&mut self) -> &mut Self::Inspector; + fn inspector(&mut self) -> &mut Self::Inspector { + let (_, _, _, _, inspector) = self.all_mut_inspector(); + inspector + } /// Returns a tuple of mutable references to the context and the inspector. /// /// Useful when you want to allow inspector to modify the context. - fn ctx_inspector(&mut self) -> (&mut Self::Context, &mut Self::Inspector); + fn ctx_inspector(&mut self) -> (&mut Self::Context, &mut Self::Inspector) { + let (ctx, _, _, _, inspector) = self.all_mut_inspector(); + (ctx, inspector) + } /// Returns a tuple of mutable references to the context, the inspector and the frame. /// /// Useful when you want to allow inspector to modify the context and the frame. fn ctx_inspector_frame( &mut self, - ) -> (&mut Self::Context, &mut Self::Inspector, &mut Self::Frame); + ) -> (&mut Self::Context, &mut Self::Inspector, &mut Self::Frame) { + let (ctx, _, _, frame, inspector) = self.all_mut_inspector(); + (ctx, inspector, frame.get()) + } /// Returns a tuple of mutable references to the context, the inspector, the frame and the instructions. fn ctx_inspector_frame_instructions( @@ -49,7 +86,10 @@ pub trait InspectorEvmTr: &mut Self::Inspector, &mut Self::Frame, &mut Self::Instructions, - ); + ) { + let (ctx, instructions, _, frame, inspector) = self.all_mut_inspector(); + (ctx, inspector, frame.get(), instructions) + } /// Initializes the frame for the given frame input. Frame is pushed to the frame stack. #[inline] @@ -72,8 +112,10 @@ pub trait InspectorEvmTr: // if it is new frame, initialize the interpreter. let (ctx, inspector, frame) = self.ctx_inspector_frame(); - let interp = frame.interpreter(); - inspector.initialize_interp(interp, ctx); + if let Some(frame) = frame.eth_frame() { + let interp = &mut frame.interpreter; + inspector.initialize_interp(interp, ctx); + }; Ok(ItemOrResult::Item(frame)) } @@ -86,9 +128,13 @@ pub trait InspectorEvmTr: ) -> Result, ContextDbError> { let (ctx, inspector, frame, instructions) = self.ctx_inspector_frame_instructions(); + let Some(frame) = frame.eth_frame() else { + return self.frame_run(); + }; + let next_action = inspect_instructions( ctx, - frame.interpreter(), + &mut frame.interpreter, inspector, instructions.instruction_table(), ); @@ -96,34 +142,33 @@ pub trait InspectorEvmTr: if let Ok(ItemOrResult::Result(frame_result)) = &mut result { let (ctx, inspector, frame) = self.ctx_inspector_frame(); - frame_end(ctx, inspector, frame.frame_input(), frame_result); - frame.set_finished(true); + // TODO When all_mut fn is added we can fetch inspector at the top of the function.s + if let Some(frame) = frame.eth_frame() { + frame_end(ctx, inspector, &frame.input, frame_result); + frame.set_finished(true); + } }; result } } /// Trait that extends the [`FrameTr`] trait with additional functionality that is needed for inspection. -pub trait InspectorFrame: FrameTr { +pub trait InspectorFrame: FrameTr { /// The interpreter types used by this frame. type IT: InterpreterTypes; - /// Returns a mutable reference to the interpreter. - fn interpreter(&mut self) -> &mut Interpreter; - - /// Returns a reference to the frame input. Frame input is needed for call/create/eofcreate [`crate::Inspector`] methods - fn frame_input(&self) -> &FrameInput; + /// Returns a mutable reference to the EthFrame. + /// + /// If this frame does not have support for tracing (does not contain + /// the EthFrame) Inspector calls for this frame will be skipped. + fn eth_frame(&mut self) -> Option<&mut EthFrame>; } /// Impl InspectorFrame for EthFrame. impl InspectorFrame for EthFrame { type IT = EthInterpreter; - fn interpreter(&mut self) -> &mut Interpreter { - &mut self.interpreter - } - - fn frame_input(&self) -> &FrameInput { - &self.input + fn eth_frame(&mut self) -> Option<&mut EthFrame> { + Some(self) } } diff --git a/crates/interpreter/CHANGELOG.md b/crates/interpreter/CHANGELOG.md index 371f22484e..098648fc89 100644 --- a/crates/interpreter/CHANGELOG.md +++ b/crates/interpreter/CHANGELOG.md @@ -7,6 +7,88 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [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 + +- Optional Bytecode in CallInput ([#3110](https://github.com/bluealloy/revm/pull/3110)) + +### Fixed + +- return MemoryLimitOOG when memory_limit is enabled ([#3109](https://github.com/bluealloy/revm/pull/3109)) + +### Other + +- *(instructions)* fix typo in spurious_dragon variable ([#3106](https://github.com/bluealloy/revm/pull/3106)) + +## [27.0.2](https://github.com/bluealloy/revm/compare/revm-interpreter-v27.0.1...revm-interpreter-v27.0.2) - 2025-10-15 + +### 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 + +### Fixed + +- support legacy JumpTable serde format ([#3098](https://github.com/bluealloy/revm/pull/3098)) + +### Other + +- make CallInput::bytes accept immutable ContextTr ([#3082](https://github.com/bluealloy/revm/pull/3082)) + +## [27.0.0](https://github.com/bluealloy/revm/compare/revm-interpreter-v26.0.0...revm-interpreter-v27.0.0) - 2025-10-09 + +### Other + +- remove deprecated methods ([#3050](https://github.com/bluealloy/revm/pull/3050)) + +## [26.0.0](https://github.com/bluealloy/revm/compare/revm-interpreter-v25.0.3...revm-interpreter-v26.0.0) - 2025-10-07 + +### Added + +- Support bubbling up first precompile error messages ([#2905](https://github.com/bluealloy/revm/pull/2905)) +- send bytecode with call input ([#2963](https://github.com/bluealloy/revm/pull/2963)) + +### Fixed + +- remove redundant U256::from on Host getters in instructions ([#3053](https://github.com/bluealloy/revm/pull/3053)) +- *(interpreter)* remove redundant stack underflow check in LOG instruction ([#3028](https://github.com/bluealloy/revm/pull/3028)) +- unsafe stack capacity invariant and serde deserialization assumptions ([#3025](https://github.com/bluealloy/revm/pull/3025)) +- Apply spelling corrections from PRs #2926, #2915, #2908 ([#2978](https://github.com/bluealloy/revm/pull/2978)) +- skip cold load on oog ([#2903](https://github.com/bluealloy/revm/pull/2903)) + +### Other + +- changelog update for v87 ([#3056](https://github.com/bluealloy/revm/pull/3056)) +- add boundless ([#3043](https://github.com/bluealloy/revm/pull/3043)) +- expose stack data ([#3047](https://github.com/bluealloy/revm/pull/3047)) +- use offset_from_unsigned ([#2999](https://github.com/bluealloy/revm/pull/2999)) +- rm eof comments ([#2987](https://github.com/bluealloy/revm/pull/2987)) +- comments on EIP-2929/2930 constants ([#2969](https://github.com/bluealloy/revm/pull/2969)) +- add SECURITY.md ([#2956](https://github.com/bluealloy/revm/pull/2956)) +- remove duplicate comment for TOTAL_COST_FLOOR_PER_TOKEN constant ([#2950](https://github.com/bluealloy/revm/pull/2950)) +- clean static_selfdestruct_cost ([#2944](https://github.com/bluealloy/revm/pull/2944)) +- rename SELFDESTRUCT to SELFDESTRUCT_REFUND ([#2937](https://github.com/bluealloy/revm/pull/2937)) + +## [25.0.3](https://github.com/bluealloy/revm/compare/revm-interpreter-v25.0.2...revm-interpreter-v25.0.3) - 2025-09-23 + +### Other + +- 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 @@ -49,14 +131,6 @@ 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). -## [Unreleased] - -## [25.0.3](https://github.com/bluealloy/revm/compare/revm-interpreter-v25.0.2...revm-interpreter-v25.0.3) - 2025-09-23 - -### Other - -- updated the following local packages: revm-context-interface - ## [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 2bef431c47..ff34e2a734 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 = "25.0.3" +version = "29.0.1" authors.workspace = true edition.workspace = true keywords.workspace = true @@ -22,23 +22,31 @@ workspace = true bytecode.workspace = true primitives.workspace = true context-interface.workspace = true +state.workspace = true # optional serde = { workspace = true, features = ["derive", "rc"], optional = true } [dev-dependencies] -bincode.workspace = true +serde_json = { workspace = true, features = ["alloc"] } [features] default = ["std"] -std = ["serde?/std", "primitives/std", "context-interface/std", "bytecode/std"] +std = [ + "serde?/std", + "primitives/std", + "context-interface/std", + "bytecode/std", + "state/std", + "serde_json/std", +] hashbrown = ["primitives/hashbrown"] serde = [ "dep:serde", "primitives/serde", "bytecode/serde", "context-interface/serde", - "bincode/serde", + "state/serde", ] arbitrary = ["std", "primitives/arbitrary"] # TODO : Should be set from Context or from crate that consumes this PR. diff --git a/crates/interpreter/src/gas/calc.rs b/crates/interpreter/src/gas/calc.rs index 77b014bcd3..52f4f41b52 100644 --- a/crates/interpreter/src/gas/calc.rs +++ b/crates/interpreter/src/gas/calc.rs @@ -181,9 +181,45 @@ pub const fn sload_cost(spec_id: SpecId, is_cold: bool) -> u64 { } } +/// 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 fn sstore_cost(spec_id: SpecId, vals: &SStoreResult, is_cold: bool) -> u64 { +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); @@ -203,7 +239,7 @@ pub fn sstore_cost(spec_id: SpecId, vals: &SStoreResult, is_cold: bool) -> u64 { /// EIP-2200: Structured Definitions for Net Gas Metering #[inline] -fn istanbul_sstore_cost( +const fn istanbul_sstore_cost( vals: &SStoreResult, ) -> u64 { if vals.is_new_eq_present() { @@ -219,7 +255,7 @@ fn istanbul_sstore_cost( /// Frontier sstore cost just had two cases set and reset values. #[inline] -fn frontier_sstore_cost(vals: &SStoreResult) -> u64 { +const fn frontier_sstore_cost(vals: &SStoreResult) -> u64 { if vals.is_present_zero() && !vals.is_new_zero() { SSTORE_SET } else { @@ -227,9 +263,23 @@ fn frontier_sstore_cost(vals: &SStoreResult) -> u64 { } } +/// 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 selfdestruct_cost(spec_id: SpecId, res: StateLoad) -> u64 { +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 @@ -238,51 +288,32 @@ 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 /// -/// While [`bytecode::opcode::STATICCALL`], [`bytecode::opcode::DELEGATECALL`], -/// [`bytecode::opcode::CALLCODE`] need to have this field hardcoded to false -/// as they were present before SPURIOUS_DRAGON hardfork. +/// 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 const fn call_cost( - spec_id: SpecId, - transfers_value: bool, - account_load: StateLoad, -) -> u64 { - let is_empty = account_load.data.is_empty; +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_cold_cost_with_delegation(account_load) + WARM_STORAGE_READ_COST } else if spec_id.is_enabled_in(SpecId::TANGERINE) { // EIP-150: Gas cost changes for IO-heavy operations 700 @@ -291,23 +322,10 @@ pub const fn call_cost( }; // Transfer value cost - if transfers_value { + if has_transfer { gas += CALLVALUE; } - // New account cost - if is_empty { - // EIP-161: State trie clearing (invariant-preserving alternative) - if spec_id.is_enabled_in(SpecId::SPURIOUS_DRAGON) { - // Account only if there is value transferred. - if transfers_value { - gas += NEWACCOUNT; - } - } else { - gas += NEWACCOUNT; - } - } - gas } @@ -386,12 +404,6 @@ pub fn calculate_initial_tx_gas( // Initdate stipend let tokens_in_calldata = get_tokens_in_calldata(input, spec_id.is_enabled_in(SpecId::ISTANBUL)); - // TODO(EOF) Tx type is removed - // initcode stipend - // for initcode in initcodes { - // tokens_in_calldata += get_tokens_in_calldata(initcode.as_ref(), true); - // } - gas.initial_gas += tokens_in_calldata * STANDARD_TOKEN_COST; // Get number of access list account and storages. @@ -459,14 +471,6 @@ pub fn calculate_initial_tx_gas_for_tx( .unwrap_or_default(); } - // Access initcodes only if tx is Eip7873. - // TODO(EOF) Tx type is removed - // let initcodes = if tx.tx_type() == TransactionType::Eip7873 { - // tx.initcodes() - // } else { - // &[] - // }; - calculate_initial_tx_gas( spec, tx.input(), @@ -476,7 +480,6 @@ pub fn calculate_initial_tx_gas_for_tx( accounts as u64, storages as u64, tx.authorization_list_len() as u64, - //initcodes, ) } diff --git a/crates/interpreter/src/gas/constants.rs b/crates/interpreter/src/gas/constants.rs index 057d2d899f..1739e8ca08 100644 --- a/crates/interpreter/src/gas/constants.rs +++ b/crates/interpreter/src/gas/constants.rs @@ -23,8 +23,8 @@ pub const MID: u64 = 8; pub const HIGH: u64 = 10; /// Gas cost for JUMPDEST instruction. pub const JUMPDEST: u64 = 1; -/// Gas cost for SELFDESTRUCT instruction. -pub const SELFDESTRUCT: i64 = 24000; +/// Gas cost for REFUND SELFDESTRUCT instruction. +pub const SELFDESTRUCT_REFUND: i64 = 24000; /// Gas cost for CREATE instruction. pub const CREATE: u64 = 32000; /// Additional gas cost when a call transfers value. @@ -72,22 +72,26 @@ pub const NON_ZERO_BYTE_DATA_COST_ISTANBUL: u64 = 16; /// The multiplier for a non zero byte in calldata adjusted by [EIP-2028](https://eips.ethereum.org/EIPS/eip-2028). pub const NON_ZERO_BYTE_MULTIPLIER_ISTANBUL: u64 = NON_ZERO_BYTE_DATA_COST_ISTANBUL / STANDARD_TOKEN_COST; -// The cost floor per token as defined by [EIP-2028](https://eips.ethereum.org/EIPS/eip-2028). /// The cost floor per token as defined by EIP-2028. pub const TOTAL_COST_FLOOR_PER_TOKEN: u64 = 10; /// Gas cost for EOF CREATE instruction. pub const EOF_CREATE_GAS: u64 = 32000; -// Berlin eip2929 constants -/// Gas cost for accessing an address in the access list (EIP-2929). +// Berlin EIP-2929/EIP-2930 constants +/// Gas cost for accessing an address in the access list (EIP-2930). pub const ACCESS_LIST_ADDRESS: u64 = 2400; -/// Gas cost for accessing a storage key in the access list (EIP-2929). +/// 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). 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. diff --git a/crates/interpreter/src/instruction_result.rs b/crates/interpreter/src/instruction_result.rs index c6d8f3cada..74b295e64d 100644 --- a/crates/interpreter/src/instruction_result.rs +++ b/crates/interpreter/src/instruction_result.rs @@ -123,6 +123,7 @@ impl From for InstructionResult { HaltReason::OutOfOffset => Self::OutOfOffset, HaltReason::CreateCollision => Self::CreateCollision, HaltReason::PrecompileError => Self::PrecompileError, + HaltReason::PrecompileErrorWithContext(_) => Self::PrecompileError, HaltReason::NonceOverflow => Self::NonceOverflow, HaltReason::CreateContractSizeLimit => Self::CreateContractSizeLimit, HaltReason::CreateContractStartingWithEF => Self::CreateContractStartingWithEF, diff --git a/crates/interpreter/src/instructions/block_info.rs b/crates/interpreter/src/instructions/block_info.rs index 0dff4a6ad1..fa2c118c7a 100644 --- a/crates/interpreter/src/instructions/block_info.rs +++ b/crates/interpreter/src/instructions/block_info.rs @@ -2,7 +2,7 @@ use crate::{ interpreter_types::{InterpreterTypes, RuntimeFlag, StackTr}, Host, }; -use primitives::{hardfork::SpecId::*, U256}; +use primitives::hardfork::SpecId::*; use crate::InstructionContext; @@ -43,7 +43,7 @@ pub fn block_number( context: InstructionContext<'_, H, WIRE>, ) { //gas!(context.interpreter, gas::BASE); - push!(context.interpreter, U256::from(context.host.block_number())); + push!(context.interpreter, context.host.block_number()); } /// Implements the DIFFICULTY/PREVRANDAO instruction. diff --git a/crates/interpreter/src/instructions/contract.rs b/crates/interpreter/src/instructions/contract.rs index 731fb1e50b..22a36f6629 100644 --- a/crates/interpreter/src/instructions/contract.rs +++ b/crates/interpreter/src/instructions/contract.rs @@ -1,6 +1,9 @@ mod call_helpers; -pub use call_helpers::{calc_call_gas, get_memory_input_and_out_ranges, resize_memory}; +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, +}; use crate::{ gas, @@ -105,48 +108,31 @@ pub fn create( /// Implements the CALL instruction. /// /// Message call with value transfer to another account. -pub fn call(context: InstructionContext<'_, H, WIRE>) { +pub fn call( + mut context: InstructionContext<'_, H, WIRE>, +) { popn!([local_gas_limit, to, value], context.interpreter); let to = to.into_address(); // 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 has_transfer = !value.is_zero(); + if context.interpreter.runtime_flag.is_static() && has_transfer { context .interpreter .halt(InstructionResult::CallNotAllowedInsideStatic); return; } - let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(context.interpreter) else { return; }; - - let Some(account_load) = context.host.load_account_delegated(to) else { - context - .interpreter - .halt(InstructionResult::FatalExternalError); - return; - }; - - let Some(mut gas_limit) = calc_call_gas( - context.interpreter, - account_load, - has_transfer, - local_gas_limit, - ) else { + let Some((gas_limit, bytecode, bytecode_hash)) = + load_acc_and_calc_gas(&mut context, to, has_transfer, true, local_gas_limit) + else { return; }; - gas!(context.interpreter, gas_limit); - - // Add call stipend if there is value to be transferred. - if has_transfer { - gas_limit = gas_limit.saturating_add(gas::CALL_STIPEND); - } - // Call host to interact with target contract context .interpreter @@ -158,6 +144,7 @@ pub fn call(context: InstructionContex target_address: to, caller: context.interpreter.input.target_address(), bytecode_address: to, + known_bytecode: Some((bytecode_hash, bytecode)), value: CallValue::Transfer(value), scheme: CallScheme::Call, is_static: context.interpreter.runtime_flag.is_static(), @@ -170,41 +157,25 @@ pub fn call(context: InstructionContex /// /// Message call with alternative account's code. pub fn call_code( - context: InstructionContext<'_, H, WIRE>, + mut context: InstructionContext<'_, H, WIRE>, ) { popn!([local_gas_limit, to, value], context.interpreter); let to = Address::from_word(B256::from(to)); // 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 has_transfer = !value.is_zero(); - //pop!(context.interpreter, value); let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(context.interpreter) else { return; }; - let Some(mut load) = context.host.load_account_delegated(to) else { - context - .interpreter - .halt(InstructionResult::FatalExternalError); - return; - }; - - // Set `is_empty` to false as we are not creating this account. - load.is_empty = false; - let Some(mut gas_limit) = - calc_call_gas(context.interpreter, load, !value.is_zero(), local_gas_limit) + let Some((gas_limit, bytecode, bytecode_hash)) = + load_acc_and_calc_gas(&mut context, to, has_transfer, false, local_gas_limit) else { return; }; - gas!(context.interpreter, gas_limit); - - // Add call stipend if there is value to be transferred. - if !value.is_zero() { - gas_limit = gas_limit.saturating_add(gas::CALL_STIPEND); - } - // Call host to interact with target contract context .interpreter @@ -216,6 +187,7 @@ pub fn call_code( target_address: context.interpreter.input.target_address(), caller: context.interpreter.input.target_address(), bytecode_address: to, + known_bytecode: Some((bytecode_hash, bytecode)), value: CallValue::Transfer(value), scheme: CallScheme::CallCode, is_static: context.interpreter.runtime_flag.is_static(), @@ -228,7 +200,7 @@ pub fn call_code( /// /// Message call with alternative account's code but same sender and value. pub fn delegate_call( - context: InstructionContext<'_, H, WIRE>, + mut context: InstructionContext<'_, H, WIRE>, ) { check!(context.interpreter, HOMESTEAD); popn!([local_gas_limit, to], context.interpreter); @@ -241,21 +213,12 @@ pub fn delegate_call( return; }; - let Some(mut load) = context.host.load_account_delegated(to) else { - context - .interpreter - .halt(InstructionResult::FatalExternalError); - return; - }; - - // Set is_empty to false as we are not creating this account. - load.is_empty = false; - let Some(gas_limit) = calc_call_gas(context.interpreter, load, false, local_gas_limit) else { + let Some((gas_limit, bytecode, bytecode_hash)) = + load_acc_and_calc_gas(&mut context, to, false, false, local_gas_limit) + else { return; }; - gas!(context.interpreter, gas_limit); - // Call host to interact with target contract context .interpreter @@ -267,6 +230,7 @@ pub fn delegate_call( target_address: context.interpreter.input.target_address(), caller: context.interpreter.input.caller_address(), bytecode_address: to, + 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(), @@ -279,7 +243,7 @@ pub fn delegate_call( /// /// Static message call (cannot modify state). pub fn static_call( - context: InstructionContext<'_, H, WIRE>, + mut context: InstructionContext<'_, H, WIRE>, ) { check!(context.interpreter, BYZANTIUM); popn!([local_gas_limit, to], context.interpreter); @@ -292,18 +256,11 @@ pub fn static_call( return; }; - let Some(mut load) = context.host.load_account_delegated(to) else { - context - .interpreter - .halt(InstructionResult::FatalExternalError); - return; - }; - // Set `is_empty` to false as we are not creating this account. - load.is_empty = false; - let Some(gas_limit) = calc_call_gas(context.interpreter, load, false, local_gas_limit) else { + let Some((gas_limit, bytecode, bytecode_hash)) = + load_acc_and_calc_gas(&mut context, to, false, false, local_gas_limit) + else { return; }; - gas!(context.interpreter, gas_limit); // Call host to interact with target contract context @@ -316,6 +273,7 @@ pub fn static_call( target_address: to, caller: context.interpreter.input.target_address(), bytecode_address: to, + 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 37cf571466..88163bea09 100644 --- a/crates/interpreter/src/instructions/contract/call_helpers.rs +++ b/crates/interpreter/src/instructions/contract/call_helpers.rs @@ -1,11 +1,19 @@ use crate::{ - gas, + 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::{context::StateLoad, journaled_state::AccountLoad}; +use context_interface::{host::LoadError, Host}; use core::{cmp::min, ops::Range}; -use primitives::{hardfork::SpecId::*, U256}; +use primitives::{ + hardfork::SpecId::{self, *}, + Address, B256, U256, +}; +use state::Bytecode; /// Gets memory input and output ranges for call instructions. #[inline] @@ -45,27 +53,134 @@ pub fn resize_memory( } /// Calculates gas cost and limit for call instructions. -#[inline] -pub fn calc_call_gas( - interpreter: &mut Interpreter, - account_load: StateLoad, - has_transfer: bool, - local_gas_limit: u64, -) -> Option { - let call_cost = gas::call_cost( - interpreter.runtime_flag.spec_id(), - has_transfer, - account_load, - ); - gas!(interpreter, call_cost, None); +#[inline(never)] +pub fn load_acc_and_calc_gas( + context: &mut InstructionContext<'_, H, impl InterpreterTypes>, + to: Address, + transfers_value: bool, + 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); + + // load account delegated and deduct dynamic gas. + let (gas, bytecode, code_hash) = + load_account_delegated_handle_error(context, to, transfers_value, create_empty_account)?; + let interpreter = &mut context.interpreter; + + // deduct dynamic gas. + gas!(interpreter, gas, None); // EIP-150: Gas cost changes for IO-heavy operations - let gas_limit = if interpreter.runtime_flag.spec_id().is_enabled_in(TANGERINE) { + 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(), local_gas_limit) + min(interpreter.gas.remaining_63_of_64_parts(), stack_gas_limit) } else { - local_gas_limit + stack_gas_limit }; - Some(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); + } + + Some((gas_limit, bytecode, code_hash)) +} + +/// Loads accounts and its delegate account. +#[inline] +pub fn load_account_delegated_handle_error( + context: &mut InstructionContext<'_, H, impl InterpreterTypes>, + to: Address, + transfers_value: bool, + create_empty_account: bool, +) -> Option<(u64, Bytecode, B256)> { + // move this to static gas. + let remaining_gas = context.interpreter.gas.remaining(); + match load_account_delegated( + context.host, + context.interpreter.runtime_flag.spec_id(), + remaining_gas, + to, + transfers_value, + create_empty_account, + ) { + Ok(out) => return Some(out), + Err(LoadError::ColdLoadSkipped) => { + context.interpreter.halt_oog(); + } + Err(LoadError::DBError) => { + context.interpreter.halt_fatal(); + } + } + None +} + +/// Loads accounts and its delegate account. +/// +/// Assumption is that warm gas is already deducted. +#[inline] +pub fn load_account_delegated( + host: &mut H, + spec: SpecId, + remaining_gas: u64, + address: Address, + transfers_value: bool, + create_empty_account: bool, +) -> Result<(u64, Bytecode, B256), LoadError> { + let mut cost = 0; + let is_berlin = spec.is_enabled_in(SpecId::BERLIN); + 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 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; + } + 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_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; + 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 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; + } + bytecode = delegate_account.code.clone().unwrap_or_default(); + code_hash = delegate_account.code_hash(); + } + + Ok((cost, bytecode, code_hash)) +} + +/// Returns new account cost. +#[inline] +pub fn new_account_cost(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 NEWACCOUNT; + } + 0 } diff --git a/crates/interpreter/src/instructions/host.rs b/crates/interpreter/src/instructions/host.rs index 4fb362e7a9..b68f284e2e 100644 --- a/crates/interpreter/src/instructions/host.rs +++ b/crates/interpreter/src/instructions/host.rs @@ -1,9 +1,13 @@ use crate::{ - gas::{self, warm_cold_cost, CALL_STIPEND}, + 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}; @@ -15,27 +19,28 @@ use crate::InstructionContext; pub fn balance(context: InstructionContext<'_, H, WIRE>) { popn_top!([], top, context.interpreter); let address = top.into_address(); - let Some(balance) = context.host.balance(address) else { - context - .interpreter - .halt(InstructionResult::FatalExternalError); - return; - }; let spec_id = context.interpreter.runtime_flag.spec_id(); - gas!( - context.interpreter, - if spec_id.is_enabled_in(BERLIN) { - warm_cold_cost(balance.is_cold) - } else if spec_id.is_enabled_in(ISTANBUL) { + if spec_id.is_enabled_in(BERLIN) { + 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 - } - ); - *top = balance.data; + }; + gas!(context.interpreter, gas); + let Ok(account) = context + .host + .load_account_info_skip_cold_load(address, false, false) + else { + return context.interpreter.halt_fatal(); + }; + *top = account.balance; + }; } /// EIP-1884: Repricing for trie-size-dependent opcodes @@ -49,10 +54,7 @@ pub fn selfbalance( .host .balance(context.interpreter.input.target_address()) else { - context - .interpreter - .halt(InstructionResult::FatalExternalError); - return; + return context.interpreter.halt_fatal(); }; push!(context.interpreter, balance.data); } @@ -65,22 +67,27 @@ pub fn extcodesize( ) { popn_top!([], top, context.interpreter); let address = top.into_address(); - let Some(code) = context.host.load_account_code(address) else { - context - .interpreter - .halt(InstructionResult::FatalExternalError); - return; - }; let spec_id = context.interpreter.runtime_flag.spec_id(); if spec_id.is_enabled_in(BERLIN) { - gas!(context.interpreter, warm_cold_cost(code.is_cold)); - } else if spec_id.is_enabled_in(TANGERINE) { - gas!(context.interpreter, 700); + 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 { - gas!(context.interpreter, 20); + 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) + else { + return context.interpreter.halt_fatal(); + }; + // safe to unwrap because we are loading code + *top = U256::from(account.code.as_ref().unwrap().len()); } - - *top = U256::from(code.len()); } /// EIP-1052: EXTCODEHASH opcode @@ -90,20 +97,31 @@ pub fn extcodehash( check!(context.interpreter, CONSTANTINOPLE); popn_top!([], top, context.interpreter); let address = top.into_address(); - let Some(code_hash) = context.host.load_account_code_hash(address) else { - context - .interpreter - .halt(InstructionResult::FatalExternalError); - return; - }; + let spec_id = context.interpreter.runtime_flag.spec_id(); - if spec_id.is_enabled_in(BERLIN) { - gas!(context.interpreter, warm_cold_cost(code_hash.is_cold)); - } else if spec_id.is_enabled_in(ISTANBUL) { - gas!(context.interpreter, 700); + let account = if spec_id.is_enabled_in(BERLIN) { + berlin_load_account!(context, address, true) } else { - gas!(context.interpreter, 400); - } + 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) + else { + return context.interpreter.halt_fatal(); + }; + account + }; + // if account is empty, code hash is zero + let code_hash = if account.is_empty() { + B256::ZERO + } else { + account.code_hash + }; *top = code_hash.into_u256(); } @@ -118,34 +136,48 @@ pub fn extcodecopy( context.interpreter ); let address = address.into_address(); - let Some(code) = context.host.load_account_code(address) else { - context - .interpreter - .halt(InstructionResult::FatalExternalError); - return; - }; + + let spec_id = context.interpreter.runtime_flag.spec_id(); let len = as_usize_or_fail!(context.interpreter, len_u256); - gas_or_fail!( + gas!( context.interpreter, - gas::extcodecopy_cost( - context.interpreter.runtime_flag.spec_id(), - len, - code.is_cold - ) + gas::copy_cost(0, len).unwrap_or(u64::MAX) ); - if len == 0 { - return; + + let mut memory_offset_usize = 0; + // resize memory only if len is not zero + 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); } - let memory_offset = as_usize_or_fail!(context.interpreter, memory_offset); - let code_offset = min(as_usize_saturated!(code_offset), code.len()); - resize_memory!(context.interpreter, memory_offset, 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(); + }; + code.data + }; + + let code_offset_usize = min(as_usize_saturated!(code_offset), code.len()); // Note: This can't panic because we resized memory to fit. + // len zero is handled in set_data context .interpreter .memory - .set_data(memory_offset, code_offset, len, &code); + .set_data(memory_offset_usize, code_offset_usize, len, &code); } /// Implements the BLOCKHASH instruction. @@ -175,10 +207,7 @@ pub fn blockhash( *number = if diff <= BLOCK_HASH_HISTORY { let Some(hash) = context.host.block_hash(as_u64_saturated!(requested_number)) else { - context - .interpreter - .halt(InstructionResult::FatalExternalError); - return; + return context.interpreter.halt_fatal(); }; U256::from_be_bytes(hash.0) } else { @@ -191,22 +220,42 @@ pub fn blockhash( /// Loads a word from storage. pub fn sload(context: InstructionContext<'_, H, WIRE>) { popn_top!([], index, context.interpreter); + let spec_id = context.interpreter.runtime_flag.spec_id(); + let target = context.interpreter.input.target_address(); - let Some(value) = context - .host - .sload(context.interpreter.input.target_address(), *index) - else { - context - .interpreter - .halt(InstructionResult::FatalExternalError); - return; + // `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 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); + } + + *index = storage.data; + } + Err(LoadError::ColdLoadSkipped) => context.interpreter.halt_oog(), + Err(LoadError::DBError) => context.interpreter.halt_fatal(), + } + } else { + let Some(storage) = context.host.sload(target, *index) else { + return context.interpreter.halt_fatal(); + }; + *index = storage.data; }; - - gas!( - context.interpreter, - gas::sload_cost(context.interpreter.runtime_flag.spec_id(), value.is_cold) - ); - *index = value.data; } /// Implements the SSTORE instruction. @@ -214,19 +263,10 @@ pub fn sload(context: InstructionConte /// Stores a word to storage. pub fn sstore(context: InstructionContext<'_, H, WIRE>) { require_non_staticcall!(context.interpreter); - popn!([index, value], context.interpreter); - let Some(state_load) = - context - .host - .sstore(context.interpreter.input.target_address(), index, value) - else { - context - .interpreter - .halt(InstructionResult::FatalExternalError); - return; - }; + 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 @@ -241,15 +281,41 @@ pub fn sstore(context: InstructionCont .halt(InstructionResult::ReentrancySentryOOG); return; } + + // static gas + gas!( + context.interpreter, + gas::static_sstore_cost(context.interpreter.runtime_flag.spec_id()) + ); + + let state_load = if spec_id.is_enabled_in(BERLIN) { + let skip_cold = context.interpreter.gas.remaining() < COLD_SLOAD_COST_ADDITIONAL; + let res = context + .host + .sstore_skip_cold_load(target, index, value, skip_cold); + match res { + Ok(load) => load, + Err(LoadError::ColdLoadSkipped) => return context.interpreter.halt_oog(), + Err(LoadError::DBError) => return context.interpreter.halt_fatal(), + } + } else { + let Some(load) = context.host.sstore(target, index, value) else { + return context.interpreter.halt_fatal(); + }; + load + }; + + // dynamic gas gas!( context.interpreter, - gas::sstore_cost( + gas::dyn_sstore_cost( context.interpreter.runtime_flag.spec_id(), &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, @@ -301,12 +367,8 @@ pub fn log( resize_memory!(context.interpreter, offset, len); Bytes::copy_from_slice(context.interpreter.memory.slice_len(offset, len).as_ref()) }; - if context.interpreter.stack.len() < N { - context.interpreter.halt(InstructionResult::StackUnderflow); - return; - } let Some(topics) = context.interpreter.stack.popn::() else { - context.interpreter.halt(InstructionResult::StackUnderflow); + context.interpreter.halt_underflow(); return; }; @@ -328,6 +390,10 @@ pub fn selfdestruct( require_non_staticcall!(context.interpreter); popn!([target], context.interpreter); let target = target.into_address(); + let spec = context.interpreter.runtime_flag.spec_id(); + + // static gas + gas!(context.interpreter, gas::static_selfdestruct_cost(spec)); let Some(res) = context .host @@ -339,6 +405,8 @@ pub fn selfdestruct( return; }; + gas!(context.interpreter, gas::dyn_selfdestruct_cost(spec, &res)); + // EIP-3529: Reduction in refunds if !context .interpreter @@ -347,13 +415,11 @@ pub fn selfdestruct( .is_enabled_in(LONDON) && !res.previously_destroyed { - context.interpreter.gas.record_refund(gas::SELFDESTRUCT) + context + .interpreter + .gas + .record_refund(gas::SELFDESTRUCT_REFUND); } - gas!( - context.interpreter, - gas::selfdestruct_cost(context.interpreter.runtime_flag.spec_id(), res) - ); - context.interpreter.halt(InstructionResult::SelfDestruct); } diff --git a/crates/interpreter/src/instructions/macros.rs b/crates/interpreter/src/instructions/macros.rs index 8904830211..7e675dc74c 100644 --- a/crates/interpreter/src/instructions/macros.rs +++ b/crates/interpreter/src/instructions/macros.rs @@ -68,6 +68,43 @@ macro_rules! gas { }; } +/// Loads account and account berlin gas cost accounting. +#[macro_export] +#[collapse_debuginfo(yes)] +macro_rules! berlin_load_account { + ($context:expr, $address:expr, $load_code:expr) => { + $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; + 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 + ); + } + account + } + Err(LoadError::ColdLoadSkipped) => { + $context.interpreter.halt_oog(); + return $ret; + } + Err(LoadError::DBError) => { + $context.interpreter.halt_fatal(); + return $ret; + } + } + }}; +} + /// Same as [`gas!`], but with `gas` as an option. #[macro_export] #[collapse_debuginfo(yes)] @@ -95,6 +132,11 @@ macro_rules! resize_memory { $crate::resize_memory!($interpreter, $offset, $len, ()) }; ($interpreter:expr, $offset:expr, $len:expr, $ret:expr) => { + #[cfg(feature = "memory_limit")] + if $interpreter.memory.limit_reached($offset, $len) { + $interpreter.halt_memory_limit_oog(); + return $ret; + } if !$crate::interpreter::resize_memory( &mut $interpreter.gas, &mut $interpreter.memory, diff --git a/crates/interpreter/src/instructions/tx_info.rs b/crates/interpreter/src/instructions/tx_info.rs index 9f0e9f38cb..d710b1d24f 100644 --- a/crates/interpreter/src/instructions/tx_info.rs +++ b/crates/interpreter/src/instructions/tx_info.rs @@ -2,7 +2,6 @@ use crate::{ interpreter_types::{InterpreterTypes, RuntimeFlag, StackTr}, Host, }; -use primitives::U256; use crate::InstructionContext; @@ -13,10 +12,7 @@ pub fn gasprice( context: InstructionContext<'_, H, WIRE>, ) { //gas!(context.interpreter, gas::BASE); - push!( - context.interpreter, - U256::from(context.host.effective_gas_price()) - ); + push!(context.interpreter, context.host.effective_gas_price()); } /// Implements the ORIGIN instruction. diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index aa77301911..3eeb6f10b0 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -27,7 +27,7 @@ use primitives::{hardfork::SpecId, Bytes}; /// Main interpreter structure that contains all components defined in [`InterpreterTypes`]. #[derive(Debug, Clone)] -#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Interpreter { /// Bytecode being executed. pub bytecode: WIRE::Bytecode, @@ -209,6 +209,18 @@ impl Interpreter { .set_action(InterpreterAction::new_halt(result, self.gas)); } + /// Halt the interpreter with the given result. + /// + /// This will set the action to [`InterpreterAction::Return`] and set the gas to the current gas. + #[cold] + #[inline(never)] + pub fn halt_fatal(&mut self) { + self.bytecode.set_action(InterpreterAction::new_halt( + InstructionResult::FatalExternalError, + self.gas, + )); + } + /// Halt the interpreter with an out-of-gas error. #[cold] #[inline(never)] @@ -221,6 +233,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); } @@ -409,13 +428,8 @@ mod tests { u64::MAX, ); - let serialized = - bincode::serde::encode_to_vec(&interpreter, bincode::config::legacy()).unwrap(); - - let deserialized: Interpreter = - bincode::serde::decode_from_slice(&serialized, bincode::config::legacy()) - .unwrap() - .0; + let serialized = serde_json::to_string_pretty(&interpreter).unwrap(); + let deserialized: Interpreter = serde_json::from_str(&serialized).unwrap(); assert_eq!( interpreter.bytecode.pc(), @@ -424,3 +438,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; + 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; + 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.rs b/crates/interpreter/src/interpreter/ext_bytecode.rs index bbfc7030bf..d8a5306ba6 100644 --- a/crates/interpreter/src/interpreter/ext_bytecode.rs +++ b/crates/interpreter/src/interpreter/ext_bytecode.rs @@ -68,14 +68,6 @@ impl ExtBytecode { } } - /// Regenerates the bytecode hash. - #[inline] - #[deprecated(note = "use `get_or_calculate_hash` or `calculate_hash` instead")] - #[doc(hidden)] - pub fn regenerate_hash(&mut self) -> B256 { - self.calculate_hash() - } - /// Re-calculates the bytecode hash. /// /// Prefer [`get_or_calculate_hash`](Self::get_or_calculate_hash) if you just need to get the hash. @@ -166,7 +158,7 @@ impl Jumps for ExtBytecode { // In practice this is always true unless a caller modifies the `instruction_pointer` field manually. unsafe { self.instruction_pointer - .offset_from(self.base.bytes_ref().as_ptr()) as usize + .offset_from_unsigned(self.base.bytes_ref().as_ptr()) } } } diff --git a/crates/interpreter/src/interpreter/shared_memory.rs b/crates/interpreter/src/interpreter/shared_memory.rs index 8e1b503954..f83dde5e14 100644 --- a/crates/interpreter/src/interpreter/shared_memory.rs +++ b/crates/interpreter/src/interpreter/shared_memory.rs @@ -125,6 +125,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 +195,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 +222,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 @@ -513,6 +527,9 @@ impl SharedMemory { /// Assumes that dst and src are valid. /// Assumes that dst and src do not overlap. unsafe fn set_data(dst: &mut [u8], src: &[u8], dst_offset: usize, src_offset: usize, len: usize) { + if len == 0 { + return; + } if src_offset >= src.len() { // Nullify all memory slots dst.get_mut(dst_offset..dst_offset + len).unwrap().fill(0); diff --git a/crates/interpreter/src/interpreter/stack.rs b/crates/interpreter/src/interpreter/stack.rs index ae675fea20..761ec7da6c 100644 --- a/crates/interpreter/src/interpreter/stack.rs +++ b/crates/interpreter/src/interpreter/stack.rs @@ -38,9 +38,9 @@ impl Default for Stack { impl Clone for Stack { fn clone(&self) -> Self { - // Use `Self::new()` to ensure the cloned Stack maintains the STACK_LIMIT capacity, - // and then copy the data. This preserves the invariant that Stack always has - // STACK_LIMIT capacity, which is crucial for the safety and correctness of other methods. + // Use `Self::new()` to ensure the cloned Stack is constructed with at least + // STACK_LIMIT capacity, and then copy the data. This preserves the invariant + // that Stack has sufficient capacity for operations that rely on it. let mut new_stack = Self::new(); new_stack.data.extend_from_slice(&self.data); new_stack @@ -53,6 +53,11 @@ impl StackTr for Stack { self.len() } + #[inline] + fn data(&self) -> &[U256] { + &self.data + } + #[inline] fn clear(&mut self) { self.data.clear(); @@ -208,8 +213,8 @@ impl Stack { #[must_use] #[cfg_attr(debug_assertions, track_caller)] pub fn push(&mut self, value: U256) -> bool { - // Allows the compiler to optimize out the `Vec::push` capacity check. - assume!(self.data.capacity() == STACK_LIMIT); + // In debug builds, verify we have sufficient capacity provisioned. + debug_assert!(self.data.capacity() >= STACK_LIMIT); if self.data.len() == STACK_LIMIT { return false; } @@ -317,6 +322,9 @@ impl Stack { return false; } + // In debug builds, ensure underlying capacity is sufficient for the write. + debug_assert!(self.data.capacity() >= new_len); + // SAFETY: Length checked above. unsafe { let dst = self.data.as_mut_ptr().add(self.data.len()).cast::(); @@ -389,16 +397,21 @@ impl<'de> serde::Deserialize<'de> for Stack { where D: serde::Deserializer<'de>, { - let mut data = Vec::::deserialize(deserializer)?; - if data.len() > STACK_LIMIT { + #[derive(serde::Deserialize)] + struct StackSerde { + data: Vec, + } + + let mut stack = StackSerde::deserialize(deserializer)?; + if stack.data.len() > STACK_LIMIT { return Err(serde::de::Error::custom(std::format!( "stack size exceeds limit: {} > {}", - data.len(), + stack.data.len(), STACK_LIMIT ))); } - data.reserve(STACK_LIMIT - data.len()); - Ok(Self { data }) + stack.data.reserve(STACK_LIMIT - stack.data.len()); + Ok(Self { data: stack.data }) } } diff --git a/crates/interpreter/src/interpreter_action/call_inputs.rs b/crates/interpreter/src/interpreter_action/call_inputs.rs index 11865c818f..e7224607cf 100644 --- a/crates/interpreter/src/interpreter_action/call_inputs.rs +++ b/crates/interpreter/src/interpreter_action/call_inputs.rs @@ -1,6 +1,7 @@ use context_interface::{ContextTr, LocalContextTr}; use core::ops::Range; -use primitives::{Address, Bytes, U256}; +use primitives::{Address, Bytes, B256, U256}; +use state::Bytecode; /// Input enum for a call. /// /// As CallInput uses shared memory buffer it can get overridden if not used directly when call happens. @@ -42,7 +43,7 @@ impl CallInput { /// /// If this `CallInput` is a `SharedBuffer`, the slice will be copied /// into a fresh `Bytes` buffer, which can pose a performance penalty. - pub fn bytes(&self, ctx: &mut CTX) -> Bytes + pub fn bytes(&self, ctx: &CTX) -> Bytes where CTX: ContextTr, { @@ -78,6 +79,10 @@ pub struct CallInputs { /// /// Previously `context.code_address`. pub bytecode_address: Address, + /// 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_types.rs b/crates/interpreter/src/interpreter_types.rs index 4c152336a3..3478be4ea2 100644 --- a/crates/interpreter/src/interpreter_types.rs +++ b/crates/interpreter/src/interpreter_types.rs @@ -144,8 +144,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. @@ -153,6 +158,9 @@ pub trait StackTr { /// Returns stack length. fn len(&self) -> usize; + /// Returns stack content. + fn data(&self) -> &[U256]; + /// Returns `true` if stack is empty. fn is_empty(&self) -> bool { self.len() == 0 diff --git a/crates/op-revm/CHANGELOG.md b/crates/op-revm/CHANGELOG.md index 05e6874cf9..a509975f12 100644 --- a/crates/op-revm/CHANGELOG.md +++ b/crates/op-revm/CHANGELOG.md @@ -7,6 +7,99 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [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 + +- updated the following local packages: revm + +## [11.1.0](https://github.com/bluealloy/revm/compare/op-revm-v11.0.0...op-revm-v11.1.0) - 2025-10-09 + +### Fixed + +- *(op-revm)* return error instead of panic when enveloped_tx is missing ([#3055](https://github.com/bluealloy/revm/pull/3055)) + +### Other + +- *(op)* backport of #3073 fix for l1block info ([#3076](https://github.com/bluealloy/revm/pull/3076)) +- backport v89 changelog ([#3075](https://github.com/bluealloy/revm/pull/3075)) +- *(op)* split paths for deposit tx in caller deduction ([#3041](https://github.com/bluealloy/revm/pull/3041)) + +## [10.1.1](https://github.com/bluealloy/revm/compare/op-revm-v10.0.0...op-revm-v10.1.1) - 2025-09-23 + +## [11.0.0](https://github.com/bluealloy/revm/compare/op-revm-v10.1.0...op-revm-v11.0.0) - 2025-10-07 + +### Added + +- *(jovian)* add da footprint block limit. ([#3003](https://github.com/bluealloy/revm/pull/3003)) +- *(op-revm)* implement jovian operator fee fix ([#2996](https://github.com/bluealloy/revm/pull/2996)) +- *(op-revm)* Add an option to disable "fee-charge" on `op-revm` ([#2980](https://github.com/bluealloy/revm/pull/2980)) +- [**breaking**] Remove kzg-rs ([#2909](https://github.com/bluealloy/revm/pull/2909)) + +### Fixed + +- add missing is_fee_charge_disabled check ([#3007](https://github.com/bluealloy/revm/pull/3007)) +- Apply spelling corrections from PRs #2926, #2915, #2908 ([#2978](https://github.com/bluealloy/revm/pull/2978)) +- *(op-revm)* clear enveloped_tx for deposit txs in build_fill and align docs ([#2957](https://github.com/bluealloy/revm/pull/2957)) + +### Other + +- changelog update for v87 ([#3056](https://github.com/bluealloy/revm/pull/3056)) +- add boundless ([#3043](https://github.com/bluealloy/revm/pull/3043)) +- helper function gas_balance_spending ([#3030](https://github.com/bluealloy/revm/pull/3030)) +- helper caller_initial_modification added ([#3032](https://github.com/bluealloy/revm/pull/3032)) +- EvmTr and InspectorEvmTr receive all/all_mut fn ([#3037](https://github.com/bluealloy/revm/pull/3037)) +- add ensure_enough_balance helper ([#3033](https://github.com/bluealloy/revm/pull/3033)) +- *(op-revm)* propagate optional_fee_charge feature ([#3020](https://github.com/bluealloy/revm/pull/3020)) +- Set l2_block in try_fetch for pre-Isthmus forks; add reload tests ([#2994](https://github.com/bluealloy/revm/pull/2994)) +- prealloc few frames ([#2965](https://github.com/bluealloy/revm/pull/2965)) +- treat empty input as zero operator fee in operator_fee_charge ([#2973](https://github.com/bluealloy/revm/pull/2973)) +- add SECURITY.md ([#2956](https://github.com/bluealloy/revm/pull/2956)) +- *(op-revm)* rm redundant phantom ([#2943](https://github.com/bluealloy/revm/pull/2943)) +- *(op-revm)* add serialize DepositTransactionParts test ([#2942](https://github.com/bluealloy/revm/pull/2942)) +- *(handler)* provide `&CallInputs`to`PrecompileProvider::run` ([#2921](https://github.com/bluealloy/revm/pull/2921)) + ## [10.1.0](https://github.com/bluealloy/revm/compare/op-revm-v10.0.0...op-revm-v10.1.0) - 2025-09-23 ### Added diff --git a/crates/op-revm/Cargo.toml b/crates/op-revm/Cargo.toml index c2abb9f6af..40c28811f6 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 = "10.1.0" +version = "12.0.1" authors.workspace = true edition.workspace = true keywords.workspace = true @@ -61,11 +61,10 @@ optional_block_gas_limit = ["revm/optional_block_gas_limit"] optional_eip3541 = ["revm/optional_eip3541"] optional_eip3607 = ["revm/optional_eip3607"] optional_no_base_fee = ["revm/optional_no_base_fee"] +optional_fee_charge = ["revm/optional_fee_charge"] # See comments in `revm-precompile` secp256k1 = ["revm/secp256k1"] c-kzg = ["revm/c-kzg"] -# `kzg-rs` is not audited but useful for `no_std` environment, use it with causing and default to `c-kzg` if possible. -kzg-rs = ["revm/kzg-rs"] blst = ["revm/blst"] bn = ["revm/bn"] diff --git a/crates/op-revm/src/constants.rs b/crates/op-revm/src/constants.rs index c639e8573c..2c93bff13b 100644 --- a/crates/op-revm/src/constants.rs +++ b/crates/op-revm/src/constants.rs @@ -18,11 +18,18 @@ pub const OPERATOR_FEE_SCALAR_OFFSET: usize = 20; /// the storage slot of the 8-byte operatorFeeConstant attribute. 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 = 18; + /// The fixed point decimal scaling factor associated with the operator fee scalar. /// /// Allows users to use 6 decimal points of precision when specifying the operator_fee_scalar. pub const OPERATOR_FEE_SCALAR_DECIMAL: u64 = 1_000_000; +/// The Jovian multiplier applied to the operator fee scalar component. +pub const OPERATOR_FEE_JOVIAN_MULTIPLIER: u64 = 100; + /// The L1 base fee slot. pub const L1_BASE_FEE_SLOT: U256 = U256::from_limbs([1u64, 0, 0, 0]); /// The L1 overhead slot. @@ -41,6 +48,10 @@ 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 16-bit daFootprintGasScalar attribute at +/// offset [DA_FOOTPRINT_GAS_SCALAR_OFFSET]. +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 8f13b36c66..a93a172756 100644 --- a/crates/op-revm/src/evm.rs +++ b/crates/op-revm/src/evm.rs @@ -34,7 +34,7 @@ impl OpEvm inspector, instruction: EthInstructions::new_mainnet(), precompiles: OpPrecompiles::default(), - frame_stack: FrameStack::new(), + frame_stack: FrameStack::new_prealloc(8), }) } } @@ -65,38 +65,30 @@ where { type Inspector = INSP; - fn inspector(&mut self) -> &mut Self::Inspector { - &mut self.0.inspector - } - - fn ctx_inspector(&mut self) -> (&mut Self::Context, &mut Self::Inspector) { - (&mut self.0.ctx, &mut self.0.inspector) - } - - fn ctx_inspector_frame( - &mut self, - ) -> (&mut Self::Context, &mut Self::Inspector, &mut Self::Frame) { - ( - &mut self.0.ctx, - &mut self.0.inspector, - self.0.frame_stack.get(), - ) + #[inline] + fn all_inspector( + &self, + ) -> ( + &Self::Context, + &Self::Instructions, + &Self::Precompiles, + &FrameStack, + &Self::Inspector, + ) { + self.0.all_inspector() } - fn ctx_inspector_frame_instructions( + #[inline] + fn all_mut_inspector( &mut self, ) -> ( &mut Self::Context, - &mut Self::Inspector, - &mut Self::Frame, &mut Self::Instructions, + &mut Self::Precompiles, + &mut FrameStack, + &mut Self::Inspector, ) { - ( - &mut self.0.ctx, - &mut self.0.inspector, - self.0.frame_stack.get(), - &mut self.0.instruction, - ) + self.0.all_mut_inspector() } } @@ -111,24 +103,28 @@ where type Precompiles = P; type Frame = EthFrame; - fn ctx(&mut self) -> &mut Self::Context { - &mut self.0.ctx - } - - fn ctx_ref(&self) -> &Self::Context { - &self.0.ctx - } - - fn ctx_instructions(&mut self) -> (&mut Self::Context, &mut Self::Instructions) { - (&mut self.0.ctx, &mut self.0.instruction) - } - - fn ctx_precompiles(&mut self) -> (&mut Self::Context, &mut Self::Precompiles) { - (&mut self.0.ctx, &mut self.0.precompiles) + #[inline] + fn all( + &self, + ) -> ( + &Self::Context, + &Self::Instructions, + &Self::Precompiles, + &FrameStack, + ) { + self.0.all() } - fn frame_stack(&mut self) -> &mut FrameStack { - &mut self.0.frame_stack + #[inline] + fn all_mut( + &mut self, + ) -> ( + &mut Self::Context, + &mut Self::Instructions, + &mut Self::Precompiles, + &mut FrameStack, + ) { + self.0.all_mut() } fn frame_init( diff --git a/crates/op-revm/src/handler.rs b/crates/op-revm/src/handler.rs index 732be00847..5f4f4ed16d 100644 --- a/crates/op-revm/src/handler.rs +++ b/crates/op-revm/src/handler.rs @@ -6,7 +6,7 @@ use crate::{ L1BlockInfo, OpHaltReason, OpSpecId, }; use revm::{ - context::{result::InvalidTransaction, LocalContextTr}, + context::{journaled_state::JournalCheckpoint, result::InvalidTransaction, LocalContextTr}, context_interface::{ context::ContextError, result::{EVMError, ExecutionResult, FromStringError}, @@ -16,7 +16,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}, @@ -31,8 +31,6 @@ pub struct OpHandler { /// Mainnet handler allows us to use functions from the mainnet handler inside optimism handler. /// So we dont duplicate the logic pub mainnet: MainnetHandler, - /// Phantom data to avoid type inference issues. - pub _phantom: core::marker::PhantomData<(EVM, ERROR, FRAME)>, } impl OpHandler { @@ -40,7 +38,6 @@ impl OpHandler { pub fn new() -> Self { Self { mainnet: MainnetHandler::default(), - _phantom: core::marker::PhantomData, } } } @@ -91,6 +88,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) } @@ -98,122 +101,76 @@ 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 mint = if is_deposit { - ctx.tx().mint().unwrap_or_default() - } else { - 0 - }; - - let mut additional_cost = U256::ZERO; - - // The L1-cost fee is only computed for Optimism non-deposit transactions. - if !is_deposit && !ctx.cfg().is_fee_charge_disabled() { - // 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 != block_number { - *ctx.chain_mut() = L1BlockInfo::try_fetch(ctx.db_mut(), block_number, spec)?; + let (block, tx, cfg, journal, chain, _) = evm.ctx().all_mut(); + let spec = cfg.spec(); + + 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 mut caller = journal.load_account_with_code_mut(tx.caller())?.data; + + let effective_balance_spending = tx + .effective_balance_spending(basefee, blob_price) + .expect("Deposit transaction effective balance spending overflow") + - tx.value(); + + // Mind value should be added first before subtracting the effective balance spending. + let mut new_balance = caller + .balance() + .saturating_add(U256::from(tx.mint().unwrap_or_default())) + .saturating_sub(effective_balance_spending); + + if cfg.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()); } - // account for additional cost of l1 fee and operator fee - let enveloped_tx = ctx - .tx() - .enveloped_tx() - .expect("all not deposit tx have enveloped tx") - .clone(); - - // 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); - additional_cost = additional_cost.saturating_add(operator_fee_charge); + // 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(); } - } - - let (tx, journal) = ctx.tx_journal_mut(); - - let caller_account = journal.load_account_code(tx.caller())?.data; - if !is_deposit { - // validates account nonce and code - validate_account_nonce_and_code( - &mut caller_account.info, - tx.nonce(), - is_eip3607_disabled, - is_nonce_check_disabled, - )?; + return Ok(()); } - let max_balance_spending = tx.max_balance_spending()?.saturating_add(additional_cost); - - // old balance is journaled before mint is incremented. - let old_balance = caller_account.info.balance; - - // If the transaction is a deposit with a `mint` value, add the mint value - // in wei to the caller's balance. This should be persisted to the database - // prior to the rest of execution. - let mut new_balance = caller_account.info.balance.saturating_add(U256::from(mint)); - - // Check if account has enough balance for `gas_limit * max_fee`` and value transfer. - // Transfer will be done inside `*_inner` functions. - if !is_deposit && max_balance_spending > new_balance && !is_balance_check_disabled { - // skip max balance check for deposit transactions. - // this check for deposit was skipped previously in `validate_tx_against_state` function - return Err(InvalidTransaction::LackOfFundForMaxFee { - fee: Box::new(max_balance_spending), - balance: Box::new(new_balance), - } - .into()); + // 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 chain.l2_block != Some(block.number()) { + *chain = L1BlockInfo::try_fetch(journal.db_mut(), block.number(), spec)?; } - let effective_balance_spending = tx - .effective_balance_spending(basefee, blob_price) - .expect("effective balance is always smaller than max balance so it can't overflow"); - - // subtracting max balance spending with value that is going to be deducted later in the call. - let gas_balance_spending = effective_balance_spending - tx.value(); + let mut caller_account = journal.load_account_with_code_mut(tx.caller())?.data; - // 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); + // validates account nonce and code + validate_account_nonce_and_code_with_components(&caller_account.info, tx, cfg)?; - new_balance = new_balance.saturating_sub(op_gas_balance_spending); + // check additional cost and deduct it from the caller's balances + let mut balance = caller_account.info.balance; - 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()); + if !cfg.is_fee_charge_disabled() { + let additional_cost = chain.tx_cost_with_tx(tx, spec); + let Some(new_balance) = balance.checked_sub(additional_cost) else { + return Err(InvalidTransaction::LackOfFundForMaxFee { + fee: Box::new(additional_cost), + balance: Box::new(balance), + } + .into()); + }; + balance = new_balance } - // Touch account so we know it is changed. - caller_account.mark_touch(); - caller_account.info.balance = new_balance; + let balance = calculate_caller_fee(balance, tx, block, cfg)?; - // Bump the nonce for calls. Nonce for CREATE will be bumped in `handle_create`. + // make changes to the account + caller_account.set_balance(balance); if tx.kind().is_call() { - caller_account.info.nonce = caller_account.info.nonce.saturating_add(1); + caller_account.bump_nonce(); } - // 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(()) } @@ -290,7 +247,9 @@ where ) -> Result<(), Self::Error> { let mut additional_refund = U256::ZERO; - if evm.ctx().tx().tx_type() != DEPOSIT_TRANSACTION_TYPE { + if evm.ctx().tx().tx_type() != DEPOSIT_TRANSACTION_TYPE + && !evm.ctx().cfg().is_fee_charge_disabled() + { let spec = evm.ctx().cfg().spec(); additional_refund = evm .ctx() @@ -355,7 +314,11 @@ where let l1_cost = l1_block_info.calculate_tx_l1_cost(enveloped_tx, spec); let operator_fee_cost = if spec.is_enabled_in(OpSpecId::ISTHMUS) { - l1_block_info.operator_fee_charge(enveloped_tx, U256::from(frame_result.gas().used())) + l1_block_info.operator_fee_charge( + enveloped_tx, + U256::from(frame_result.gas().used()), + spec, + ) } else { U256::ZERO }; @@ -418,9 +381,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 @@ -431,23 +396,12 @@ 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 old_balance = acc.info.balance; + let mut acc = journal.load_account_mut(caller)?; + acc.bump_nonce(); + acc.incr_balance(U256::from(mint.unwrap_or_default())); - // 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 @@ -685,6 +639,7 @@ mod tests { l1_base_fee: U256::from(1_000), l1_fee_overhead: Some(U256::from(1_000)), l1_base_fee_scalar: U256::from(1_000), + l2_block: Some(U256::from(0)), ..Default::default() }) .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH) @@ -753,9 +708,297 @@ mod tests { let ctx = Context::op() .with_db(db) .with_chain(L1BlockInfo { - l2_block: BLOCK_NUM + U256::from(1), // ahead by one block + l2_block: Some(BLOCK_NUM + U256::from(1)), // ahead by one block + ..Default::default() + }) + .with_block(BlockEnv { + number: BLOCK_NUM, + ..Default::default() + }) + .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::ISTHMUS); + + let mut evm = ctx.build_op(); + + assert_ne!(evm.ctx().chain().l2_block, Some(BLOCK_NUM)); + + let handler = + OpHandler::<_, EVMError<_, OpTransactionError>, EthFrame>::new(); + handler + .validate_against_state_and_deduct_caller(&mut evm) + .unwrap(); + + assert_eq!( + *evm.ctx().chain(), + L1BlockInfo { + l2_block: Some(BLOCK_NUM), + l1_base_fee: L1_BASE_FEE, + l1_base_fee_scalar: U256::from(L1_BASE_FEE_SCALAR), + l1_blob_base_fee: Some(L1_BLOB_BASE_FEE), + l1_blob_base_fee_scalar: Some(U256::from(L1_BLOB_BASE_FEE_SCALAR)), + empty_ecotone_scalars: false, + l1_fee_overhead: None, + 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: None + } + ); + } + + #[test] + fn test_parse_da_footprint_gas_scalar_jovian() { + const BLOCK_NUM: U256 = uint!(100_U256); + const L1_BASE_FEE: U256 = uint!(1_U256); + const L1_BLOB_BASE_FEE: U256 = uint!(2_U256); + const L1_BASE_FEE_SCALAR: u64 = 3; + const L1_BLOB_BASE_FEE_SCALAR: u64 = 4; + const L1_FEE_SCALARS: U256 = U256::from_limbs([ + 0, + (L1_BASE_FEE_SCALAR << (64 - BASE_FEE_SCALAR_OFFSET * 2)) | L1_BLOB_BASE_FEE_SCALAR, + 0, + 0, + ]); + 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(); + l1_block_contract + .storage + .insert(L1_BASE_FEE_SLOT, L1_BASE_FEE); + l1_block_contract + .storage + .insert(ECOTONE_L1_BLOB_BASE_FEE_SLOT, L1_BLOB_BASE_FEE); + l1_block_contract + .storage + .insert(ECOTONE_L1_FEE_SCALARS_SLOT, L1_FEE_SCALARS); + l1_block_contract.storage.insert( + OPERATOR_FEE_SCALARS_SLOT, + operator_fee_and_da_footprint_u256, + ); + db.insert_account_info( + Address::ZERO, + AccountInfo { + balance: U256::from(6000), + ..Default::default() + }, + ); + + let ctx = Context::op() + .with_db(db) + .with_chain(L1BlockInfo { + l2_block: Some(BLOCK_NUM + U256::from(1)), // ahead by one block + operator_fee_scalar: Some(U256::from(2)), + operator_fee_constant: Some(U256::from(50)), + ..Default::default() + }) + .with_block(BlockEnv { + number: BLOCK_NUM, + ..Default::default() + }) + .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::JOVIAN) + // set the operator fee to a low value + .with_tx( + OpTransaction::builder() + .base(TxEnv::builder().gas_limit(10)) + .enveloped_tx(Some(bytes!("FACADE"))) + .build_fill(), + ); + + let mut evm = ctx.build_op(); + + assert_ne!(evm.ctx().chain().l2_block, Some(BLOCK_NUM)); + + let handler = + OpHandler::<_, EVMError<_, OpTransactionError>, EthFrame>::new(); + handler + .validate_against_state_and_deduct_caller(&mut evm) + .unwrap(); + + assert_eq!( + *evm.ctx().chain(), + L1BlockInfo { + l2_block: Some(BLOCK_NUM), + l1_base_fee: L1_BASE_FEE, + l1_base_fee_scalar: U256::from(L1_BASE_FEE_SCALAR), + l1_blob_base_fee: Some(L1_BLOB_BASE_FEE), + l1_blob_base_fee_scalar: Some(U256::from(L1_BLOB_BASE_FEE_SCALAR)), + empty_ecotone_scalars: false, + l1_fee_overhead: None, + 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 as u16), + } + ); + } + + #[test] + fn test_reload_l1_block_info_regolith() { + const BLOCK_NUM: U256 = uint!(200_U256); + const L1_BASE_FEE: U256 = uint!(7_U256); + const L1_FEE_OVERHEAD: U256 = uint!(9_U256); + const L1_BASE_FEE_SCALAR: u64 = 11; + + let mut db = InMemoryDB::default(); + let l1_block_contract = db.load_account(L1_BLOCK_CONTRACT).unwrap(); + l1_block_contract + .storage + .insert(L1_BASE_FEE_SLOT, L1_BASE_FEE); + // Pre-ecotone bedrock/regolith slots + use crate::constants::{L1_OVERHEAD_SLOT, L1_SCALAR_SLOT}; + l1_block_contract + .storage + .insert(L1_OVERHEAD_SLOT, L1_FEE_OVERHEAD); + l1_block_contract + .storage + .insert(L1_SCALAR_SLOT, U256::from(L1_BASE_FEE_SCALAR)); + + let ctx = Context::op() + .with_db(db) + .with_chain(L1BlockInfo { + l2_block: Some(BLOCK_NUM + U256::from(1)), + ..Default::default() + }) + .with_block(BlockEnv { + number: BLOCK_NUM, + ..Default::default() + }) + .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH); + + let mut evm = ctx.build_op(); + assert_ne!(evm.ctx().chain().l2_block, Some(BLOCK_NUM)); + + let handler = + OpHandler::<_, EVMError<_, OpTransactionError>, EthFrame>::new(); + handler + .validate_against_state_and_deduct_caller(&mut evm) + .unwrap(); + + assert_eq!( + *evm.ctx().chain(), + L1BlockInfo { + l2_block: Some(BLOCK_NUM), + l1_base_fee: L1_BASE_FEE, + l1_fee_overhead: Some(L1_FEE_OVERHEAD), + l1_base_fee_scalar: U256::from(L1_BASE_FEE_SCALAR), + tx_l1_cost: Some(U256::ZERO), + ..Default::default() + } + ); + } + + #[test] + fn test_reload_l1_block_info_ecotone_pre_isthmus() { + const BLOCK_NUM: U256 = uint!(300_U256); + const L1_BASE_FEE: U256 = uint!(13_U256); + const L1_BLOB_BASE_FEE: U256 = uint!(17_U256); + const L1_BASE_FEE_SCALAR: u64 = 19; + const L1_BLOB_BASE_FEE_SCALAR: u64 = 23; + const L1_FEE_SCALARS: U256 = U256::from_limbs([ + 0, + (L1_BASE_FEE_SCALAR << (64 - BASE_FEE_SCALAR_OFFSET * 2)) | L1_BLOB_BASE_FEE_SCALAR, + 0, + 0, + ]); + + let mut db = InMemoryDB::default(); + let l1_block_contract = db.load_account(L1_BLOCK_CONTRACT).unwrap(); + l1_block_contract + .storage + .insert(L1_BASE_FEE_SLOT, L1_BASE_FEE); + l1_block_contract + .storage + .insert(ECOTONE_L1_BLOB_BASE_FEE_SLOT, L1_BLOB_BASE_FEE); + l1_block_contract + .storage + .insert(ECOTONE_L1_FEE_SCALARS_SLOT, L1_FEE_SCALARS); + + let ctx = Context::op() + .with_db(db) + .with_chain(L1BlockInfo { + l2_block: Some(BLOCK_NUM + U256::from(1)), + ..Default::default() + }) + .with_block(BlockEnv { + number: BLOCK_NUM, ..Default::default() }) + .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::ECOTONE); + + let mut evm = ctx.build_op(); + assert_ne!(evm.ctx().chain().l2_block, Some(BLOCK_NUM)); + + let handler = + OpHandler::<_, EVMError<_, OpTransactionError>, EthFrame>::new(); + handler + .validate_against_state_and_deduct_caller(&mut evm) + .unwrap(); + + assert_eq!( + *evm.ctx().chain(), + L1BlockInfo { + l2_block: Some(BLOCK_NUM), + l1_base_fee: L1_BASE_FEE, + l1_base_fee_scalar: U256::from(L1_BASE_FEE_SCALAR), + l1_blob_base_fee: Some(L1_BLOB_BASE_FEE), + l1_blob_base_fee_scalar: Some(U256::from(L1_BLOB_BASE_FEE_SCALAR)), + empty_ecotone_scalars: false, + l1_fee_overhead: None, + tx_l1_cost: Some(U256::ZERO), + ..Default::default() + } + ); + } + + #[test] + fn test_load_l1_block_info_isthmus_none() { + const BLOCK_NUM: U256 = uint!(100_U256); + const L1_BASE_FEE: U256 = uint!(1_U256); + const L1_BLOB_BASE_FEE: U256 = uint!(2_U256); + const L1_BASE_FEE_SCALAR: u64 = 3; + const L1_BLOB_BASE_FEE_SCALAR: u64 = 4; + const L1_FEE_SCALARS: U256 = U256::from_limbs([ + 0, + (L1_BASE_FEE_SCALAR << (64 - BASE_FEE_SCALAR_OFFSET * 2)) | L1_BLOB_BASE_FEE_SCALAR, + 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]); + + let mut db = InMemoryDB::default(); + let l1_block_contract = db.load_account(L1_BLOCK_CONTRACT).unwrap(); + l1_block_contract + .storage + .insert(L1_BASE_FEE_SLOT, L1_BASE_FEE); + l1_block_contract + .storage + .insert(ECOTONE_L1_BLOB_BASE_FEE_SLOT, L1_BLOB_BASE_FEE); + l1_block_contract + .storage + .insert(ECOTONE_L1_FEE_SCALARS_SLOT, L1_FEE_SCALARS); + l1_block_contract + .storage + .insert(OPERATOR_FEE_SCALARS_SLOT, OPERATOR_FEE); + db.insert_account_info( + Address::ZERO, + AccountInfo { + balance: U256::from(1000), + ..Default::default() + }, + ); + + let ctx = Context::op() + .with_db(db) .with_block(BlockEnv { number: BLOCK_NUM, ..Default::default() @@ -764,7 +1007,7 @@ mod tests { let mut evm = ctx.build_op(); - assert_ne!(evm.ctx().chain().l2_block, BLOCK_NUM); + assert_ne!(evm.ctx().chain().l2_block, Some(BLOCK_NUM)); let handler = OpHandler::<_, EVMError<_, OpTransactionError>, EthFrame>::new(); @@ -775,7 +1018,7 @@ mod tests { assert_eq!( *evm.ctx().chain(), L1BlockInfo { - l2_block: BLOCK_NUM, + l2_block: Some(BLOCK_NUM), l1_base_fee: L1_BASE_FEE, l1_base_fee_scalar: U256::from(L1_BASE_FEE_SCALAR), l1_blob_base_fee: Some(L1_BLOB_BASE_FEE), @@ -785,6 +1028,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), + ..Default::default() } ); } @@ -806,6 +1050,7 @@ mod tests { l1_base_fee: U256::from(1_000), l1_fee_overhead: Some(U256::from(1_000)), l1_base_fee_scalar: U256::from(1_000), + l2_block: Some(U256::from(0)), ..Default::default() }) .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH) @@ -833,7 +1078,7 @@ mod tests { } #[test] - fn test_remove_operator_cost() { + fn test_remove_operator_cost_isthmus() { let caller = Address::ZERO; let mut db = InMemoryDB::default(); db.insert_account_info( @@ -848,6 +1093,7 @@ mod tests { .with_chain(L1BlockInfo { operator_fee_scalar: Some(U256::from(10_000_000)), operator_fee_constant: Some(U256::from(50)), + l2_block: Some(U256::from(0)), ..Default::default() }) .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::ISTHMUS) @@ -862,7 +1108,7 @@ mod tests { let handler = OpHandler::<_, EVMError<_, OpTransactionError>, EthFrame>::new(); - // operator fee cost is operator_fee_scalar * gas_limit / 1e6 + operator_fee_constant + // Under Isthmus the operator fee cost is operator_fee_scalar * gas_limit / 1e6 + operator_fee_constant // 10_000_000 * 10 / 1_000_000 + 50 = 150 handler .validate_against_state_and_deduct_caller(&mut evm) @@ -873,6 +1119,47 @@ mod tests { assert_eq!(account.info.balance, U256::from(1)); } + #[test] + fn test_remove_operator_cost_jovian() { + let caller = Address::ZERO; + let mut db = InMemoryDB::default(); + db.insert_account_info( + caller, + AccountInfo { + balance: U256::from(2_051), + ..Default::default() + }, + ); + let ctx = Context::op() + .with_db(db) + .with_chain(L1BlockInfo { + operator_fee_scalar: Some(U256::from(2)), + operator_fee_constant: Some(U256::from(50)), + l2_block: Some(U256::from(0)), + ..Default::default() + }) + .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::JOVIAN) + .with_tx( + OpTransaction::builder() + .base(TxEnv::builder().gas_limit(10)) + .enveloped_tx(Some(bytes!("FACADE"))) + .build_fill(), + ); + + let mut evm = ctx.build_op(); + let handler = + OpHandler::<_, EVMError<_, OpTransactionError>, EthFrame>::new(); + + // Under Jovian the operator fee cost is operator_fee_scalar * gas_limit * 100 + operator_fee_constant + // 2 * 10 * 100 + 50 = 2_050 + handler + .validate_against_state_and_deduct_caller(&mut evm) + .unwrap(); + + let account = evm.ctx().journal_mut().load_account(caller).unwrap(); + assert_eq!(account.info.balance, U256::from(1)); + } + #[test] fn test_remove_l1_cost_lack_of_funds() { let caller = Address::ZERO; @@ -890,6 +1177,7 @@ mod tests { l1_base_fee: U256::from(1_000), l1_fee_overhead: Some(U256::from(1_000)), l1_base_fee_scalar: U256::from(1_000), + l2_block: Some(U256::from(0)), ..Default::default() }) .modify_cfg_chained(|cfg| cfg.spec = OpSpecId::REGOLITH) @@ -1142,4 +1430,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 cc4ec83000..67f52b6b60 100644 --- a/crates/op-revm/src/l1block.rs +++ b/crates/op-revm/src/l1block.rs @@ -1,12 +1,13 @@ //! Contains the `[L1BlockInfo]` type and its implementation. use crate::{ constants::{ - BASE_FEE_SCALAR_OFFSET, BLOB_BASE_FEE_SCALAR_OFFSET, ECOTONE_L1_BLOB_BASE_FEE_SLOT, - ECOTONE_L1_FEE_SCALARS_SLOT, EMPTY_SCALARS, L1_BASE_FEE_SLOT, L1_BLOCK_CONTRACT, - L1_OVERHEAD_SLOT, L1_SCALAR_SLOT, NON_ZERO_BYTE_COST, OPERATOR_FEE_CONSTANT_OFFSET, + BASE_FEE_SCALAR_OFFSET, BLOB_BASE_FEE_SCALAR_OFFSET, DA_FOOTPRINT_GAS_SCALAR_OFFSET, + DA_FOOTPRINT_GAS_SCALAR_SLOT, ECOTONE_L1_BLOB_BASE_FEE_SLOT, ECOTONE_L1_FEE_SCALARS_SLOT, + EMPTY_SCALARS, L1_BASE_FEE_SLOT, L1_BLOCK_CONTRACT, L1_OVERHEAD_SLOT, L1_SCALAR_SLOT, + 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::{ @@ -33,7 +34,7 @@ use revm::{ pub struct L1BlockInfo { /// The L2 block number. If not same as the one in the context, /// L1BlockInfo is not valid and will be reloaded from the database. - pub l2_block: U256, + pub l2_block: Option, /// The base fee of the L1 origin block. pub l1_base_fee: U256, /// The current L1 fee overhead. None if Ecotone is activated. @@ -48,13 +49,89 @@ pub struct L1BlockInfo { pub operator_fee_scalar: Option, /// The current L1 blob base fee scalar. None if Isthmus is not activated. pub operator_fee_constant: Option, + /// Da footprint gas scalar. Used to set the DA footprint block limit on the L2. Always null prior to the Jovian hardfork. + pub da_footprint_gas_scalar: Option, /// True if Ecotone is activated, but the L1 fee scalars have not yet been set. - pub(crate) empty_ecotone_scalars: bool, + pub empty_ecotone_scalars: bool, /// Last calculated l1 fee cost. Uses as a cache between validation and pre execution stages. pub tx_l1_cost: Option, } impl L1BlockInfo { + /// 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>(); + + // Extract the first 2 bytes directly as a u16 in big-endian format + let bytes = [ + da_footprint_gas_scalar_slot[DA_FOOTPRINT_GAS_SCALAR_OFFSET], + da_footprint_gas_scalar_slot[DA_FOOTPRINT_GAS_SCALAR_OFFSET + 1], + ]; + 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(()) + } + + /// Try to fetch the L1 block info from the database, post-Isthmus. + fn try_fetch_isthmus(&mut self, db: &mut DB) -> Result<(), DB::Error> { + // Post-isthmus L1 block info + let operator_fee_scalars = db + .storage(L1_BLOCK_CONTRACT, OPERATOR_FEE_SCALARS_SLOT)? + .to_be_bytes::<32>(); + + // The `operator_fee_scalar` is stored as a big endian u32 at + // OPERATOR_FEE_SCALAR_OFFSET. + self.operator_fee_scalar = Some(U256::from_be_slice( + operator_fee_scalars[OPERATOR_FEE_SCALAR_OFFSET..OPERATOR_FEE_SCALAR_OFFSET + 4] + .as_ref(), + )); + // The `operator_fee_constant` is stored as a big endian u64 at + // OPERATOR_FEE_CONSTANT_OFFSET. + self.operator_fee_constant = Some(U256::from_be_slice( + operator_fee_scalars[OPERATOR_FEE_CONSTANT_OFFSET..OPERATOR_FEE_CONSTANT_OFFSET + 8] + .as_ref(), + )); + + Ok(()) + } + + /// Try to fetch the L1 block info from the database, post-Ecotone. + fn try_fetch_ecotone(&mut self, db: &mut DB) -> Result<(), DB::Error> { + self.l1_blob_base_fee = Some(db.storage(L1_BLOCK_CONTRACT, ECOTONE_L1_BLOB_BASE_FEE_SLOT)?); + + let l1_fee_scalars = db + .storage(L1_BLOCK_CONTRACT, ECOTONE_L1_FEE_SCALARS_SLOT)? + .to_be_bytes::<32>(); + + self.l1_base_fee_scalar = U256::from_be_slice( + l1_fee_scalars[BASE_FEE_SCALAR_OFFSET..BASE_FEE_SCALAR_OFFSET + 4].as_ref(), + ); + + let l1_blob_base_fee = U256::from_be_slice( + l1_fee_scalars[BLOB_BASE_FEE_SCALAR_OFFSET..BLOB_BASE_FEE_SCALAR_OFFSET + 4].as_ref(), + ); + self.l1_blob_base_fee_scalar = Some(l1_blob_base_fee); + + // Check if the L1 fee scalars are empty. If so, we use the Bedrock cost function. + // The L1 fee overhead is only necessary if `empty_ecotone_scalars` is true, as it was deprecated in Ecotone. + self.empty_ecotone_scalars = l1_blob_base_fee.is_zero() + && l1_fee_scalars[BASE_FEE_SCALAR_OFFSET..BLOB_BASE_FEE_SCALAR_OFFSET + 4] + == EMPTY_SCALARS; + self.l1_fee_overhead = self + .empty_ecotone_scalars + .then(|| db.storage(L1_BLOCK_CONTRACT, L1_OVERHEAD_SLOT)) + .transpose()?; + + Ok(()) + } + /// Try to fetch the L1 block info from the database. pub fn try_fetch( db: &mut DB, @@ -67,102 +144,49 @@ impl L1BlockInfo { let _ = db.basic(L1_BLOCK_CONTRACT)?; } - let l1_base_fee = db.storage(L1_BLOCK_CONTRACT, L1_BASE_FEE_SLOT)?; + let mut out = L1BlockInfo { + l2_block: Some(l2_block), + l1_base_fee: db.storage(L1_BLOCK_CONTRACT, L1_BASE_FEE_SLOT)?, + ..Default::default() + }; + // Post-Ecotone if !spec_id.is_enabled_in(OpSpecId::ECOTONE) { - let l1_fee_overhead = db.storage(L1_BLOCK_CONTRACT, L1_OVERHEAD_SLOT)?; - let l1_fee_scalar = db.storage(L1_BLOCK_CONTRACT, L1_SCALAR_SLOT)?; - - Ok(L1BlockInfo { - l1_base_fee, - l1_fee_overhead: Some(l1_fee_overhead), - l1_base_fee_scalar: l1_fee_scalar, - ..Default::default() - }) - } else { - let l1_blob_base_fee = db.storage(L1_BLOCK_CONTRACT, ECOTONE_L1_BLOB_BASE_FEE_SLOT)?; - let l1_fee_scalars = db - .storage(L1_BLOCK_CONTRACT, ECOTONE_L1_FEE_SCALARS_SLOT)? - .to_be_bytes::<32>(); - - let l1_base_fee_scalar = U256::from_be_slice( - l1_fee_scalars[BASE_FEE_SCALAR_OFFSET..BASE_FEE_SCALAR_OFFSET + 4].as_ref(), - ); - let l1_blob_base_fee_scalar = U256::from_be_slice( - l1_fee_scalars[BLOB_BASE_FEE_SCALAR_OFFSET..BLOB_BASE_FEE_SCALAR_OFFSET + 4] - .as_ref(), - ); - - // Check if the L1 fee scalars are empty. If so, we use the Bedrock cost function. - // The L1 fee overhead is only necessary if `empty_ecotone_scalars` is true, as it was deprecated in Ecotone. - let empty_ecotone_scalars = l1_blob_base_fee.is_zero() - && l1_fee_scalars[BASE_FEE_SCALAR_OFFSET..BLOB_BASE_FEE_SCALAR_OFFSET + 4] - == EMPTY_SCALARS; - let l1_fee_overhead = empty_ecotone_scalars - .then(|| db.storage(L1_BLOCK_CONTRACT, L1_OVERHEAD_SLOT)) - .transpose()?; - - if spec_id.is_enabled_in(OpSpecId::ISTHMUS) { - let operator_fee_scalars = db - .storage(L1_BLOCK_CONTRACT, OPERATOR_FEE_SCALARS_SLOT)? - .to_be_bytes::<32>(); - - // Post-isthmus L1 block info - // The `operator_fee_scalar` is stored as a big endian u32 at - // OPERATOR_FEE_SCALAR_OFFSET. - let operator_fee_scalar = U256::from_be_slice( - operator_fee_scalars - [OPERATOR_FEE_SCALAR_OFFSET..OPERATOR_FEE_SCALAR_OFFSET + 4] - .as_ref(), - ); - // The `operator_fee_constant` is stored as a big endian u64 at - // OPERATOR_FEE_CONSTANT_OFFSET. - let operator_fee_constant = U256::from_be_slice( - operator_fee_scalars - [OPERATOR_FEE_CONSTANT_OFFSET..OPERATOR_FEE_CONSTANT_OFFSET + 8] - .as_ref(), - ); - Ok(L1BlockInfo { - l2_block, - l1_base_fee, - l1_base_fee_scalar, - l1_blob_base_fee: Some(l1_blob_base_fee), - l1_blob_base_fee_scalar: Some(l1_blob_base_fee_scalar), - empty_ecotone_scalars, - l1_fee_overhead, - operator_fee_scalar: Some(operator_fee_scalar), - operator_fee_constant: Some(operator_fee_constant), - tx_l1_cost: None, - }) - } else { - // Pre-isthmus L1 block info - Ok(L1BlockInfo { - l1_base_fee, - l1_base_fee_scalar, - l1_blob_base_fee: Some(l1_blob_base_fee), - l1_blob_base_fee_scalar: Some(l1_blob_base_fee_scalar), - empty_ecotone_scalars, - l1_fee_overhead, - ..Default::default() - }) - } + out.l1_base_fee_scalar = db.storage(L1_BLOCK_CONTRACT, L1_SCALAR_SLOT)?; + out.l1_fee_overhead = Some(db.storage(L1_BLOCK_CONTRACT, L1_OVERHEAD_SLOT)?); + + return Ok(out); + } + + out.try_fetch_ecotone(db)?; + + // Post-Isthmus L1 block info + if spec_id.is_enabled_in(OpSpecId::ISTHMUS) { + out.try_fetch_isthmus(db)?; + } + + // Pre-Jovian + if spec_id.is_enabled_in(OpSpecId::JOVIAN) { + out.try_fetch_jovian(db)?; } + + Ok(out) } /// Calculate the operator fee for executing this transaction. /// /// Introduced in isthmus. Prior to isthmus, the operator fee is always zero. - pub fn operator_fee_charge(&self, input: &[u8], gas_limit: U256) -> U256 { + pub fn operator_fee_charge(&self, input: &[u8], gas_limit: U256, spec_id: OpSpecId) -> U256 { // If the input is a deposit transaction or empty, the default value is zero. - if input.first() == Some(&0x7E) { + if input.is_empty() || input.first() == Some(&0x7E) { return U256::ZERO; } - self.operator_fee_charge_inner(gas_limit) + self.operator_fee_charge_inner(gas_limit, spec_id) } /// Calculate the operator fee for the given `gas`. - fn operator_fee_charge_inner(&self, gas: U256) -> U256 { + fn operator_fee_charge_inner(&self, gas: U256, spec_id: OpSpecId) -> U256 { let operator_fee_scalar = self .operator_fee_scalar .expect("Missing operator fee scalar for isthmus L1 Block"); @@ -170,8 +194,12 @@ impl L1BlockInfo { .operator_fee_constant .expect("Missing operator fee constant for isthmus L1 Block"); - let product = - gas.saturating_mul(operator_fee_scalar) / (U256::from(OPERATOR_FEE_SCALAR_DECIMAL)); + let product = if spec_id.is_enabled_in(OpSpecId::JOVIAN) { + gas.saturating_mul(operator_fee_scalar) + .saturating_mul(U256::from(OPERATOR_FEE_JOVIAN_MULTIPLIER)) + } else { + gas.saturating_mul(operator_fee_scalar) / U256::from(OPERATOR_FEE_SCALAR_DECIMAL) + }; product.saturating_add(operator_fee_constant) } @@ -184,10 +212,12 @@ impl L1BlockInfo { return U256::ZERO; } - let operator_cost_gas_limit = self.operator_fee_charge_inner(U256::from(gas.limit())); - let operator_cost_gas_used = self.operator_fee_charge_inner(U256::from( - gas.limit() - (gas.remaining() + gas.refunded() as u64), - )); + let operator_cost_gas_limit = + self.operator_fee_charge_inner(U256::from(gas.limit()), spec_id); + let operator_cost_gas_used = self.operator_fee_charge_inner( + U256::from(gas.limit() - (gas.remaining() + gas.refunded() as u64)), + spec_id, + ); operator_cost_gas_limit.saturating_sub(operator_cost_gas_used) } @@ -231,6 +261,35 @@ impl L1BlockInfo { self.tx_l1_cost = None; } + /// Calculate additional transaction cost with OpTxTr. + /// + /// Internally calls [`L1BlockInfo::tx_cost`]. + #[track_caller] + pub fn tx_cost_with_tx(&mut self, tx: impl OpTxTr, spec: OpSpecId) -> U256 { + // account for additional cost of l1 fee and operator fee + let enveloped_tx = tx + .enveloped_tx() + .expect("all not deposit tx have enveloped tx") + .clone(); + let gas_limit = U256::from(tx.gas_limit()); + 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 { @@ -573,6 +632,25 @@ mod tests { assert_eq!(l1_fee, expected_l1_fee) } + #[test] + fn test_operator_fee_charge_formulas() { + let l1_block_info = L1BlockInfo { + operator_fee_scalar: Some(U256::from(1_000u64)), + operator_fee_constant: Some(U256::from(10u64)), + ..Default::default() + }; + + let input = [0x01u8]; + + let isthmus_fee = + l1_block_info.operator_fee_charge(&input, U256::from(1_000u64), OpSpecId::ISTHMUS); + assert_eq!(isthmus_fee, U256::from(11u64)); + + let jovian_fee = + l1_block_info.operator_fee_charge(&input, U256::from(1_000u64), OpSpecId::JOVIAN); + assert_eq!(jovian_fee, U256::from(100_000_010u64)); + } + #[test] fn test_operator_fee_refund() { let gas = Gas::new(50000); diff --git a/crates/op-revm/src/precompiles.rs b/crates/op-revm/src/precompiles.rs index 579ceb30c8..025a6afbad 100644 --- a/crates/op-revm/src/precompiles.rs +++ b/crates/op-revm/src/precompiles.rs @@ -4,7 +4,7 @@ use revm::{ context::Cfg, context_interface::ContextTr, handler::{EthPrecompiles, PrecompileProvider}, - interpreter::{InputsImpl, InterpreterResult}, + interpreter::{CallInputs, InterpreterResult}, precompile::{ self, bn254, secp256r1, Precompile, PrecompileError, PrecompileId, PrecompileResult, Precompiles, @@ -34,7 +34,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 => isthmus(), + OpSpecId::ISTHMUS => isthmus(), + OpSpecId::INTEROP | OpSpecId::OSAKA | OpSpecId::JOVIAN => jovian(), }; Self { @@ -92,6 +93,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>, @@ -111,13 +140,9 @@ where fn run( &mut self, context: &mut CTX, - address: &Address, - inputs: &InputsImpl, - is_static: bool, - gas_limit: u64, + inputs: &CallInputs, ) -> Result, String> { - self.inner - .run(context, address, inputs, is_static, gas_limit) + self.inner.run(context, inputs) } #[inline] @@ -133,7 +158,7 @@ where impl Default for OpPrecompiles { fn default() -> Self { - Self::new_with_spec(OpSpecId::ISTHMUS) + Self::new_with_spec(OpSpecId::JOVIAN) } } @@ -144,11 +169,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); } @@ -159,6 +187,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. @@ -171,33 +221,67 @@ pub mod bls12_381 { /// 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".to_string(), + )); + } + 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".to_string(), )); } 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(), @@ -206,8 +290,18 @@ pub mod bls12_381 { 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".to_string(), + )); + } + 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(), @@ -215,18 +309,30 @@ pub mod bls12_381 { } 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".to_string(), + )); + } + precompile::bls12_381::pairing::pairing(input, gas_limit) + } } #[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; @@ -252,7 +358,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 @@ -265,20 +371,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 @@ -300,6 +477,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()) @@ -317,7 +511,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")) @@ -328,7 +534,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")) @@ -339,7 +556,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 c0e764c598..e192847a0f 100644 --- a/crates/op-revm/src/spec.rs +++ b/crates/op-revm/src/spec.rs @@ -25,6 +25,8 @@ pub enum OpSpecId { /// Isthmus spec id. #[default] ISTHMUS, + /// Jovian spec id. + JOVIAN, /// Interop spec id. INTEROP, /// Osaka spec id. @@ -38,7 +40,7 @@ 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::ISTHMUS | Self::JOVIAN | Self::INTEROP => SpecId::PRAGUE, Self::OSAKA => SpecId::OSAKA, } } @@ -68,6 +70,7 @@ impl FromStr for OpSpecId { name::GRANITE => Ok(OpSpecId::GRANITE), name::HOLOCENE => Ok(OpSpecId::HOLOCENE), name::ISTHMUS => Ok(OpSpecId::ISTHMUS), + name::JOVIAN => Ok(OpSpecId::JOVIAN), name::INTEROP => Ok(OpSpecId::INTEROP), eth_name::OSAKA => Ok(OpSpecId::OSAKA), _ => Err(UnknownHardfork), @@ -86,6 +89,7 @@ impl From for &'static str { OpSpecId::GRANITE => name::GRANITE, OpSpecId::HOLOCENE => name::HOLOCENE, OpSpecId::ISTHMUS => name::ISTHMUS, + OpSpecId::JOVIAN => name::JOVIAN, OpSpecId::INTEROP => name::INTEROP, OpSpecId::OSAKA => eth_name::OSAKA, } @@ -110,6 +114,8 @@ pub mod name { pub const HOLOCENE: &str = "Holocene"; /// Isthmus spec name. pub const ISTHMUS: &str = "Isthmus"; + /// Jovian spec name. + pub const JOVIAN: &str = "Jovian"; /// Interop spec name. pub const INTEROP: &str = "Interop"; } @@ -188,6 +194,24 @@ mod tests { (OpSpecId::FJORD, true), ], ), + ( + OpSpecId::JOVIAN, + vec![ + (SpecId::PRAGUE, true), + (SpecId::SHANGHAI, true), + (SpecId::CANCUN, true), + (SpecId::MERGE, true), + ], + vec![ + (OpSpecId::BEDROCK, true), + (OpSpecId::REGOLITH, true), + (OpSpecId::CANYON, true), + (OpSpecId::ECOTONE, true), + (OpSpecId::FJORD, true), + (OpSpecId::HOLOCENE, true), + (OpSpecId::ISTHMUS, true), + ], + ), ]; for (op_spec, eth_tests, op_tests) in test_cases { diff --git a/crates/op-revm/src/transaction/abstraction.rs b/crates/op-revm/src/transaction/abstraction.rs index 2043f1ca0a..d0179a7807 100644 --- a/crates/op-revm/src/transaction/abstraction.rs +++ b/crates/op-revm/src/transaction/abstraction.rs @@ -275,7 +275,10 @@ impl OpTransactionBuilder { /// This is useful for testing and debugging where it is not necessary to /// have full [`OpTransaction`] instance. /// - /// If the source hash is not [`B256::ZERO`], set the transaction type to deposit and remove the enveloped transaction. + /// If the transaction is a deposit (either `tx_type == DEPOSIT_TRANSACTION_TYPE` or + /// `source_hash != B256::ZERO`), set the transaction type accordingly and ensure the + /// `enveloped_tx` is removed (`None`). For non-deposit transactions, ensure + /// `enveloped_tx` is set. pub fn build_fill(mut self) -> OpTransaction { let tx_type = self.base.get_tx_type(); if tx_type.is_some() { @@ -284,6 +287,8 @@ impl OpTransactionBuilder { if self.deposit.source_hash == B256::ZERO { self.deposit.source_hash = B256::from([1u8; 32]); } + // deposit transactions should not carry enveloped bytes + self.enveloped_tx = None; } else { // enveloped is required for non-deposit transactions self.enveloped_tx = Some(vec![0x00].into()); @@ -291,6 +296,8 @@ impl OpTransactionBuilder { } else if self.deposit.source_hash != B256::ZERO { // if type is not set and source hash is set, set the transaction type to deposit self.base = self.base.tx_type(Some(DEPOSIT_TRANSACTION_TYPE)); + // deposit transactions should not carry enveloped bytes + self.enveloped_tx = None; } else if self.enveloped_tx.is_none() { // if type is not set and source hash is not set, set the enveloped transaction to something. self.enveloped_tx = Some(vec![0x00].into()); diff --git a/crates/op-revm/src/transaction/deposit.rs b/crates/op-revm/src/transaction/deposit.rs index 1d51fb1224..509a0fea6a 100644 --- a/crates/op-revm/src/transaction/deposit.rs +++ b/crates/op-revm/src/transaction/deposit.rs @@ -33,17 +33,20 @@ mod tests { use revm::primitives::b256; #[test] - fn serialize_json_deposit_tx_parts() { + fn serialize_deserialize_json_deposit_tx_parts() { + let parts = DepositTransactionParts::new( + b256!("0xe927a1448525fb5d32cb50ee1408461a945ba6c39bd5cf5621407d500ecc8de9"), + Some(0x34), + false, + ); let response = r#"{"source_hash":"0xe927a1448525fb5d32cb50ee1408461a945ba6c39bd5cf5621407d500ecc8de9","mint":52,"is_system_transaction":false}"#; + // serialize + let json = serde_json::to_string(&parts).unwrap(); + assert_eq!(json.as_str(), response); + + // deserialize let deposit_tx_parts: DepositTransactionParts = serde_json::from_str(response).unwrap(); - assert_eq!( - deposit_tx_parts, - DepositTransactionParts::new( - b256!("0xe927a1448525fb5d32cb50ee1408461a945ba6c39bd5cf5621407d500ecc8de9"), - Some(0x34), - false, - ) - ); + assert_eq!(deposit_tx_parts, parts); } } 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 e58f0a4933..77df9e83d3 100644 --- a/crates/precompile/CHANGELOG.md +++ b/crates/precompile/CHANGELOG.md @@ -7,6 +7,56 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [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 + +- bump minor versions ([#3078](https://github.com/bluealloy/revm/pull/3078)) + + +## [28.1.0](https://github.com/bluealloy/revm/compare/revm-precompile-v28.0.1...revm-precompile-v28.1.0) - 2025-10-09 + +## [28.0.1](https://github.com/bluealloy/revm/compare/revm-precompile-v28.0.0...revm-precompile-v28.0.1) - 2025-10-09 + +### Fixed + +- *(kzg/blst)* use uncompress for compressed G1/G2 inputs ([#3067](https://github.com/bluealloy/revm/pull/3067)) + +## [28.0.0](https://github.com/bluealloy/revm/compare/revm-precompile-v27.0.0...revm-precompile-v28.0.0) - 2025-10-07 + +### Added + +- [**breaking**] Remove kzg-rs ([#2909](https://github.com/bluealloy/revm/pull/2909)) + +### Fixed + +- racecondition return on install_crypto fn ([#2997](https://github.com/bluealloy/revm/pull/2997)) +- Apply spelling corrections from PRs #2926, #2915, #2908 ([#2978](https://github.com/bluealloy/revm/pull/2978)) + +### Other + +- add boundless ([#3043](https://github.com/bluealloy/revm/pull/3043)) +- typo imputs ([#3031](https://github.com/bluealloy/revm/pull/3031)) +- add display for precompileid ([#3018](https://github.com/bluealloy/revm/pull/3018)) +- Remove libsecp256k1 parity lib ([#2954](https://github.com/bluealloy/revm/pull/2954)) +- add SECURITY.md ([#2956](https://github.com/bluealloy/revm/pull/2956)) +- add amsterdam in spec id ([#2934](https://github.com/bluealloy/revm/pull/2934)) +- cargo update ([#2930](https://github.com/bluealloy/revm/pull/2930)) +- *(precompile)* add new specific `PrecompileError` variants ([#2907](https://github.com/bluealloy/revm/pull/2907)) +- add Precompil::into_precompile ([#2913](https://github.com/bluealloy/revm/pull/2913)) + ## [27.0.0](https://github.com/bluealloy/revm/compare/revm-precompile-v26.0.1...revm-precompile-v27.0.0) - 2025-08-23 ### Added @@ -51,8 +101,6 @@ 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). -## [Unreleased] - ## [25.0.0](https://github.com/bluealloy/revm/compare/revm-precompile-v24.0.1...revm-precompile-v25.0.0) - 2025-07-23 ### Added diff --git a/crates/precompile/Cargo.toml b/crates/precompile/Cargo.toml index e5a9ff5a86..ec0ce2a1eb 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 = "27.0.0" +version = "29.0.1" authors.workspace = true edition.workspace = true keywords.workspace = true @@ -34,9 +34,6 @@ secp256k1 = { workspace = true, features = [ "rand", "global-context", ], optional = true } -libsecp256k1 = { workspace = true, features = [ - "static-context", -], optional = true } # SHA2-256 and RIPEMD-160 sha2.workspace = true @@ -56,8 +53,6 @@ c-kzg = { workspace = true, optional = true, features = [ "ethereum_kzg_settings", ] } -# Optionally use `kzg-rs` for a pure Rust implementation of KZG point evaluation. -kzg-rs = { workspace = true, optional = true } # Use the BLS12-381 implementation of blst for EIP2537 blst = { workspace = true, optional = true } @@ -87,7 +82,6 @@ std = [ "sha2/std", "c-kzg?/std", "secp256k1?/std", - "libsecp256k1?/std", "ark-bn254/std", "ark-bls12-381/std", "aurora-engine-modexp/std", @@ -105,8 +99,6 @@ asm-sha2 = ["sha2/asm"] # Enables the c-kzg point evaluation precompile. Enabled by default as most stable/used implementation. c-kzg = ["dep:c-kzg"] -# `kzg-rs` is not audited but useful for `no_std` environment, use it with causing and default to `c-kzg` if possible. -kzg-rs = ["dep:kzg-rs"] # Compile in portable mode, without ISA extensions. # Binary can be executed on all systems. @@ -116,7 +108,6 @@ portable = ["c-kzg?/portable", "blst?/portable"] # The problem that `secp256k1` has is it fails to build for `wasm` target on Windows and Mac as it is c lib. # In Linux it passes. If you don't require to build wasm on win/mac, it is safe to use it and it is enabled by default. secp256k1 = ["dep:secp256k1"] -libsecp256k1 = ["dep:libsecp256k1"] # Enables the blst implementation of the BLS12-381 precompile. blst = ["dep:blst"] diff --git a/crates/precompile/bench/ecrecover.rs b/crates/precompile/bench/ecrecover.rs index d5b62ffc83..d40e2c5712 100644 --- a/crates/precompile/bench/ecrecover.rs +++ b/crates/precompile/bench/ecrecover.rs @@ -11,7 +11,7 @@ pub fn add_benches(group: &mut BenchmarkGroup<'_, M>) { let hash = keccak256(data); let secret_key = SecretKey::new(&mut secp256k1::rand::rng()); - let message = Message::from_digest_slice(&hash[..]).unwrap(); + let message = Message::from_digest(hash.0); let s = SECP256K1.sign_ecdsa_recoverable(message, &secret_key); let (rec_id, data) = s.serialize_compact(); let rec_id = i32::from(rec_id) as u8 + 27; diff --git a/crates/precompile/src/bls12_381/arkworks.rs b/crates/precompile/src/bls12_381/arkworks.rs index 4ca5573cbd..dea815f156 100644 --- a/crates/precompile/src/bls12_381/arkworks.rs +++ b/crates/precompile/src/bls12_381/arkworks.rs @@ -13,7 +13,7 @@ use ark_ec::{ use ark_ff::{One, PrimeField, Zero}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; -use std::{string::ToString, vec::Vec}; +use std::vec::Vec; /// Reads a single `Fp` field element from the input slice. /// @@ -34,8 +34,7 @@ fn read_fp(input_be: &[u8]) -> Result { // Reverse in-place to convert from big-endian to little-endian. input_le.reverse(); - Fq::deserialize_uncompressed(&input_le[..]) - .map_err(|_| PrecompileError::Other("non-canonical fp value".to_string())) + Fq::deserialize_uncompressed(&input_le[..]).map_err(|_| PrecompileError::NonCanonicalFp) } /// Encodes an `Fp` field element into a big-endian byte array. @@ -80,9 +79,7 @@ fn new_g1_point_no_subgroup_check(px: Fq, py: Fq) -> Result Result Result Result { let point = read_g1_no_subgroup_check(x, y)?; if !point.is_in_correct_subgroup_assuming_on_curve() { - return Err(PrecompileError::Other( - "Element not in the correct subgroup".to_string(), - )); + return Err(PrecompileError::Bls12381G1NotInSubgroup); } Ok(point) } @@ -186,9 +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::Other( - "Element not in the correct subgroup".to_string(), - )); + return Err(PrecompileError::Bls12381G1NotInSubgroup); } Ok(point) } @@ -244,10 +235,7 @@ fn encode_g2_point(input: &G2Affine) -> [u8; G2_LENGTH] { #[inline] fn read_scalar(input: &[u8]) -> Result { if input.len() != SCALAR_LENGTH { - return Err(PrecompileError::Other(format!( - "Input should be {SCALAR_LENGTH} bytes, was {}", - input.len() - ))); + return Err(PrecompileError::Bls12381ScalarInputLength); } Ok(Fr::from_be_bytes_mod_order(input)) diff --git a/crates/precompile/src/bls12_381/blst.rs b/crates/precompile/src/bls12_381/blst.rs index 747c695f4a..d20c7af561 100644 --- a/crates/precompile/src/bls12_381/blst.rs +++ b/crates/precompile/src/bls12_381/blst.rs @@ -14,7 +14,6 @@ use blst::{ blst_p2_affine, blst_p2_affine_in_g2, blst_p2_affine_on_curve, blst_p2_from_affine, blst_p2_mult, blst_p2_to_affine, blst_scalar, blst_scalar_from_bendian, MultiPoint, }; -use std::string::ToString; use std::vec::Vec; // Big-endian non-Montgomery form. @@ -382,9 +381,7 @@ fn decode_g1_on_curve( // // SAFETY: Out is a blst value. if unsafe { !blst_p1_affine_on_curve(&out) } { - return Err(PrecompileError::Other( - "Element not on G1 curve".to_string(), - )); + return Err(PrecompileError::Bls12381G1NotOnCurve); } Ok(out) @@ -436,7 +433,7 @@ fn _extract_g1_input( // As endomorphism acceleration requires input on the correct subgroup, implementers MAY // use endomorphism acceleration. if unsafe { !blst_p1_affine_in_g1(&out) } { - return Err(PrecompileError::Other("Element not in G1".to_string())); + return Err(PrecompileError::Bls12381G1NotInSubgroup); } } Ok(out) @@ -481,9 +478,7 @@ fn decode_g2_on_curve( // // SAFETY: Out is a blst value. if unsafe { !blst_p2_affine_on_curve(&out) } { - return Err(PrecompileError::Other( - "Element not on G2 curve".to_string(), - )); + return Err(PrecompileError::Bls12381G2NotOnCurve); } Ok(out) @@ -559,7 +554,7 @@ fn _extract_g2_input( // As endomorphism acceleration requires input on the correct subgroup, implementers MAY // use endomorphism acceleration. if unsafe { !blst_p2_affine_in_g2(&out) } { - return Err(PrecompileError::Other("Element not in G2".to_string())); + return Err(PrecompileError::Bls12381G2NotInSubgroup); } } Ok(out) @@ -571,7 +566,7 @@ fn _extract_g2_input( /// Note: The field element is expected to be in big endian format. fn read_fp(input: &[u8; FP_LENGTH]) -> Result { if !is_valid_be(input) { - return Err(PrecompileError::Other("non-canonical fp value".to_string())); + return Err(PrecompileError::NonCanonicalFp); } let mut fp = blst_fp::default(); // SAFETY: `input` has fixed length, and `fp` is a blst value. @@ -595,10 +590,7 @@ fn read_fp(input: &[u8; FP_LENGTH]) -> Result { /// `q`. fn read_scalar(input: &[u8]) -> Result { if input.len() != SCALAR_LENGTH { - return Err(PrecompileError::Other(format!( - "Input should be {SCALAR_LENGTH} bytes, was {}", - input.len() - ))); + return Err(PrecompileError::Bls12381ScalarInputLength); } let mut out = blst_scalar::default(); diff --git a/crates/precompile/src/bls12_381/g1_add.rs b/crates/precompile/src/bls12_381/g1_add.rs index 3b2dd2e59d..07e0112321 100644 --- a/crates/precompile/src/bls12_381/g1_add.rs +++ b/crates/precompile/src/bls12_381/g1_add.rs @@ -22,10 +22,7 @@ pub fn g1_add(input: &[u8], gas_limit: u64) -> PrecompileResult { } if input.len() != G1_ADD_INPUT_LENGTH { - return Err(PrecompileError::Other(format!( - "G1ADD input should be {G1_ADD_INPUT_LENGTH} bytes, was {}", - input.len() - ))); + return Err(PrecompileError::Bls12381G1AddInputLength); } // Extract coordinates from padded input diff --git a/crates/precompile/src/bls12_381/g1_msm.rs b/crates/precompile/src/bls12_381/g1_msm.rs index c4840cfe1c..7a2d07505c 100644 --- a/crates/precompile/src/bls12_381/g1_msm.rs +++ b/crates/precompile/src/bls12_381/g1_msm.rs @@ -25,9 +25,7 @@ pub const PRECOMPILE: Precompile = pub fn g1_msm(input: &[u8], gas_limit: u64) -> PrecompileResult { let input_len = input.len(); if input_len == 0 || !input_len.is_multiple_of(G1_MSM_INPUT_LENGTH) { - return Err(PrecompileError::Other(format!( - "G1MSM input length should be multiple of {G1_MSM_INPUT_LENGTH}, was {input_len}", - ))); + return Err(PrecompileError::Bls12381G1MsmInputLength); } let k = input_len / G1_MSM_INPUT_LENGTH; @@ -66,11 +64,6 @@ mod test { fn bls_g1multiexp_g1_not_on_curve_but_in_subgroup() { let input = Bytes::from(hex!("000000000000000000000000000000000a2833e497b38ee3ca5c62828bf4887a9f940c9e426c7890a759c20f248c23a7210d2432f4c98a514e524b5184a0ddac00000000000000000000000000000000150772d56bf9509469f9ebcd6e47570429fd31b0e262b66d512e245c38ec37255529f2271fd70066473e393a8bead0c30000000000000000000000000000000000000000000000000000000000000000")); let fail = g1_msm(&input, G1_MSM_BASE_GAS_FEE); - assert_eq!( - fail, - Err(PrecompileError::Other( - "Element not on G1 curve".to_string() - )) - ); + assert_eq!(fail, Err(PrecompileError::Bls12381G1NotOnCurve)); } } diff --git a/crates/precompile/src/bls12_381/g2_add.rs b/crates/precompile/src/bls12_381/g2_add.rs index d3af54f4c3..63cb6b6a51 100644 --- a/crates/precompile/src/bls12_381/g2_add.rs +++ b/crates/precompile/src/bls12_381/g2_add.rs @@ -23,10 +23,7 @@ pub fn g2_add(input: &[u8], gas_limit: u64) -> PrecompileResult { } if input.len() != G2_ADD_INPUT_LENGTH { - return Err(PrecompileError::Other(format!( - "G2ADD input should be {G2_ADD_INPUT_LENGTH} bytes, was {}", - input.len() - ))); + return Err(PrecompileError::Bls12381G2AddInputLength); } // Extract coordinates from padded input diff --git a/crates/precompile/src/bls12_381/g2_msm.rs b/crates/precompile/src/bls12_381/g2_msm.rs index eaa2c4f4f7..a37a7df623 100644 --- a/crates/precompile/src/bls12_381/g2_msm.rs +++ b/crates/precompile/src/bls12_381/g2_msm.rs @@ -24,9 +24,7 @@ pub const PRECOMPILE: Precompile = pub fn g2_msm(input: &[u8], gas_limit: u64) -> PrecompileResult { let input_len = input.len(); if input_len == 0 || !input_len.is_multiple_of(G2_MSM_INPUT_LENGTH) { - return Err(PrecompileError::Other(format!( - "G2MSM input length should be multiple of {G2_MSM_INPUT_LENGTH}, was {input_len}", - ))); + return Err(PrecompileError::Bls12381G2MsmInputLength); } let k = input_len / G2_MSM_INPUT_LENGTH; 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 847f9638d6..f16cb8cb99 100644 --- a/crates/precompile/src/bls12_381/map_fp2_to_g2.rs +++ b/crates/precompile/src/bls12_381/map_fp2_to_g2.rs @@ -24,10 +24,7 @@ pub fn map_fp2_to_g2(input: &[u8], gas_limit: u64) -> PrecompileResult { } if input.len() != PADDED_FP2_LENGTH { - return Err(PrecompileError::Other(format!( - "MAP_FP2_TO_G2 input should be {PADDED_FP2_LENGTH} bytes, was {}", - input.len() - ))); + return Err(PrecompileError::Bls12381MapFp2ToG2InputLength); } let input_p0_x = remove_fp_padding(&input[..PADDED_FP_LENGTH])?; 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 64a8f11393..1babc3ae8a 100644 --- a/crates/precompile/src/bls12_381/map_fp_to_g1.rs +++ b/crates/precompile/src/bls12_381/map_fp_to_g1.rs @@ -21,10 +21,7 @@ pub fn map_fp_to_g1(input: &[u8], gas_limit: u64) -> PrecompileResult { } if input.len() != PADDED_FP_LENGTH { - return Err(PrecompileError::Other(format!( - "MAP_FP_TO_G1 input should be {PADDED_FP_LENGTH} bytes, was {}", - input.len() - ))); + return Err(PrecompileError::Bls12381MapFpToG1InputLength); } let input_p0 = remove_fp_padding(input)?; @@ -49,9 +46,6 @@ mod test { fn sanity_test() { let input = Bytes::from(hex!("000000000000000000000000000000006900000000000000636f6e7472616374595a603f343061cd305a03f40239f5ffff31818185c136bc2595f2aa18e08f17")); let fail = map_fp_to_g1(&input, MAP_FP_TO_G1_BASE_GAS_FEE); - assert_eq!( - fail, - Err(PrecompileError::Other("non-canonical fp value".to_string())) - ); + assert_eq!(fail, Err(PrecompileError::NonCanonicalFp)); } } diff --git a/crates/precompile/src/bls12_381/pairing.rs b/crates/precompile/src/bls12_381/pairing.rs index 4cf4c3f5b5..654d0865f9 100644 --- a/crates/precompile/src/bls12_381/pairing.rs +++ b/crates/precompile/src/bls12_381/pairing.rs @@ -30,9 +30,7 @@ pub const PRECOMPILE: Precompile = pub fn pairing(input: &[u8], gas_limit: u64) -> PrecompileResult { let input_len = input.len(); if input_len == 0 || !input_len.is_multiple_of(PAIRING_INPUT_LENGTH) { - return Err(PrecompileError::Other(format!( - "Pairing input length should be multiple of {PAIRING_INPUT_LENGTH}, was {input_len}" - ))); + return Err(PrecompileError::Bls12381PairingInputLength); } let k = input_len / PAIRING_INPUT_LENGTH; diff --git a/crates/precompile/src/bls12_381/utils.rs b/crates/precompile/src/bls12_381/utils.rs index c53f0cae5e..335ceda278 100644 --- a/crates/precompile/src/bls12_381/utils.rs +++ b/crates/precompile/src/bls12_381/utils.rs @@ -7,16 +7,11 @@ 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> { if input.len() != PADDED_FP_LENGTH { - return Err(PrecompileError::Other(format!( - "Padded input should be {PADDED_FP_LENGTH} bytes, was {}", - input.len() - ))); + return Err(PrecompileError::Bls12381FpPaddingLength); } let (padding, unpadded) = input.split_at(FP_PAD_BY); if !padding.iter().all(|&x| x == 0) { - return Err(PrecompileError::Other(format!( - "{FP_PAD_BY} top bytes of input are not zero", - ))); + return Err(PrecompileError::Bls12381FpPaddingInvalid); } Ok(unpadded.try_into().unwrap()) } @@ -24,10 +19,7 @@ pub(super) fn remove_fp_padding(input: &[u8]) -> Result<&[u8; FP_LENGTH], Precom /// encoded G1 element. pub(super) fn remove_g1_padding(input: &[u8]) -> Result<[&[u8; FP_LENGTH]; 2], PrecompileError> { if input.len() != PADDED_G1_LENGTH { - return Err(PrecompileError::Other(format!( - "Input should be {PADDED_G1_LENGTH} bytes, was {}", - input.len() - ))); + return Err(PrecompileError::Bls12381G1PaddingLength); } let x = remove_fp_padding(&input[..PADDED_FP_LENGTH])?; @@ -39,10 +31,7 @@ pub(super) fn remove_g1_padding(input: &[u8]) -> Result<[&[u8; FP_LENGTH]; 2], P /// encoded G2 element. pub(super) fn remove_g2_padding(input: &[u8]) -> Result<[&[u8; FP_LENGTH]; 4], PrecompileError> { if input.len() != PADDED_G2_LENGTH { - return Err(PrecompileError::Other(format!( - "Input should be {PADDED_G2_LENGTH} bytes, was {}", - input.len() - ))); + return Err(PrecompileError::Bls12381G2PaddingLength); } let mut input_fps = [&[0; FP_LENGTH]; 4]; diff --git a/crates/precompile/src/id.rs b/crates/precompile/src/id.rs index 49c8259fff..8c4077fee1 100644 --- a/crates/precompile/src/id.rs +++ b/crates/precompile/src/id.rs @@ -1,6 +1,5 @@ use std::borrow::Cow; - -use primitives::{address, Address}; +use std::fmt; use crate::{Precompile, PrecompileSpecId}; @@ -57,32 +56,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 { @@ -179,3 +152,9 @@ impl PrecompileId { Some(precompile) } } + +impl fmt::Display for PrecompileId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.name()) + } +} diff --git a/crates/precompile/src/interface.rs b/crates/precompile/src/interface.rs index cac543fffd..f05cd83bf4 100644 --- a/crates/precompile/src/interface.rs +++ b/crates/precompile/src/interface.rs @@ -11,12 +11,7 @@ static CRYPTO: OnceLock> = OnceLock::new(); /// Install a custom crypto provider globally. pub fn install_crypto(crypto: C) -> bool { - if CRYPTO.get().is_some() { - return false; - } - - CRYPTO.get_or_init(|| Box::new(crypto)); - true + CRYPTO.set(Box::new(crypto)).is_ok() } /// Get the installed crypto provider, or the default if none is installed. @@ -115,7 +110,7 @@ pub trait Crypto: Send + Sync + Debug { msg: &[u8; 32], ) -> Result<[u8; 32], PrecompileError> { crate::secp256k1::ecrecover_bytes(*sig, recid, *msg) - .ok_or_else(|| PrecompileError::other("ecrecover failed")) + .ok_or(PrecompileError::Secp256k1RecoverFailed) } /// Modular exponentiation. @@ -230,6 +225,50 @@ pub enum PrecompileError { BlobMismatchedVersion, /// The proof verification failed BlobVerifyKzgProofFailed, + /// Non-canonical field element + NonCanonicalFp, + /// BLS12-381 G1 point not on curve + Bls12381G1NotOnCurve, + /// BLS12-381 G1 point not in correct subgroup + Bls12381G1NotInSubgroup, + /// BLS12-381 G2 point not on curve + Bls12381G2NotOnCurve, + /// BLS12-381 G2 point not in correct subgroup + Bls12381G2NotInSubgroup, + /// BLS12-381 scalar input length error + Bls12381ScalarInputLength, + /// BLS12-381 G1 add input length error + Bls12381G1AddInputLength, + /// BLS12-381 G1 msm input length error + Bls12381G1MsmInputLength, + /// BLS12-381 G2 add input length error + Bls12381G2AddInputLength, + /// BLS12-381 G2 msm input length error + Bls12381G2MsmInputLength, + /// BLS12-381 pairing input length error + Bls12381PairingInputLength, + /// BLS12-381 map fp to g1 input length error + Bls12381MapFpToG1InputLength, + /// BLS12-381 map fp2 to g2 input length error + Bls12381MapFp2ToG2InputLength, + /// BLS12-381 padding error + Bls12381FpPaddingInvalid, + /// BLS12-381 fp padding length error + Bls12381FpPaddingLength, + /// BLS12-381 g1 padding length error + Bls12381G1PaddingLength, + /// BLS12-381 g2 padding length error + Bls12381G2PaddingLength, + /// KZG invalid G1 point + KzgInvalidG1Point, + /// KZG G1 point not on curve + KzgG1PointNotOnCurve, + /// KZG G1 point not in correct subgroup + KzgG1PointNotInSubgroup, + /// KZG input length error + KzgInvalidInputLength, + /// secp256k1 ecrecover failed + Secp256k1RecoverFailed, /// Fatal error with a custom error message Fatal(String), /// Catch-all variant for other errors @@ -266,6 +305,28 @@ impl fmt::Display for PrecompileError { Self::BlobInvalidInputLength => "invalid blob input length", Self::BlobMismatchedVersion => "mismatched blob version", Self::BlobVerifyKzgProofFailed => "verifying blob kzg proof failed", + Self::NonCanonicalFp => "non-canonical field element", + Self::Bls12381G1NotOnCurve => "bls12-381 g1 point not on curve", + Self::Bls12381G1NotInSubgroup => "bls12-381 g1 point not in correct subgroup", + Self::Bls12381G2NotOnCurve => "bls12-381 g2 point not on curve", + Self::Bls12381G2NotInSubgroup => "bls12-381 g2 point not in correct subgroup", + Self::Bls12381ScalarInputLength => "bls12-381 scalar input length error", + Self::Bls12381G1AddInputLength => "bls12-381 g1 add input length error", + Self::Bls12381G1MsmInputLength => "bls12-381 g1 msm input length error", + Self::Bls12381G2AddInputLength => "bls12-381 g2 add input length error", + Self::Bls12381G2MsmInputLength => "bls12-381 g2 msm input length error", + Self::Bls12381PairingInputLength => "bls12-381 pairing input length error", + Self::Bls12381MapFpToG1InputLength => "bls12-381 map fp to g1 input length error", + Self::Bls12381MapFp2ToG2InputLength => "bls12-381 map fp2 to g2 input length error", + Self::Bls12381FpPaddingInvalid => "bls12-381 fp 64 top bytes of input are not zero", + Self::Bls12381FpPaddingLength => "bls12-381 fp padding length error", + Self::Bls12381G1PaddingLength => "bls12-381 g1 padding length error", + Self::Bls12381G2PaddingLength => "bls12-381 g2 padding length error", + Self::KzgInvalidG1Point => "kzg invalid g1 point", + Self::KzgG1PointNotOnCurve => "kzg g1 point not on curve", + Self::KzgG1PointNotInSubgroup => "kzg g1 point not in correct subgroup", + Self::KzgInvalidInputLength => "kzg invalid input length", + Self::Secp256k1RecoverFailed => "secp256k1 signature recovery failed", Self::Fatal(s) => s, Self::Other(s) => s, }; diff --git a/crates/precompile/src/kzg_point_evaluation.rs b/crates/precompile/src/kzg_point_evaluation.rs index 9bec2c322c..41ebfa7074 100644 --- a/crates/precompile/src/kzg_point_evaluation.rs +++ b/crates/precompile/src/kzg_point_evaluation.rs @@ -90,14 +90,6 @@ pub fn verify_kzg_proof( let kzg_settings = c_kzg::ethereum_kzg_settings(8); kzg_settings.verify_kzg_proof(as_bytes48(commitment), as_bytes32(z), as_bytes32(y), as_bytes48(proof)).unwrap_or(false) - } else if #[cfg(feature = "kzg-rs")] { - use kzg_rs::{Bytes48, Bytes32, KzgProof}; - let env = kzg_rs::EnvKzgSettings::default(); - let as_bytes48 = |bytes: &[u8; 48]| -> &Bytes48 { unsafe { &*bytes.as_ptr().cast() } }; - let as_bytes32 = |bytes: &[u8; 32]| -> &Bytes32 { unsafe { &*bytes.as_ptr().cast() } }; - - let kzg_settings = env.get(); - KzgProof::verify_kzg_proof(as_bytes48(commitment), as_bytes32(z), as_bytes32(y), as_bytes48(proof), kzg_settings).unwrap_or(false) } else if #[cfg(feature = "blst")] { blst::verify_kzg_proof(commitment, z, y, proof) } else { diff --git a/crates/precompile/src/kzg_point_evaluation/arkworks.rs b/crates/precompile/src/kzg_point_evaluation/arkworks.rs index 2f23d2481e..fa30382100 100644 --- a/crates/precompile/src/kzg_point_evaluation/arkworks.rs +++ b/crates/precompile/src/kzg_point_evaluation/arkworks.rs @@ -7,7 +7,6 @@ use ark_ec::{AffineRepr, CurveGroup}; use ark_ff::{BigInteger, PrimeField}; use ark_serialize::CanonicalDeserialize; use core::ops::Neg; -use std::string::ToString; /// Verify KZG proof using BLS12-381 implementation. /// @@ -72,8 +71,7 @@ fn get_trusted_setup_g2() -> G2Affine { /// Parse a G1 point from compressed format (48 bytes) fn parse_g1_compressed(bytes: &[u8; 48]) -> Result { - G1Affine::deserialize_compressed(&bytes[..]) - .map_err(|_| PrecompileError::Other("Invalid compressed G1 point".to_string())) + G1Affine::deserialize_compressed(&bytes[..]).map_err(|_| PrecompileError::KzgInvalidG1Point) } /// Read a scalar field element from bytes and verify it's canonical @@ -84,9 +82,7 @@ fn read_scalar_canonical(bytes: &[u8; 32]) -> Result { let bytes_roundtrip = fr.into_bigint().to_bytes_be(); if bytes_roundtrip.as_slice() != bytes { - return Err(PrecompileError::Other( - "Non-canonical scalar field element".to_string(), - )); + return Err(PrecompileError::NonCanonicalFp); } Ok(fr) diff --git a/crates/precompile/src/kzg_point_evaluation/blst.rs b/crates/precompile/src/kzg_point_evaluation/blst.rs index fc13973e3d..57377f8907 100644 --- a/crates/precompile/src/kzg_point_evaluation/blst.rs +++ b/crates/precompile/src/kzg_point_evaluation/blst.rs @@ -9,7 +9,6 @@ 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, }; -use std::string::ToString; /// Verify KZG proof using BLST BLS12-381 implementation. /// @@ -69,8 +68,8 @@ fn get_trusted_setup_g2() -> blst_p2_affine { let mut g2_affine = blst_p2_affine::default(); unsafe { // The compressed format has x coordinate and a flag bit for y - // We use deserialize_compressed which handles this automatically - let result = blst::blst_p2_deserialize(&mut g2_affine, TRUSTED_SETUP_TAU_G2_BYTES.as_ptr()); + // We use uncompress which handles this automatically + let result = blst::blst_p2_uncompress(&mut g2_affine, TRUSTED_SETUP_TAU_G2_BYTES.as_ptr()); if result != blst::BLST_ERROR::BLST_SUCCESS { panic!("Failed to deserialize trusted setup G2 point"); } @@ -92,23 +91,19 @@ fn get_g2_generator() -> blst_p2_affine { fn parse_g1_compressed(bytes: &[u8; 48]) -> Result { let mut point = blst_p1_affine::default(); unsafe { - let result = blst::blst_p1_deserialize(&mut point, bytes.as_ptr()); + let result = blst::blst_p1_uncompress(&mut point, bytes.as_ptr()); if result != blst::BLST_ERROR::BLST_SUCCESS { - return Err(PrecompileError::Other( - "Invalid compressed G1 point".to_string(), - )); + return Err(PrecompileError::KzgInvalidG1Point); } // Verify the point is on curve if !blst_p1_affine_on_curve(&point) { - return Err(PrecompileError::Other("G1 point not on curve".to_string())); + return Err(PrecompileError::KzgG1PointNotOnCurve); } // Verify the point is in the correct subgroup if !blst_p1_affine_in_g1(&point) { - return Err(PrecompileError::Other( - "G1 point not in correct subgroup".to_string(), - )); + return Err(PrecompileError::KzgG1PointNotInSubgroup); } } Ok(point) @@ -124,9 +119,7 @@ fn read_scalar_canonical(bytes: &[u8; 32]) -> Result PrecompileFn { + self.fn_ + } + /// Executes the precompile. #[inline] pub fn execute(&self, input: &[u8], gas_limit: u64) -> PrecompileResult { @@ -425,7 +427,7 @@ impl PrecompileSpecId { BERLIN | LONDON | ARROW_GLACIER | GRAY_GLACIER | MERGE | SHANGHAI => Self::BERLIN, CANCUN => Self::CANCUN, PRAGUE => Self::PRAGUE, - OSAKA => Self::OSAKA, + OSAKA | AMSTERDAM => Self::OSAKA, } } } diff --git a/crates/precompile/src/secp256k1.rs b/crates/precompile/src/secp256k1.rs index 314ae2bd19..87ee0da4be 100644 --- a/crates/precompile/src/secp256k1.rs +++ b/crates/precompile/src/secp256k1.rs @@ -4,9 +4,8 @@ //! * [`k256`](https://crates.io/crates/k256) - uses maintained pure rust lib `k256`, it is perfect use for no_std environments. //! * [`secp256k1`](https://crates.io/crates/secp256k1) - uses `bitcoin_secp256k1` lib, it is a C implementation of secp256k1 used in bitcoin core. //! It is faster than k256 and enabled by default and in std environment. -//! * [`libsecp256k1`](https://crates.io/crates/libsecp256k1) - is made from parity in pure rust, it is alternative for k256. -//! -//! Order of preference is `secp256k1` -> `k256` -> `libsecp256k1`. Where if no features are enabled, it will use `k256`. + +//! Order of preference is `secp256k1` -> `k256`. Where if no features are enabled, it will use `k256`. //! //! Input format: //! [32 bytes for message][64 bytes for signature][1 byte for recovery id] @@ -16,8 +15,6 @@ #[cfg(feature = "secp256k1")] pub mod bitcoin_secp256k1; pub mod k256; -#[cfg(feature = "libsecp256k1")] -pub mod parity_libsecp256k1; use crate::{ crypto, utilities::right_pad, Precompile, PrecompileError, PrecompileId, PrecompileOutput, @@ -70,8 +67,6 @@ pub(crate) fn ecrecover_bytes(sig: [u8; 64], recid: u8, msg: [u8; 32]) -> Option cfg_if::cfg_if! { if #[cfg(feature = "secp256k1")] { pub use bitcoin_secp256k1::ecrecover; - } else if #[cfg(feature = "libsecp256k1")] { - pub use parity_libsecp256k1::ecrecover; } else { pub use k256::ecrecover; } diff --git a/crates/precompile/src/secp256k1/bitcoin_secp256k1.rs b/crates/precompile/src/secp256k1/bitcoin_secp256k1.rs index a1f2bb1651..9297094dae 100644 --- a/crates/precompile/src/secp256k1/bitcoin_secp256k1.rs +++ b/crates/precompile/src/secp256k1/bitcoin_secp256k1.rs @@ -10,7 +10,7 @@ use k256 as _; /// Recover the public key from a signature and a message. /// -/// This function is using the `secp256k1` crate, it is enabled by `libsecp256k1` feature and it is in default. +/// This function is using the `secp256k1` crate, it is enabled by `secp256k1` feature and it is in default. pub fn ecrecover(sig: &B512, recid: u8, msg: &B256) -> Result { let recid = RecoveryId::try_from(recid as i32).expect("recovery ID is valid"); let sig = RecoverableSignature::from_compact(sig.as_slice(), recid)?; diff --git a/crates/precompile/src/secp256k1/parity_libsecp256k1.rs b/crates/precompile/src/secp256k1/parity_libsecp256k1.rs deleted file mode 100644 index 8a460a009b..0000000000 --- a/crates/precompile/src/secp256k1/parity_libsecp256k1.rs +++ /dev/null @@ -1,19 +0,0 @@ -//! `libsecp256k1` implementation of `ecrecover`. More about it in [`crate::secp256k1`]. -use libsecp256k1::{recover, Error, Message, RecoveryId, Signature}; -use primitives::{alloy_primitives::B512, keccak256, B256}; - -/// Recover the public key from a signature and a message. -/// -/// This function is using the `libsecp256k1` crate. -pub fn ecrecover(sig: &B512, recid: u8, msg: &B256) -> Result { - let recid = RecoveryId::parse(recid)?; - let sig = Signature::parse_standard(sig)?; - let msg = Message::parse(msg.as_ref()); - - // uses static context. - let public = recover(&msg, &sig, &recid)?; - - let mut hash = keccak256(&public.serialize()[1..]); - hash[..12].fill(0); - Ok(hash) -} diff --git a/crates/primitives/CHANGELOG.md b/crates/primitives/CHANGELOG.md index 9b8105eae8..2a39fc1765 100644 --- a/crates/primitives/CHANGELOG.md +++ b/crates/primitives/CHANGELOG.md @@ -7,6 +7,38 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [21.0.2](https://github.com/bluealloy/revm/compare/revm-primitives-v21.0.1...revm-primitives-v21.0.2) - 2025-11-07 + +### 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 + +### Other + +- resize short addresses bitvec instead of reallocating ([#3083](https://github.com/bluealloy/revm/pull/3083)) + +## [21.0.0](https://github.com/bluealloy/revm/compare/revm-primitives-v20.2.1...revm-primitives-v21.0.0) - 2025-10-07 + +### Added + +- *(revme)* ef blockchain tests cli ([#2935](https://github.com/bluealloy/revm/pull/2935)) + +### Fixed + +- Align no_std OnceLock semantics with std ([#3026](https://github.com/bluealloy/revm/pull/3026)) +- racecondition return on install_crypto fn ([#2997](https://github.com/bluealloy/revm/pull/2997)) +- Apply spelling corrections from PRs #2926, #2915, #2908 ([#2978](https://github.com/bluealloy/revm/pull/2978)) + +### Other + +- add boundless ([#3043](https://github.com/bluealloy/revm/pull/3043)) +- add SECURITY.md ([#2956](https://github.com/bluealloy/revm/pull/2956)) +- add amsterdam in spec id ([#2934](https://github.com/bluealloy/revm/pull/2934)) +- *(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 @@ -32,8 +64,6 @@ 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). -## [Unreleased] - ## [20.1.0](https://github.com/bluealloy/revm/compare/revm-primitives-v20.0.0...revm-primitives-v20.1.0) - 2025-07-23 ### Added diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index e6de97202f..044b3eb21c 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "revm-primitives" description = "Revm primitives types" -version = "20.2.1" +version = "21.0.2" authors.workspace = true edition.workspace = true keywords.workspace = true @@ -22,7 +22,7 @@ workspace = true alloy-primitives = { workspace = true, features = ["rlp", "map"] } # mics -num_enum = { version = "0.7.3", default-features = false } +num_enum = { version = "0.7", default-features = false } once_cell = { version = "1.21", default-features = false, features = [ "alloc", "race", diff --git a/crates/primitives/src/eip4844.rs b/crates/primitives/src/eip4844.rs index 8a67e8224a..217e4a9070 100644 --- a/crates/primitives/src/eip4844.rs +++ b/crates/primitives/src/eip4844.rs @@ -24,7 +24,7 @@ pub const MAX_BLOB_GAS_PER_BLOCK_CANCUN: u64 = MAX_BLOB_NUMBER_PER_BLOCK_CANCUN pub const TARGET_BLOB_GAS_PER_BLOCK_CANCUN: u64 = TARGET_BLOB_NUMBER_PER_BLOCK_CANCUN * GAS_PER_BLOB; -/// Controls the maximum rate of change for blob gas price +/// Controls the maximum rate of change for blob gas price. Hex 0x32f0ed. pub const BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN: u64 = 3_338_477; /// Target number of the blob per block @@ -40,5 +40,5 @@ pub const MAX_BLOB_GAS_PER_BLOCK_PRAGUE: u64 = MAX_BLOB_NUMBER_PER_BLOCK_PRAGUE pub const TARGET_BLOB_GAS_PER_BLOCK_PRAGUE: u64 = TARGET_BLOB_NUMBER_PER_BLOCK_PRAGUE * GAS_PER_BLOB; -/// Controls the maximum rate of change for blob gas price +/// Controls the maximum rate of change for blob gas price. Hex 0x4c6964. pub const BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE: u64 = 5_007_716; diff --git a/crates/primitives/src/eip7918.rs b/crates/primitives/src/eip7918.rs deleted file mode 100644 index a84d99c902..0000000000 --- a/crates/primitives/src/eip7918.rs +++ /dev/null @@ -1,6 +0,0 @@ -//! EIP-7918: Blob Base Fee Bounded by Execution Cost -//! -//! Constants for blob base fee calculation with execution cost bounds. - -/// Minimum base fee for blobs, if price of the blob is less than this value, this value will be used. -pub const BLOB_BASE_COST: u64 = 2_u64.pow(14); diff --git a/crates/primitives/src/hardfork.rs b/crates/primitives/src/hardfork.rs index 0e2862602c..b094b2567b 100644 --- a/crates/primitives/src/hardfork.rs +++ b/crates/primitives/src/hardfork.rs @@ -75,6 +75,9 @@ pub enum SpecId { /// Osaka hard fork /// Activated at block TBD OSAKA, + /// Amsterdam hard fork + /// Activated at block TBD + AMSTERDAM, } impl SpecId { @@ -133,6 +136,8 @@ pub mod name { pub const PRAGUE: &str = "Prague"; /// String identifier for the Osaka hardfork pub const OSAKA: &str = "Osaka"; + /// String identifier for the Amsterdam hardfork + pub const AMSTERDAM: &str = "Amsterdam"; /// String identifier for the latest hardfork pub const LATEST: &str = "Latest"; } @@ -166,6 +171,7 @@ impl FromStr for SpecId { name::CANCUN => Ok(Self::CANCUN), name::PRAGUE => Ok(Self::PRAGUE), name::OSAKA => Ok(Self::OSAKA), + name::AMSTERDAM => Ok(Self::AMSTERDAM), _ => Err(UnknownHardfork), } } @@ -194,6 +200,7 @@ impl From for &'static str { SpecId::CANCUN => name::CANCUN, SpecId::PRAGUE => name::PRAGUE, SpecId::OSAKA => name::OSAKA, + SpecId::AMSTERDAM => name::AMSTERDAM, } } } diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 8fb8bceeb2..d1b027b2ad 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -23,7 +23,6 @@ pub mod eip7702; pub mod eip7823; pub mod eip7825; pub mod eip7907; -pub mod eip7918; pub mod hardfork; mod once_lock; @@ -55,11 +54,17 @@ pub const SHORT_ADDRESS_CAP: usize = 300; /// and last two bytes are less than [`SHORT_ADDRESS_CAP`]. #[inline] pub fn short_address(address: &Address) -> Option { - if address.0[..18].iter().all(|b| *b == 0) { - let short_address = u16::from_be_bytes([address.0[18], address.0[19]]) as usize; + if address[..18].iter().all(|b| *b == 0) { + let short_address = u16::from_be_bytes([address[18], address[19]]) as usize; if short_address < SHORT_ADDRESS_CAP { return Some(short_address); } } None } + +/// 1 ether = 10^18 wei +pub const ONE_ETHER: u128 = 1_000_000_000_000_000_000; + +/// 1 gwei = 10^9 wei +pub const ONE_GWEI: u128 = 1_000_000_000; diff --git a/crates/primitives/src/once_lock.rs b/crates/primitives/src/once_lock.rs index 90d129ea6a..125d233b17 100644 --- a/crates/primitives/src/once_lock.rs +++ b/crates/primitives/src/once_lock.rs @@ -31,9 +31,8 @@ mod no_std_impl { pub fn get_or_init(&self, f: F) -> &T where F: FnOnce() -> T, - T: Into>, { - self.inner.get_or_init(|| f().into()) + self.inner.get_or_init(|| Box::new(f())) } /// Gets the contents of the OnceLock, returning None if it is not initialized. @@ -41,6 +40,12 @@ mod no_std_impl { pub fn get(&self) -> Option<&T> { self.inner.get() } + + /// Sets the contents of the OnceLock. + #[inline] + pub fn set(&self, value: T) -> Result<(), T> { + self.inner.set(Box::new(value)).map_err(|e| *e) + } } } diff --git a/crates/revm/CHANGELOG.md b/crates/revm/CHANGELOG.md index e5825a10c3..39de77ace9 100644 --- a/crates/revm/CHANGELOG.md +++ b/crates/revm/CHANGELOG.md @@ -7,6 +7,60 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [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 + +- updated the following local packages: revm-primitives, revm-bytecode, revm-context, revm-interpreter, revm-precompile, revm-handler, revm-state, revm-database-interface, revm-context-interface, revm-database, revm-inspector + +## [30.1.0](https://github.com/bluealloy/revm/compare/revm-v30.0.0...revm-v30.1.0) - 2025-10-09 + +### Other + +- updated the following local packages: revm-database-interface, revm-database, revm-interpreter, revm-precompile, revm-handler, revm-inspector, revm-context-interface, revm-context + +## [30.0.0](https://github.com/bluealloy/revm/compare/revm-v29.0.1...revm-v30.0.0) - 2025-10-07 + +### Added + +- allow EIP-7623 to be disabled ([#2985](https://github.com/bluealloy/revm/pull/2985)) +- [**breaking**] Remove kzg-rs ([#2909](https://github.com/bluealloy/revm/pull/2909)) + +### Fixed + +- Apply spelling corrections from PRs #2926, #2915, #2908 ([#2978](https://github.com/bluealloy/revm/pull/2978)) + +### Other + +- changelog update for v87 ([#3056](https://github.com/bluealloy/revm/pull/3056)) +- add boundless ([#3043](https://github.com/bluealloy/revm/pull/3043)) +- forward optional_fee_charge feature ([#3005](https://github.com/bluealloy/revm/pull/3005)) +- add SECURITY.md ([#2956](https://github.com/bluealloy/revm/pull/2956)) + ## [29.0.1](https://github.com/bluealloy/revm/compare/revm-v29.0.0...revm-v29.0.1) - 2025-09-23 ### Other diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index 9cf1149111..6e7429261c 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 = "29.0.1" +version = "31.0.1" authors.workspace = true edition.workspace = true keywords.workspace = true @@ -78,12 +78,16 @@ 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", "optional_block_gas_limit", "optional_eip3541", "optional_eip3607", + "optional_eip7623", "optional_no_base_fee", ] memory_limit = ["context/memory_limit", "interpreter/memory_limit"] @@ -91,19 +95,18 @@ optional_balance_check = ["context/optional_balance_check"] optional_block_gas_limit = ["context/optional_block_gas_limit"] optional_eip3541 = ["context/optional_eip3541"] optional_eip3607 = ["context/optional_eip3607"] +optional_eip7623 = ["context/optional_eip7623"] optional_no_base_fee = ["context/optional_no_base_fee"] +optional_fee_charge = ["context/optional_fee_charge"] enable_eip7702 = ["context/enable_eip7702"] enable_eip7623 = ["context/enable_eip7623"] # Precompiles features: Please look at the comments in `precompile` crate for more information. -secp256k1 = ["precompile/secp256k1"] # See comments in `precompile` -c-kzg = [ - "precompile/c-kzg", -] # `kzg-rs` is not audited but useful for `no_std` environment, use it with causing and default to `c-kzg` if possible. -kzg-rs = ["precompile/kzg-rs"] +secp256k1 = ["precompile/secp256k1"] # See comments in `precompile` use it with caution +c-kzg = ["precompile/c-kzg"] -# blst will enabled both for bls precompiles and for kzg precompile. +# blst will be enabled both for bls precompiles and for kzg precompile. blst = ["precompile/blst"] bn = ["precompile/bn"] asm-sha2 = ["precompile/asm-sha2"] @@ -113,5 +116,5 @@ asm-sha2 = ["precompile/asm-sha2"] portable = ["precompile/portable"] # use gmp for modexp precompile. -# It is faster library but licences as GPL code, if enabled please make sure to follow the license. +# It is faster library but licenced as GPL code, if enabled please make sure to follow the license. gmp = ["precompile/gmp"] diff --git a/crates/state/CHANGELOG.md b/crates/state/CHANGELOG.md index 94d8d0adff..63e4abe9c5 100644 --- a/crates/state/CHANGELOG.md +++ b/crates/state/CHANGELOG.md @@ -7,6 +7,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [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 + +- updated the following local packages: revm-primitives, revm-bytecode + ## [7.0.5](https://github.com/bluealloy/revm/compare/revm-state-v7.0.4...revm-state-v7.0.5) - 2025-08-23 ### Other @@ -37,11 +61,20 @@ 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). -## [4.0.0](https://github.com/bluealloy/revm/compare/revm-state-v3.0.1...revm-state-v4.0.0) - 2025-05-07 +## [7.0.6](https://github.com/bluealloy/revm/compare/revm-state-v7.0.5...revm-state-v7.0.6) - 2025-10-07 -Dependency bump +### Fixed -## [Unreleased] +- Apply spelling corrections from PRs #2926, #2915, #2908 ([#2978](https://github.com/bluealloy/revm/pull/2978)) +- skip cold load on oog ([#2903](https://github.com/bluealloy/revm/pull/2903)) + +### Other + +- add boundless ([#3043](https://github.com/bluealloy/revm/pull/3043)) +- helper caller_initial_modification added ([#3032](https://github.com/bluealloy/revm/pull/3032)) +- *(state)* remove unnecessary core::hash::Hash import from lib.rs ([#2959](https://github.com/bluealloy/revm/pull/2959)) +- add SECURITY.md ([#2956](https://github.com/bluealloy/revm/pull/2956)) +- use primitives::HashMap default ([#2916](https://github.com/bluealloy/revm/pull/2916)) ## [7.0.2](https://github.com/bluealloy/revm/compare/revm-state-v7.0.1...revm-state-v7.0.2) - 2025-07-23 diff --git a/crates/state/Cargo.toml b/crates/state/Cargo.toml index a9f930af7d..ff57aa2044 100644 --- a/crates/state/Cargo.toml +++ b/crates/state/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "revm-state" description = "Revm state types" -version = "7.0.5" +version = "8.1.1" authors.workspace = true edition.workspace = true keywords.workspace = true diff --git a/crates/state/src/account_info.rs b/crates/state/src/account_info.rs index 11e39a0e09..8c2a41aea8 100644 --- a/crates/state/src/account_info.rs +++ b/crates/state/src/account_info.rs @@ -340,7 +340,7 @@ mod tests { "Set contains account2 (since equal)" ); - let mut accounts = vec![account2.clone(), account1.clone()]; + let mut accounts = [account2.clone(), account1.clone()]; accounts.sort(); assert_eq!(accounts[0], accounts[1], "Sorted vec treats them as equal"); } diff --git a/crates/state/src/lib.rs b/crates/state/src/lib.rs index 985af53a3d..47b6b90112 100644 --- a/crates/state/src/lib.rs +++ b/crates/state/src/lib.rs @@ -12,9 +12,8 @@ pub use primitives; pub use types::{EvmState, EvmStorage, TransientStorage}; use bitflags::bitflags; -use core::hash::Hash; use primitives::hardfork::SpecId; -use primitives::{HashMap, StorageKey, StorageValue}; +use primitives::{HashMap, StorageKey, StorageValue, U256}; /// Account type used inside Journal to track changed to state. #[derive(Debug, Clone, PartialEq, Eq, Default)] @@ -41,6 +40,24 @@ impl Account { } } + /// Make changes to the caller account. + /// + /// It marks the account as touched, changes the balance and bumps the nonce if `is_call` is true. + /// + /// Returns the old balance. + #[inline] + pub fn caller_initial_modification(&mut self, new_balance: U256, is_call: bool) -> U256 { + // Touch account so we know it is changed. + self.mark_touch(); + + if is_call { + // Nonce is already checked + self.info.nonce = self.info.nonce.saturating_add(1); + } + + core::mem::replace(&mut self.info.balance, new_balance) + } + /// Checks if account is empty and check if empty state before spurious dragon hardfork. #[inline] pub fn state_clear_aware_is_empty(&self, spec: SpecId) -> bool { @@ -105,19 +122,18 @@ impl Account { self.status |= AccountStatus::Cold; } + /// Is account warm for given transaction id. + #[inline] + pub fn is_cold_transaction_id(&self, transaction_id: usize) -> bool { + self.transaction_id != transaction_id || self.status.contains(AccountStatus::Cold) + } + /// Marks the account as warm and return true if it was previously cold. #[inline] pub fn mark_warm_with_transaction_id(&mut self, transaction_id: usize) -> bool { - let same_id = self.transaction_id == transaction_id; - let is_cold = self.status.contains(AccountStatus::Cold); - + let is_cold = self.is_cold_transaction_id(transaction_id); self.status -= AccountStatus::Cold; self.transaction_id = transaction_id; - - if !same_id { - return true; - } - is_cold } @@ -327,6 +343,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() @@ -394,21 +418,22 @@ impl EvmStorageSlot { self.is_cold = true; } + /// Is storage slot cold for given transaction id. + #[inline] + pub fn is_cold_transaction_id(&self, transaction_id: usize) -> bool { + self.transaction_id != transaction_id || self.is_cold + } + /// Marks the storage slot as warm and sets transaction_id to the given value /// /// /// Returns false if old transition_id is different from given id or in case they are same return `Self::is_cold` value. #[inline] pub fn mark_warm_with_transaction_id(&mut self, transaction_id: usize) -> bool { - let same_id = self.transaction_id == transaction_id; + let is_cold = self.is_cold_transaction_id(transaction_id); self.transaction_id = transaction_id; - let was_cold = core::mem::take(&mut self.is_cold); - - if same_id { - // only if transaction id is same we are returning was_cold. - return was_cold; - } - true + self.is_cold = false; + is_cold } } @@ -509,7 +534,7 @@ mod tests { #[test] fn test_account_with_storage() { - let mut storage = HashMap::new(); + let mut storage = HashMap::::default(); let key1 = StorageKey::from(1); let key2 = StorageKey::from(2); let slot1 = EvmStorageSlot::new(StorageValue::from(10), 0); @@ -621,7 +646,7 @@ mod tests { let slot_key = StorageKey::from(42); let slot_value = EvmStorageSlot::new(StorageValue::from(123), 0); - let mut storage = HashMap::new(); + let mut storage = HashMap::::default(); storage.insert(slot_key, slot_value.clone()); // Chain multiple builder methods together @@ -640,4 +665,17 @@ mod tests { assert!(account.is_touched()); assert!(!account.status.contains(AccountStatus::Cold)); } + + #[test] + fn test_account_is_cold_transaction_id() { + let mut account = Account::default(); + // only case where it is warm. + assert!(!account.is_cold_transaction_id(0)); + + // all other cases are cold + assert!(account.is_cold_transaction_id(1)); + account.mark_cold(); + assert!(account.is_cold_transaction_id(0)); + assert!(account.is_cold_transaction_id(1)); + } } diff --git a/crates/statetest-types/CHANGELOG.md b/crates/statetest-types/CHANGELOG.md index 33123721c9..050e93e8f1 100644 --- a/crates/statetest-types/CHANGELOG.md +++ b/crates/statetest-types/CHANGELOG.md @@ -7,6 +7,62 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [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 + +- updated the following local packages: revm + +## [10.1.0](https://github.com/bluealloy/revm/compare/revm-statetest-types-v10.0.0...revm-statetest-types-v10.1.0) - 2025-10-09 + +### Other + +- updated the following local packages: revm + +## [10.0.0](https://github.com/bluealloy/revm/compare/revm-statetest-types-v9.0.3...revm-statetest-types-v10.0.0) - 2025-10-07 + +### Added + +- *(revme)* ef blockchain tests cli ([#2935](https://github.com/bluealloy/revm/pull/2935)) + +### Fixed + +- Apply spelling corrections from PRs #2926, #2915, #2908 ([#2978](https://github.com/bluealloy/revm/pull/2978)) + +### Other + +- changelog update for v87 ([#3056](https://github.com/bluealloy/revm/pull/3056)) +- add boundless ([#3043](https://github.com/bluealloy/revm/pull/3043)) +- *(btest)* 0 chainid is considered none ([#3022](https://github.com/bluealloy/revm/pull/3022)) +- prealloc few frames ([#2965](https://github.com/bluealloy/revm/pull/2965)) +- add SECURITY.md ([#2956](https://github.com/bluealloy/revm/pull/2956)) +- remove parent blob gas used and excess ([#2933](https://github.com/bluealloy/revm/pull/2933)) +- *(cleanup)* Remove EIP-7918 related functions and EIP file ([#2925](https://github.com/bluealloy/revm/pull/2925)) + ## [9.0.3](https://github.com/bluealloy/revm/compare/revm-statetest-types-v9.0.2...revm-statetest-types-v9.0.3) - 2025-09-23 ### Other diff --git a/crates/statetest-types/Cargo.toml b/crates/statetest-types/Cargo.toml index 488804fcb0..c92a6f82eb 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 = "9.0.3" +version = "11.0.1" authors.workspace = true edition.workspace = true keywords.workspace = true @@ -24,3 +24,4 @@ serde = { workspace = true, features = ["derive", "rc"] } serde_json = { workspace = true, features = ["preserve_order"] } k256 = { workspace = true } thiserror = { workspace = true } +alloy-eips = { workspace = true } diff --git a/crates/statetest-types/src/blockchain.rs b/crates/statetest-types/src/blockchain.rs new file mode 100644 index 0000000000..7637ebd731 --- /dev/null +++ b/crates/statetest-types/src/blockchain.rs @@ -0,0 +1,849 @@ +//! Blockchain test types for Ethereum state tests. +//! +//! This module contains structures for deserializing blockchain test JSON files +//! 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 serde::Deserialize; +use std::collections::BTreeMap; + +/// Blockchain test suite containing multiple test cases +#[derive(Debug, PartialEq, Eq, Deserialize)] +pub struct BlockchainTest(pub BTreeMap); + +/// Individual blockchain test case +#[derive(Debug, PartialEq, Eq, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct BlockchainTestCase { + /// Genesis block header + pub genesis_block_header: BlockHeader, + /// Genesis block RLP encoding + #[serde(rename = "genesisRLP")] + pub genesis_rlp: Option, + /// List of blocks in the test + pub blocks: Vec, + /// Post-state accounts (optional) + pub post_state: Option>, + /// Pre-state accounts + pub pre: State, + /// Last block hash + pub lastblockhash: B256, + /// Network specification + pub network: ForkSpec, + /// Seal engine type + #[serde(default)] + pub seal_engine: SealEngine, +} + +/// Block header structure +#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Default)] +#[serde(rename_all = "camelCase")] +pub struct BlockHeader { + /// Bloom filter for logs + pub bloom: Bytes, + /// Block coinbase/beneficiary address + pub coinbase: Address, + /// Block difficulty (pre-merge) or 0 (post-merge) + pub difficulty: U256, + /// Extra data field + pub extra_data: Bytes, + /// Gas limit for this block + pub gas_limit: U256, + /// Gas used by all transactions in this block + pub gas_used: U256, + /// Block hash + pub hash: B256, + /// Mix hash for PoW validation + pub mix_hash: B256, + /// PoW nonce + pub nonce: FixedBytes<8>, + /// Block number + pub number: U256, + /// Parent block hash + pub parent_hash: B256, + /// Root hash of the receipt trie + pub receipt_trie: B256, + /// State root hash after executing this block + pub state_root: B256, + /// Block timestamp + pub timestamp: U256, + /// Root hash of the transaction trie + pub transactions_trie: B256, + /// Uncle hash (ommers hash) + pub uncle_hash: B256, + /// Base fee per gas (EIP-1559) + pub base_fee_per_gas: Option, + /// Withdrawals root hash (post-Shanghai) + pub withdrawals_root: Option, + /// Blob gas used (EIP-4844) + pub blob_gas_used: Option, + /// Excess blob gas (EIP-4844) + pub excess_blob_gas: Option, + /// Parent beacon block root (EIP-4788) + pub parent_beacon_block_root: Option, + /// Requests hash (for future EIPs) + pub requests_hash: Option, + /// Target blobs per block (EIP-4844 related) + pub target_blobs_per_block: Option, +} + +/// Block structure containing header and transactions +#[derive(Debug, PartialEq, Eq, Deserialize, Default)] +#[serde(rename_all = "camelCase")] +pub struct Block { + /// Block header (when provided directly) + pub block_header: Option, + /// RLP-encoded block data + pub rlp: Bytes, + /// Expected exception for invalid blocks + pub expect_exception: Option, + /// List of transactions in the block + pub transactions: Option>, + /// Uncle/ommer headers + pub uncle_headers: Option>, + /// Withdrawals in the block (post-Shanghai) + pub withdrawals: Option>, +} + +/// Transaction structure +#[derive(Debug, PartialEq, Eq, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Transaction { + /// Transaction type + #[serde(rename = "type")] + pub transaction_type: Option, + /// Transaction sender + #[serde(default)] + pub sender: Option
, + /// Transaction data/input + pub data: Bytes, + /// Gas limit + pub gas_limit: U256, + /// Gas price (legacy transactions) + pub gas_price: Option, + /// Transaction nonce + pub nonce: U256, + /// ECDSA signature r value + pub r: U256, + /// ECDSA signature s value + pub s: U256, + /// ECDSA signature v value + pub v: U256, + /// Ether value to transfer + pub value: U256, + /// Target address + #[serde(default, deserialize_with = "deserialize_maybe_empty")] + pub to: Option
, + /// Chain ID for replay protection + pub chain_id: Option, + /// Access list (EIP-2930) + pub access_list: Option, + /// Maximum fee per gas (EIP-1559) + pub max_fee_per_gas: Option, + /// Maximum priority fee per gas (EIP-1559) + pub max_priority_fee_per_gas: Option, + /// Blob versioned hashes (EIP-4844) + pub blob_versioned_hashes: Option>, + /// Maximum fee per blob gas (EIP-4844) + pub max_fee_per_blob_gas: Option, + /// Authorization list (EIP-7702) + pub authorization_list: Option>, + /// Transaction hash + pub hash: Option, +} + +/// Withdrawal structure +#[derive(Debug, PartialEq, Eq, Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct Withdrawal { + /// Withdrawal index + pub index: U256, + /// Validator index + pub validator_index: U256, + /// Withdrawal recipient address + pub address: Address, + /// Withdrawal amount in gwei + pub amount: U256, +} + +/// Ethereum blockchain test data state +#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Default)] +pub struct State(pub BTreeMap); + +impl State { + /// Return state as genesis state + pub fn into_genesis_state(self) -> BTreeMap { + self.0 + .into_iter() + .map(|(address, account)| { + let storage = account + .storage + .iter() + .filter(|(_, v)| !v.is_zero()) + .map(|(k, v)| (*k, *v)) + .collect(); + let account_info = AccountInfo { + balance: account.balance, + nonce: account.nonce.to::(), + code: account.code, + storage, + }; + (address, account_info) + }) + .collect::>() + } +} + +/// An account +#[derive(Debug, PartialEq, Eq, Deserialize, Clone, Default)] +#[serde(deny_unknown_fields)] +pub struct Account { + /// Balance + pub balance: U256, + /// Code + pub code: Bytes, + /// Nonce + pub nonce: U256, + /// Storage + pub storage: BTreeMap, +} + +/// Fork specification +#[derive(Debug, PartialEq, Eq, PartialOrd, Hash, Ord, Clone, Copy, Deserialize)] +pub enum ForkSpec { + /// Frontier + Frontier, + /// Frontier to Homestead + FrontierToHomesteadAt5, + /// Homestead + Homestead, + /// Homestead to Tangerine + HomesteadToDaoAt5, + /// Homestead to Tangerine + HomesteadToEIP150At5, + /// Tangerine + EIP150, + /// Spurious Dragon + EIP158, + /// Spurious Dragon to Byzantium + EIP158ToByzantiumAt5, + /// Byzantium + Byzantium, + /// Byzantium to Constantinople + ByzantiumToConstantinopleAt5, + /// Byzantium to Constantinople + ByzantiumToConstantinopleFixAt5, + /// Constantinople + Constantinople, + /// Constantinople fix + ConstantinopleFix, + /// Istanbul + Istanbul, + /// Berlin + Berlin, + /// Berlin to London + BerlinToLondonAt5, + /// London + London, + /// Paris aka The Merge + #[serde(alias = "Merge")] + Paris, + /// Paris to Shanghai transition + ParisToShanghaiAtTime15k, + /// Shanghai + Shanghai, + /// Shanghai to Cancun transition + ShanghaiToCancunAtTime15k, + /// Merge EOF test + #[serde(alias = "Merge+3540+3670")] + MergeEOF, + /// After Merge Init Code test + #[serde(alias = "Merge+3860")] + MergeMeterInitCode, + /// After Merge plus new PUSH0 opcode + #[serde(alias = "Merge+3855")] + MergePush0, + /// Cancun + Cancun, + /// Cancun to Prague transition + CancunToPragueAtTime15k, + /// Prague + Prague, + /// Prague to Osaka transition + PragueToOsakaAtTime15k, + /// Osaka + Osaka, + /// BPO1 to BPO2 transition + BPO1ToBPO2AtTime15k, +} + +/// Possible seal engines +#[derive(Debug, PartialEq, Eq, Default, Deserialize)] +pub enum SealEngine { + /// No consensus checks + #[default] + NoProof, + /// Proof of Work + Ethash, +} + +impl BlockHeader { + /// Convert BlockHeader to BlockEnv + pub fn to_block_env( + &self, + blob_excess_gas_and_price: Option, + ) -> BlockEnv { + BlockEnv { + number: self.number, + beneficiary: self.coinbase, + timestamp: self.timestamp, + gas_limit: self.gas_limit.to::(), + basefee: self.base_fee_per_gas.unwrap_or_default().to::(), + difficulty: self.difficulty, + prevrandao: if self.difficulty.is_zero() { + Some(self.mix_hash) + } else { + None + }, + blob_excess_gas_and_price, + } + } +} + +impl Transaction { + /// Convert Transaction to TxEnv + /// Note: The 'to' and 'sender' fields need to be provided separately in the reth model + pub fn to_tx_env(&self) -> Result { + // Determine transaction type + let tx_type = self.transaction_type.map(|t| t.to::()).unwrap_or(0); + + // Set transaction kind (to address) + let kind = if let Some(to_addr) = self.to { + TxKind::Call(to_addr) + } else { + TxKind::Create + }; + + let Some(sender) = self.sender else { + return Err("Sender is required".to_string()); + }; + + // Build the base transaction + let mut builder = TxEnv::builder() + .tx_type(Some(tx_type)) + .caller(sender) + .gas_limit(self.gas_limit.to::()) + .nonce(self.nonce.to::()) + .value(self.value) + .data(self.data.clone()) + .access_list(self.access_list.clone().unwrap_or_default()) + .blob_hashes(self.blob_versioned_hashes.clone().unwrap_or_default()) + .max_fee_per_blob_gas(self.max_fee_per_blob_gas.unwrap_or_default().to::()) + .authorization_list_signed( + self.authorization_list + .clone() + .map(|auth_list| auth_list.into_iter().map(Into::into).collect::>()) + .unwrap_or_default(), + ) + .kind(kind); + + // Set chain ID if present + if let Some(chain_id) = self.chain_id { + let chain_id = chain_id.to::(); + // 0 chain id is considered as no chain id + if chain_id != 0 { + builder = builder.chain_id(Some(chain_id)); + } + } + + // Handle gas pricing based on transaction type + builder = match tx_type { + 0 | 1 => { + // Legacy or EIP-2930 transaction + if let Some(gas_price) = self.gas_price { + builder.gas_price(gas_price.to::()) + } else { + builder + } + } + 2..=4 => { + // EIP-1559 or EIP-4844 transaction + let mut b = builder; + if let Some(max_fee) = self.max_fee_per_gas { + b = b.gas_price(max_fee.to::()); + } + if let Some(priority_fee) = self.max_priority_fee_per_gas { + b = b.gas_priority_fee(Some(priority_fee.to::())); + } + b + } + _ => { + // For unknown types, try to use gas_price if available + if let Some(gas_price) = self.gas_price { + builder.gas_price(gas_price.to::()) + } else { + builder + } + } + }; + + builder + .build() + .map_err(|e| format!("Failed to build TxEnv: {e:?}")) + } +} + +impl BlockchainTestCase { + /// Get the genesis block environment + pub fn genesis_block_env(&self) -> BlockEnv { + self.genesis_block_header.to_block_env(None) + } +} + +#[cfg(test)] +mod test { + use revm::primitives::address; + + use super::*; + + #[test] + fn test_transaction_deserialization() { + let tx = r#" + { + "type": "0x00", + "chainId": "0x01", + "nonce": "0x00", + "gasPrice": "0x0a", + "gasLimit": "0x05f5e100", + "to": "0x1000000000000000000000000000000000000000", + "value": "0x00", + "data": "0x", + "v": "0x25", + "r": "0x52665e44edaa715e7c5f531675a96a47c7827593adf02f5d9b97c4bc952500ec", + "s": "0x1b4d3b625da8720d6a05d67ad1aa986089717cd0ff4fef2ee0e76779f9746957", + "sender": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" + }"#; + let _: Transaction = serde_json::from_str(tx).unwrap(); + } + + #[test] + fn test_blockchain_test_deserialization() { + // Test that we can deserialize the sample JSON + 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) + // This is expected as the formats differ slightly + if let Err(e) = result { + println!("Expected deserialization error due to format differences: {e}"); + } + } + + #[test] + fn test_fork_spec_deserialization() { + // Test ForkSpec enum deserialization + let fork_specs = vec![ + ("\"Frontier\"", ForkSpec::Frontier), + ("\"Homestead\"", ForkSpec::Homestead), + ("\"Byzantium\"", ForkSpec::Byzantium), + ("\"Constantinople\"", ForkSpec::Constantinople), + ("\"Istanbul\"", ForkSpec::Istanbul), + ("\"Berlin\"", ForkSpec::Berlin), + ("\"London\"", ForkSpec::London), + ("\"Paris\"", ForkSpec::Paris), + ("\"Merge\"", ForkSpec::Paris), // Alias test + ("\"Shanghai\"", ForkSpec::Shanghai), + ("\"Cancun\"", ForkSpec::Cancun), + ( + "\"CancunToPragueAtTime15k\"", + ForkSpec::CancunToPragueAtTime15k, + ), + ("\"Prague\"", ForkSpec::Prague), + ( + "\"PragueToOsakaAtTime15k\"", + ForkSpec::PragueToOsakaAtTime15k, + ), + ("\"Osaka\"", ForkSpec::Osaka), + ("\"BPO1ToBPO2AtTime15k\"", ForkSpec::BPO1ToBPO2AtTime15k), + ]; + + for (json, expected) in fork_specs { + let result: ForkSpec = serde_json::from_str(json).unwrap(); + assert_eq!(result, expected); + } + } + + #[test] + fn test_transaction_conversion() { + use crate::blockchain::Transaction; + use revm::primitives::{Bytes, U256}; + + let tx = Transaction { + transaction_type: Some(U256::from(0)), + sender: Some(address!("0x1000000000000000000000000000000000000000")), + data: Bytes::default(), + gas_limit: U256::from(21000), + gas_price: Some(U256::from(1000000000)), + nonce: U256::from(0), + r: U256::from(1), + s: U256::from(2), + v: U256::from(27), + value: U256::from(1000), + chain_id: Some(U256::from(1)), + access_list: None, + max_fee_per_gas: None, + max_priority_fee_per_gas: None, + max_fee_per_blob_gas: None, + hash: None, + to: None, + authorization_list: None, + blob_versioned_hashes: None, + }; + + // Test conversion with dummy sender and to address + let tx_env = tx.to_tx_env().unwrap(); + + assert_eq!(tx_env.tx_type, 0); + assert_eq!(tx_env.nonce, 0); + assert_eq!(tx_env.gas_limit, 21000); + assert_eq!(tx_env.gas_price, 1000000000); + assert_eq!(tx_env.value, U256::from(1000)); + } + + const SAMPLE: &str = r#" + { + "tests/osaka/eip7825_transaction_gas_limit_cap/test_tx_gas_limit.py::test_transaction_gas_limit_cap_at_transition[fork_PragueToOsakaAtTime15k-blockchain_test]": { + "network": "PragueToOsakaAtTime15k", + "genesisBlockHeader": { + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "uncleHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "coinbase": "0x0000000000000000000000000000000000000000", + "stateRoot": "0xfe13aa0b3a4ea731b1715a429c1cf100db415262a5bdd49478dc7b9e61cbf1df", + "transactionsTrie": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "receiptTrie": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "difficulty": "0x00", + "number": "0x00", + "gasLimit": "0x044aa200", + "gasUsed": "0x00", + "timestamp": "0x00", + "extraData": "0x00", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0000000000000000", + "baseFeePerGas": "0x07", + "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "blobGasUsed": "0x00", + "excessBlobGas": "0x00", + "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "requestsHash": "0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "hash": "0x04b688c25df122da84e0b1a21b90dd8f898e6005cdc2d1ac6c60ded9ff9b2de4" + }, + "pre": { + "0x00000000219ab540356cbb839cbe05303d7705fa": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x60806040526004361061003f5760003560e01c806301ffc9a71461004457806322895118146100a4578063621fd130146101ba578063c5f2892f14610244575b600080fd5b34801561005057600080fd5b506100906004803603602081101561006757600080fd5b50357fffffffff000000000000000000000000000000000000000000000000000000001661026b565b604080519115158252519081900360200190f35b6101b8600480360360808110156100ba57600080fd5b8101906020810181356401000000008111156100d557600080fd5b8201836020820111156100e757600080fd5b8035906020019184600183028401116401000000008311171561010957600080fd5b91939092909160208101903564010000000081111561012757600080fd5b82018360208201111561013957600080fd5b8035906020019184600183028401116401000000008311171561015b57600080fd5b91939092909160208101903564010000000081111561017957600080fd5b82018360208201111561018b57600080fd5b803590602001918460018302840111640100000000831117156101ad57600080fd5b919350915035610304565b005b3480156101c657600080fd5b506101cf6110b5565b6040805160208082528351818301528351919283929083019185019080838360005b838110156102095781810151838201526020016101f1565b50505050905090810190601f1680156102365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561025057600080fd5b506102596110c7565b60408051918252519081900360200190f35b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a70000000000000000000000000000000000000000000000000000000014806102fe57507fffffffff0000000000000000000000000000000000000000000000000000000082167f8564090700000000000000000000000000000000000000000000000000000000145b92915050565b6030861461035d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118056026913960400191505060405180910390fd5b602084146103b6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603681526020018061179c6036913960400191505060405180910390fd5b6060821461040f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260298152602001806118786029913960400191505060405180910390fd5b670de0b6b3a7640000341015610470576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118526026913960400191505060405180910390fd5b633b9aca003406156104cd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260338152602001806117d26033913960400191505060405180910390fd5b633b9aca00340467ffffffffffffffff811115610535576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061182b6027913960400191505060405180910390fd5b6060610540826114ba565b90507f649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c589898989858a8a6105756020546114ba565b6040805160a0808252810189905290819060208201908201606083016080840160c085018e8e80828437600083820152601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690910187810386528c815260200190508c8c808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690920188810386528c5181528c51602091820193918e019250908190849084905b83811015610648578181015183820152602001610630565b50505050905090810190601f1680156106755780820380516001836020036101000a031916815260200191505b5086810383528881526020018989808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169092018881038452895181528951602091820193918b019250908190849084905b838110156106ef5781810151838201526020016106d7565b50505050905090810190601f16801561071c5780820380516001836020036101000a031916815260200191505b509d505050505050505050505050505060405180910390a1600060028a8a600060801b604051602001808484808284377fffffffffffffffffffffffffffffffff0000000000000000000000000000000090941691909301908152604080517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0818403018152601090920190819052815191955093508392506020850191508083835b602083106107fc57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016107bf565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610859573d6000803e3d6000fd5b5050506040513d602081101561086e57600080fd5b5051905060006002806108846040848a8c6116fe565b6040516020018083838082843780830192505050925050506040516020818303038152906040526040518082805190602001908083835b602083106108f857805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016108bb565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610955573d6000803e3d6000fd5b5050506040513d602081101561096a57600080fd5b5051600261097b896040818d6116fe565b60405160009060200180848480828437919091019283525050604080518083038152602092830191829052805190945090925082918401908083835b602083106109f457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016109b7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610a51573d6000803e3d6000fd5b5050506040513d6020811015610a6657600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610ada57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610a9d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610b37573d6000803e3d6000fd5b5050506040513d6020811015610b4c57600080fd5b50516040805160208101858152929350600092600292839287928f928f92018383808284378083019250505093505050506040516020818303038152906040526040518082805190602001908083835b60208310610bd957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610b9c565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610c36573d6000803e3d6000fd5b5050506040513d6020811015610c4b57600080fd5b50516040518651600291889160009188916020918201918291908601908083835b60208310610ca957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610c6c565b6001836020036101000a0380198251168184511680821785525050505050509050018367ffffffffffffffff191667ffffffffffffffff1916815260180182815260200193505050506040516020818303038152906040526040518082805190602001908083835b60208310610d4e57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610d11565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610dab573d6000803e3d6000fd5b5050506040513d6020811015610dc057600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610e3457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610df7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610e91573d6000803e3d6000fd5b5050506040513d6020811015610ea657600080fd5b50519050858114610f02576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260548152602001806117486054913960600191505060405180910390fd5b60205463ffffffff11610f60576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806117276021913960400191505060405180910390fd5b602080546001019081905560005b60208110156110a9578160011660011415610fa0578260008260208110610f9157fe5b0155506110ac95505050505050565b600260008260208110610faf57fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061102557805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610fe8565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015611082573d6000803e3d6000fd5b5050506040513d602081101561109757600080fd5b50519250600282049150600101610f6e565b50fe5b50505050505050565b60606110c26020546114ba565b905090565b6020546000908190815b60208110156112f05781600116600114156111e6576002600082602081106110f557fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061116b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161112e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156111c8573d6000803e3d6000fd5b5050506040513d60208110156111dd57600080fd5b505192506112e2565b600283602183602081106111f657fe5b015460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061126b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161122e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156112c8573d6000803e3d6000fd5b5050506040513d60208110156112dd57600080fd5b505192505b6002820491506001016110d1565b506002826112ff6020546114ba565b600060401b6040516020018084815260200183805190602001908083835b6020831061135a57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161131d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790527fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000095909516920191825250604080518083037ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8018152601890920190819052815191955093508392850191508083835b6020831061143f57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101611402565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa15801561149c573d6000803e3d6000fd5b5050506040513d60208110156114b157600080fd5b50519250505090565b60408051600880825281830190925260609160208201818036833701905050905060c082901b8060071a60f81b826000815181106114f457fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060061a60f81b8260018151811061153757fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060051a60f81b8260028151811061157a57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060041a60f81b826003815181106115bd57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060031a60f81b8260048151811061160057fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060021a60f81b8260058151811061164357fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060011a60f81b8260068151811061168657fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060001a60f81b826007815181106116c957fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535050919050565b6000808585111561170d578182fd5b83861115611719578182fd5b505082019391909203915056fe4465706f736974436f6e74726163743a206d65726b6c6520747265652066756c6c4465706f736974436f6e74726163743a207265636f6e7374727563746564204465706f7369744461746120646f6573206e6f74206d6174636820737570706c696564206465706f7369745f646174615f726f6f744465706f736974436f6e74726163743a20696e76616c6964207769746864726177616c5f63726564656e7469616c73206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c7565206e6f74206d756c7469706c65206f6620677765694465706f736974436f6e74726163743a20696e76616c6964207075626b6579206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f20686967684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f206c6f774465706f736974436f6e74726163743a20696e76616c6964207369676e6174757265206c656e677468a2646970667358221220dceca8706b29e917dacf25fceef95acac8d90d765ac926663ce4096195952b6164736f6c634300060b0033", + "storage": { + "0x22": "0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b", + "0x23": "0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71", + "0x24": "0xc78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c", + "0x25": "0x536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c", + "0x26": "0x9efde052aa15429fae05bad4d0b1d7c64da64d03d7a1854a588c2cb8430c0d30", + "0x27": "0xd88ddfeed400a8755596b21942c1497e114c302e6118290f91e6772976041fa1", + "0x28": "0x87eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2c", + "0x29": "0x26846476fd5fc54a5d43385167c95144f2643f533cc85bb9d16b782f8d7db193", + "0x2a": "0x506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e1", + "0x2b": "0xffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b", + "0x2c": "0x6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220", + "0x2d": "0xb7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5f", + "0x2e": "0xdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85e", + "0x2f": "0xb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784", + "0x30": "0xd49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb", + "0x31": "0x8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb", + "0x32": "0x8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab", + "0x33": "0x95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4", + "0x34": "0xf893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17f", + "0x35": "0xcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa", + "0x36": "0x8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9c", + "0x37": "0xfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167", + "0x38": "0xe71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d7", + "0x39": "0x31206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc0", + "0x3a": "0x21352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544", + "0x3b": "0x619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765", + "0x3c": "0x7cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4", + "0x3d": "0x848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe1", + "0x3e": "0x8869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636", + "0x3f": "0xb5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c", + "0x40": "0x985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7" + } + }, + "0x00000961ef480eb55e80d19ad83579a64c007002": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe1460cb5760115f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff146101f457600182026001905f5b5f82111560685781019083028483029004916001019190604d565b909390049250505036603814608857366101f457346101f4575f5260205ff35b34106101f457600154600101600155600354806003026004013381556001015f35815560010160203590553360601b5f5260385f601437604c5fa0600101600355005b6003546002548082038060101160df575060105b5f5b8181146101835782810160030260040181604c02815460601b8152601401816001015481526020019060020154807fffffffffffffffffffffffffffffffff00000000000000000000000000000000168252906010019060401c908160381c81600701538160301c81600601538160281c81600501538160201c81600401538160181c81600301538160101c81600201538160081c81600101535360010160e1565b910180921461019557906002556101a0565b90505f6002555f6003555b5f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff14156101cd57505f5b6001546002828201116101e25750505f6101e8565b01600290035b5f555f600155604c025ff35b5f5ffd", + "storage": {} + }, + "0x0000bbddc7ce488642fb579f8b00f3a590007251": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe1460d35760115f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1461019a57600182026001905f5b5f82111560685781019083028483029004916001019190604d565b9093900492505050366060146088573661019a573461019a575f5260205ff35b341061019a57600154600101600155600354806004026004013381556001015f358155600101602035815560010160403590553360601b5f5260605f60143760745fa0600101600355005b6003546002548082038060021160e7575060025b5f5b8181146101295782810160040260040181607402815460601b815260140181600101548152602001816002015481526020019060030154905260010160e9565b910180921461013b5790600255610146565b90505f6002555f6003555b5f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff141561017357505f5b6001546001828201116101885750505f61018e565b01600190035b5f555f6001556074025ff35b5f5ffd", + "storage": {} + }, + "0x0000f90827f1c53a10cb7a02335b175320002935": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500", + "storage": {} + }, + "0x000f3df6d732807ef1319fb7b8bb8522d0beac02": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500", + "storage": {} + }, + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "nonce": "0x00", + "balance": "0x3635c9adc5dea00000", + "code": "0x", + "storage": {} + }, + "0x0000000000000000000000000000000000001000": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x60016000540160005500", + "storage": {} + } + }, + "postState": { + "0x00000000219ab540356cbb839cbe05303d7705fa": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x60806040526004361061003f5760003560e01c806301ffc9a71461004457806322895118146100a4578063621fd130146101ba578063c5f2892f14610244575b600080fd5b34801561005057600080fd5b506100906004803603602081101561006757600080fd5b50357fffffffff000000000000000000000000000000000000000000000000000000001661026b565b604080519115158252519081900360200190f35b6101b8600480360360808110156100ba57600080fd5b8101906020810181356401000000008111156100d557600080fd5b8201836020820111156100e757600080fd5b8035906020019184600183028401116401000000008311171561010957600080fd5b91939092909160208101903564010000000081111561012757600080fd5b82018360208201111561013957600080fd5b8035906020019184600183028401116401000000008311171561015b57600080fd5b91939092909160208101903564010000000081111561017957600080fd5b82018360208201111561018b57600080fd5b803590602001918460018302840111640100000000831117156101ad57600080fd5b919350915035610304565b005b3480156101c657600080fd5b506101cf6110b5565b6040805160208082528351818301528351919283929083019185019080838360005b838110156102095781810151838201526020016101f1565b50505050905090810190601f1680156102365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561025057600080fd5b506102596110c7565b60408051918252519081900360200190f35b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a70000000000000000000000000000000000000000000000000000000014806102fe57507fffffffff0000000000000000000000000000000000000000000000000000000082167f8564090700000000000000000000000000000000000000000000000000000000145b92915050565b6030861461035d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118056026913960400191505060405180910390fd5b602084146103b6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603681526020018061179c6036913960400191505060405180910390fd5b6060821461040f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260298152602001806118786029913960400191505060405180910390fd5b670de0b6b3a7640000341015610470576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118526026913960400191505060405180910390fd5b633b9aca003406156104cd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260338152602001806117d26033913960400191505060405180910390fd5b633b9aca00340467ffffffffffffffff811115610535576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061182b6027913960400191505060405180910390fd5b6060610540826114ba565b90507f649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c589898989858a8a6105756020546114ba565b6040805160a0808252810189905290819060208201908201606083016080840160c085018e8e80828437600083820152601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690910187810386528c815260200190508c8c808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690920188810386528c5181528c51602091820193918e019250908190849084905b83811015610648578181015183820152602001610630565b50505050905090810190601f1680156106755780820380516001836020036101000a031916815260200191505b5086810383528881526020018989808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169092018881038452895181528951602091820193918b019250908190849084905b838110156106ef5781810151838201526020016106d7565b50505050905090810190601f16801561071c5780820380516001836020036101000a031916815260200191505b509d505050505050505050505050505060405180910390a1600060028a8a600060801b604051602001808484808284377fffffffffffffffffffffffffffffffff0000000000000000000000000000000090941691909301908152604080517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0818403018152601090920190819052815191955093508392506020850191508083835b602083106107fc57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016107bf565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610859573d6000803e3d6000fd5b5050506040513d602081101561086e57600080fd5b5051905060006002806108846040848a8c6116fe565b6040516020018083838082843780830192505050925050506040516020818303038152906040526040518082805190602001908083835b602083106108f857805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016108bb565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610955573d6000803e3d6000fd5b5050506040513d602081101561096a57600080fd5b5051600261097b896040818d6116fe565b60405160009060200180848480828437919091019283525050604080518083038152602092830191829052805190945090925082918401908083835b602083106109f457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016109b7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610a51573d6000803e3d6000fd5b5050506040513d6020811015610a6657600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610ada57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610a9d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610b37573d6000803e3d6000fd5b5050506040513d6020811015610b4c57600080fd5b50516040805160208101858152929350600092600292839287928f928f92018383808284378083019250505093505050506040516020818303038152906040526040518082805190602001908083835b60208310610bd957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610b9c565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610c36573d6000803e3d6000fd5b5050506040513d6020811015610c4b57600080fd5b50516040518651600291889160009188916020918201918291908601908083835b60208310610ca957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610c6c565b6001836020036101000a0380198251168184511680821785525050505050509050018367ffffffffffffffff191667ffffffffffffffff1916815260180182815260200193505050506040516020818303038152906040526040518082805190602001908083835b60208310610d4e57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610d11565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610dab573d6000803e3d6000fd5b5050506040513d6020811015610dc057600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610e3457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610df7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610e91573d6000803e3d6000fd5b5050506040513d6020811015610ea657600080fd5b50519050858114610f02576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260548152602001806117486054913960600191505060405180910390fd5b60205463ffffffff11610f60576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806117276021913960400191505060405180910390fd5b602080546001019081905560005b60208110156110a9578160011660011415610fa0578260008260208110610f9157fe5b0155506110ac95505050505050565b600260008260208110610faf57fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061102557805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610fe8565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015611082573d6000803e3d6000fd5b5050506040513d602081101561109757600080fd5b50519250600282049150600101610f6e565b50fe5b50505050505050565b60606110c26020546114ba565b905090565b6020546000908190815b60208110156112f05781600116600114156111e6576002600082602081106110f557fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061116b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161112e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156111c8573d6000803e3d6000fd5b5050506040513d60208110156111dd57600080fd5b505192506112e2565b600283602183602081106111f657fe5b015460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061126b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161122e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156112c8573d6000803e3d6000fd5b5050506040513d60208110156112dd57600080fd5b505192505b6002820491506001016110d1565b506002826112ff6020546114ba565b600060401b6040516020018084815260200183805190602001908083835b6020831061135a57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161131d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790527fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000095909516920191825250604080518083037ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8018152601890920190819052815191955093508392850191508083835b6020831061143f57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101611402565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa15801561149c573d6000803e3d6000fd5b5050506040513d60208110156114b157600080fd5b50519250505090565b60408051600880825281830190925260609160208201818036833701905050905060c082901b8060071a60f81b826000815181106114f457fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060061a60f81b8260018151811061153757fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060051a60f81b8260028151811061157a57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060041a60f81b826003815181106115bd57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060031a60f81b8260048151811061160057fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060021a60f81b8260058151811061164357fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060011a60f81b8260068151811061168657fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060001a60f81b826007815181106116c957fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535050919050565b6000808585111561170d578182fd5b83861115611719578182fd5b505082019391909203915056fe4465706f736974436f6e74726163743a206d65726b6c6520747265652066756c6c4465706f736974436f6e74726163743a207265636f6e7374727563746564204465706f7369744461746120646f6573206e6f74206d6174636820737570706c696564206465706f7369745f646174615f726f6f744465706f736974436f6e74726163743a20696e76616c6964207769746864726177616c5f63726564656e7469616c73206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c7565206e6f74206d756c7469706c65206f6620677765694465706f736974436f6e74726163743a20696e76616c6964207075626b6579206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f20686967684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f206c6f774465706f736974436f6e74726163743a20696e76616c6964207369676e6174757265206c656e677468a2646970667358221220dceca8706b29e917dacf25fceef95acac8d90d765ac926663ce4096195952b6164736f6c634300060b0033", + "storage": { + "0x22": "0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b", + "0x23": "0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71", + "0x24": "0xc78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c", + "0x25": "0x536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c", + "0x26": "0x9efde052aa15429fae05bad4d0b1d7c64da64d03d7a1854a588c2cb8430c0d30", + "0x27": "0xd88ddfeed400a8755596b21942c1497e114c302e6118290f91e6772976041fa1", + "0x28": "0x87eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2c", + "0x29": "0x26846476fd5fc54a5d43385167c95144f2643f533cc85bb9d16b782f8d7db193", + "0x2a": "0x506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e1", + "0x2b": "0xffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b", + "0x2c": "0x6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220", + "0x2d": "0xb7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5f", + "0x2e": "0xdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85e", + "0x2f": "0xb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784", + "0x30": "0xd49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb", + "0x31": "0x8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb", + "0x32": "0x8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab", + "0x33": "0x95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4", + "0x34": "0xf893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17f", + "0x35": "0xcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa", + "0x36": "0x8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9c", + "0x37": "0xfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167", + "0x38": "0xe71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d7", + "0x39": "0x31206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc0", + "0x3a": "0x21352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544", + "0x3b": "0x619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765", + "0x3c": "0x7cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4", + "0x3d": "0x848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe1", + "0x3e": "0x8869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636", + "0x3f": "0xb5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c", + "0x40": "0x985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7" + } + }, + "0x00000961ef480eb55e80d19ad83579a64c007002": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe1460cb5760115f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff146101f457600182026001905f5b5f82111560685781019083028483029004916001019190604d565b909390049250505036603814608857366101f457346101f4575f5260205ff35b34106101f457600154600101600155600354806003026004013381556001015f35815560010160203590553360601b5f5260385f601437604c5fa0600101600355005b6003546002548082038060101160df575060105b5f5b8181146101835782810160030260040181604c02815460601b8152601401816001015481526020019060020154807fffffffffffffffffffffffffffffffff00000000000000000000000000000000168252906010019060401c908160381c81600701538160301c81600601538160281c81600501538160201c81600401538160181c81600301538160101c81600201538160081c81600101535360010160e1565b910180921461019557906002556101a0565b90505f6002555f6003555b5f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff14156101cd57505f5b6001546002828201116101e25750505f6101e8565b01600290035b5f555f600155604c025ff35b5f5ffd", + "storage": {} + }, + "0x0000bbddc7ce488642fb579f8b00f3a590007251": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe1460d35760115f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1461019a57600182026001905f5b5f82111560685781019083028483029004916001019190604d565b9093900492505050366060146088573661019a573461019a575f5260205ff35b341061019a57600154600101600155600354806004026004013381556001015f358155600101602035815560010160403590553360601b5f5260605f60143760745fa0600101600355005b6003546002548082038060021160e7575060025b5f5b8181146101295782810160040260040181607402815460601b815260140181600101548152602001816002015481526020019060030154905260010160e9565b910180921461013b5790600255610146565b90505f6002555f6003555b5f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff141561017357505f5b6001546001828201116101885750505f61018e565b01600190035b5f555f6001556074025ff35b5f5ffd", + "storage": {} + }, + "0x0000f90827f1c53a10cb7a02335b175320002935": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500", + "storage": { + "0x00": "0x04b688c25df122da84e0b1a21b90dd8f898e6005cdc2d1ac6c60ded9ff9b2de4" + } + }, + "0x000f3df6d732807ef1319fb7b8bb8522d0beac02": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500", + "storage": { + "0x1a98": "0x3a97" + } + }, + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "nonce": "0x01", + "balance": "0x3635c9adc5de996bf0", + "code": "0x", + "storage": {} + }, + "0x0000000000000000000000000000000000001000": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x60016000540160005500", + "storage": { + "0x00": "0x01" + } + }, + "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba": { + "nonce": "0x00", + "balance": "0x01f938", + "code": "0x", + "storage": {} + } + }, + "lastblockhash": "0xd4c4adfc3e91b0f8855851d598b43c9aa24e46dc03463a1e6e39154a3b9baf11", + "config": { + "network": "PragueToOsakaAtTime15k", + "chainid": "0x01", + "blobSchedule": { + "Cancun": { + "target": "0x03", + "max": "0x06", + "baseFeeUpdateFraction": "0x32f0ed" + }, + "Prague": { + "target": "0x06", + "max": "0x09", + "baseFeeUpdateFraction": "0x4c6964" + }, + "Osaka": { + "target": "0x06", + "max": "0x09", + "baseFeeUpdateFraction": "0x4c6964" + } + } + }, + "genesisRLP": "0xf9025df90257a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0fe13aa0b3a4ea731b1715a429c1cf100db415262a5bdd49478dc7b9e61cbf1dfa056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000808084044aa200808000a0000000000000000000000000000000000000000000000000000000000000000088000000000000000007a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a00000000000000000000000000000000000000000000000000000000000000000a0e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855c0c0c0", + "blocks": [ + { + "blockHeader": { + "parentHash": "0x04b688c25df122da84e0b1a21b90dd8f898e6005cdc2d1ac6c60ded9ff9b2de4", + "uncleHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "coinbase": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "stateRoot": "0xc1f2dd64894ad795674b904a05d8b1e25e44c1bcab551f891561505cf9d23ec0", + "transactionsTrie": "0x62a7a0c935742f0a198a50f095a7936080f099512e3c0f55cf245251e52b8956", + "receiptTrie": "0x06f890d54ec65d8650b6c73eefd1fbc39f78b5b25f4e1ec10885c9f29f84ee98", + "bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "difficulty": "0x00", + "number": "0x01", + "gasLimit": "0x044aa200", + "gasUsed": "0xa868", + "timestamp": "0x3a97", + "extraData": "0x", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0000000000000000", + "baseFeePerGas": "0x07", + "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "blobGasUsed": "0x00", + "excessBlobGas": "0x00", + "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "requestsHash": "0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "hash": "0xd4c4adfc3e91b0f8855851d598b43c9aa24e46dc03463a1e6e39154a3b9baf11" + }, + "transactions": [ + { + "type": "0x00", + "chainId": "0x01", + "nonce": "0x00", + "gasPrice": "0x0a", + "gasLimit": "0x01c9c381", + "to": "0x0000000000000000000000000000000000001000", + "value": "0x00", + "data": "0x", + "v": "0x26", + "r": "0xf6fc2259158f1ab63eef05e3a8c55ca90621f289349ed04f0bdb90190aea976d", + "s": "0x1298f50281fe143647f01741fb7e68f0dc82e47a05fe5a9b6193c1270c5e3658", + "sender": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" + } + ], + "uncleHeaders": [], + "withdrawals": [], + "rlp": "0xf902c5f9025ba004b688c25df122da84e0b1a21b90dd8f898e6005cdc2d1ac6c60ded9ff9b2de4a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa0c1f2dd64894ad795674b904a05d8b1e25e44c1bcab551f891561505cf9d23ec0a062a7a0c935742f0a198a50f095a7936080f099512e3c0f55cf245251e52b8956a006f890d54ec65d8650b6c73eefd1fbc39f78b5b25f4e1ec10885c9f29f84ee98b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800184044aa20082a868823a9780a0000000000000000000000000000000000000000000000000000000000000000088000000000000000007a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a00000000000000000000000000000000000000000000000000000000000000000a0e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855f863f861800a8401c9c381940000000000000000000000000000000000001000808026a0f6fc2259158f1ab63eef05e3a8c55ca90621f289349ed04f0bdb90190aea976da01298f50281fe143647f01741fb7e68f0dc82e47a05fe5a9b6193c1270c5e3658c0c0", + "blocknumber": "1" + }, + { + "rlp": "0xf902c3f90259a0d4c4adfc3e91b0f8855851d598b43c9aa24e46dc03463a1e6e39154a3b9baf11a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa050671d216437ec3e13a03ff464ed91146fd61165e8adc1622e58d9be953ae4a5a04657b722e50dc4184f522f842bee4abeae676efe01b43d96ca1ca68695982cfda056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800284044aa20080823a9880a0000000000000000000000000000000000000000000000000000000000000000088000000000000000007a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a00000000000000000000000000000000000000000000000000000000000000000a0e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855f863f861010a8401c9c381940000000000000000000000000000000000001000808025a032fd9f592d89b3468bcee7034fab624e546d8efb4f27d62a53e4f7b1382430cca06563508d39899cc529f5d0686a530ac145d68393c858f08edaada21f17e704b7c0c0", + "expectException": "TransactionException.GAS_LIMIT_EXCEEDS_MAXIMUM", + "rlp_decoded": { + "blockHeader": { + "parentHash": "0xd4c4adfc3e91b0f8855851d598b43c9aa24e46dc03463a1e6e39154a3b9baf11", + "uncleHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "coinbase": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "stateRoot": "0x50671d216437ec3e13a03ff464ed91146fd61165e8adc1622e58d9be953ae4a5", + "transactionsTrie": "0x4657b722e50dc4184f522f842bee4abeae676efe01b43d96ca1ca68695982cfd", + "receiptTrie": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "difficulty": "0x00", + "number": "0x02", + "gasLimit": "0x044aa200", + "gasUsed": "0x00", + "timestamp": "0x3a98", + "extraData": "0x", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0000000000000000", + "baseFeePerGas": "0x07", + "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "blobGasUsed": "0x00", + "excessBlobGas": "0x00", + "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "requestsHash": "0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "hash": "0x64bf114af5b2312b734d3f7a94f15858210602292527a869c05e4f3083585385" + }, + "transactions": [ + { + "type": "0x00", + "chainId": "0x01", + "nonce": "0x01", + "gasPrice": "0x0a", + "gasLimit": "0x01c9c381", + "to": "0x0000000000000000000000000000000000001000", + "value": "0x00", + "data": "0x", + "v": "0x25", + "r": "0x32fd9f592d89b3468bcee7034fab624e546d8efb4f27d62a53e4f7b1382430cc", + "s": "0x6563508d39899cc529f5d0686a530ac145d68393c858f08edaada21f17e704b7", + "sender": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" + } + ], + "uncleHeaders": [], + "withdrawals": [], + "blocknumber": "2" + } + } + ], + "sealEngine": "NoProof", + "_info": { + "hash": "0xa19f6207b969ec0c5baa2aa6218e6410818c163eb88be6b39e61955ed4cc50c5", + "comment": "`execution-spec-tests` generated test", + "filling-transition-tool": "ethereum-spec-evm-resolver 0.0.5", + "description": "Test transaction gas limit cap behavior at the Osaka transition.\n\n Before timestamp 15000: No gas limit cap (transactions with gas > 30M are valid)\n At/after timestamp 15000: Gas limit cap of 30M is enforced", + "url": "https://github.com/ethereum/execution-spec-tests/blob/fusaka-devnet-2@v1.2.0/tests/osaka/eip7825_transaction_gas_limit_cap/test_tx_gas_limit.py#L118", + "fixture-format": "blockchain_test", + "reference-spec": "https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7825.md", + "reference-spec-version": "47cbfed315988c0bd4d10002c110ae402504cd94", + "eels-resolution": { + "git-url": "https://github.com/spencer-tb/execution-specs.git", + "branch": "forks/osaka", + "commit": "bc829598ff1923f9215a6a407ef74621077fd3bb" + } + } + } +} + "#; +} diff --git a/crates/statetest-types/src/blockchain/sample.json b/crates/statetest-types/src/blockchain/sample.json new file mode 100644 index 0000000000..8d30cd99d6 --- /dev/null +++ b/crates/statetest-types/src/blockchain/sample.json @@ -0,0 +1,329 @@ +{ + "tests/osaka/eip7825_transaction_gas_limit_cap/test_tx_gas_limit.py::test_transaction_gas_limit_cap_at_transition[fork_PragueToOsakaAtTime15k-blockchain_test]": { + "network": "PragueToOsakaAtTime15k", + "genesisBlockHeader": { + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "uncleHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "coinbase": "0x0000000000000000000000000000000000000000", + "stateRoot": "0xfe13aa0b3a4ea731b1715a429c1cf100db415262a5bdd49478dc7b9e61cbf1df", + "transactionsTrie": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "receiptTrie": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "difficulty": "0x00", + "number": "0x00", + "gasLimit": "0x044aa200", + "gasUsed": "0x00", + "timestamp": "0x00", + "extraData": "0x00", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0000000000000000", + "baseFeePerGas": "0x07", + "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "blobGasUsed": "0x00", + "excessBlobGas": "0x00", + "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "requestsHash": "0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "hash": "0x04b688c25df122da84e0b1a21b90dd8f898e6005cdc2d1ac6c60ded9ff9b2de4" + }, + "pre": { + "0x00000000219ab540356cbb839cbe05303d7705fa": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x60806040526004361061003f5760003560e01c806301ffc9a71461004457806322895118146100a4578063621fd130146101ba578063c5f2892f14610244575b600080fd5b34801561005057600080fd5b506100906004803603602081101561006757600080fd5b50357fffffffff000000000000000000000000000000000000000000000000000000001661026b565b604080519115158252519081900360200190f35b6101b8600480360360808110156100ba57600080fd5b8101906020810181356401000000008111156100d557600080fd5b8201836020820111156100e757600080fd5b8035906020019184600183028401116401000000008311171561010957600080fd5b91939092909160208101903564010000000081111561012757600080fd5b82018360208201111561013957600080fd5b8035906020019184600183028401116401000000008311171561015b57600080fd5b91939092909160208101903564010000000081111561017957600080fd5b82018360208201111561018b57600080fd5b803590602001918460018302840111640100000000831117156101ad57600080fd5b919350915035610304565b005b3480156101c657600080fd5b506101cf6110b5565b6040805160208082528351818301528351919283929083019185019080838360005b838110156102095781810151838201526020016101f1565b50505050905090810190601f1680156102365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561025057600080fd5b506102596110c7565b60408051918252519081900360200190f35b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a70000000000000000000000000000000000000000000000000000000014806102fe57507fffffffff0000000000000000000000000000000000000000000000000000000082167f8564090700000000000000000000000000000000000000000000000000000000145b92915050565b6030861461035d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118056026913960400191505060405180910390fd5b602084146103b6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603681526020018061179c6036913960400191505060405180910390fd5b6060821461040f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260298152602001806118786029913960400191505060405180910390fd5b670de0b6b3a7640000341015610470576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118526026913960400191505060405180910390fd5b633b9aca003406156104cd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260338152602001806117d26033913960400191505060405180910390fd5b633b9aca00340467ffffffffffffffff811115610535576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061182b6027913960400191505060405180910390fd5b6060610540826114ba565b90507f649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c589898989858a8a6105756020546114ba565b6040805160a0808252810189905290819060208201908201606083016080840160c085018e8e80828437600083820152601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690910187810386528c815260200190508c8c808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690920188810386528c5181528c51602091820193918e019250908190849084905b83811015610648578181015183820152602001610630565b50505050905090810190601f1680156106755780820380516001836020036101000a031916815260200191505b5086810383528881526020018989808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169092018881038452895181528951602091820193918b019250908190849084905b838110156106ef5781810151838201526020016106d7565b50505050905090810190601f16801561071c5780820380516001836020036101000a031916815260200191505b509d505050505050505050505050505060405180910390a1600060028a8a600060801b604051602001808484808284377fffffffffffffffffffffffffffffffff0000000000000000000000000000000090941691909301908152604080517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0818403018152601090920190819052815191955093508392506020850191508083835b602083106107fc57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016107bf565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610859573d6000803e3d6000fd5b5050506040513d602081101561086e57600080fd5b5051905060006002806108846040848a8c6116fe565b6040516020018083838082843780830192505050925050506040516020818303038152906040526040518082805190602001908083835b602083106108f857805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016108bb565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610955573d6000803e3d6000fd5b5050506040513d602081101561096a57600080fd5b5051600261097b896040818d6116fe565b60405160009060200180848480828437919091019283525050604080518083038152602092830191829052805190945090925082918401908083835b602083106109f457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016109b7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610a51573d6000803e3d6000fd5b5050506040513d6020811015610a6657600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610ada57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610a9d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610b37573d6000803e3d6000fd5b5050506040513d6020811015610b4c57600080fd5b50516040805160208101858152929350600092600292839287928f928f92018383808284378083019250505093505050506040516020818303038152906040526040518082805190602001908083835b60208310610bd957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610b9c565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610c36573d6000803e3d6000fd5b5050506040513d6020811015610c4b57600080fd5b50516040518651600291889160009188916020918201918291908601908083835b60208310610ca957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610c6c565b6001836020036101000a0380198251168184511680821785525050505050509050018367ffffffffffffffff191667ffffffffffffffff1916815260180182815260200193505050506040516020818303038152906040526040518082805190602001908083835b60208310610d4e57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610d11565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610dab573d6000803e3d6000fd5b5050506040513d6020811015610dc057600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610e3457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610df7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610e91573d6000803e3d6000fd5b5050506040513d6020811015610ea657600080fd5b50519050858114610f02576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260548152602001806117486054913960600191505060405180910390fd5b60205463ffffffff11610f60576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806117276021913960400191505060405180910390fd5b602080546001019081905560005b60208110156110a9578160011660011415610fa0578260008260208110610f9157fe5b0155506110ac95505050505050565b600260008260208110610faf57fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061102557805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610fe8565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015611082573d6000803e3d6000fd5b5050506040513d602081101561109757600080fd5b50519250600282049150600101610f6e565b50fe5b50505050505050565b60606110c26020546114ba565b905090565b6020546000908190815b60208110156112f05781600116600114156111e6576002600082602081106110f557fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061116b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161112e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156111c8573d6000803e3d6000fd5b5050506040513d60208110156111dd57600080fd5b505192506112e2565b600283602183602081106111f657fe5b015460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061126b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161122e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156112c8573d6000803e3d6000fd5b5050506040513d60208110156112dd57600080fd5b505192505b6002820491506001016110d1565b506002826112ff6020546114ba565b600060401b6040516020018084815260200183805190602001908083835b6020831061135a57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161131d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790527fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000095909516920191825250604080518083037ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8018152601890920190819052815191955093508392850191508083835b6020831061143f57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101611402565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa15801561149c573d6000803e3d6000fd5b5050506040513d60208110156114b157600080fd5b50519250505090565b60408051600880825281830190925260609160208201818036833701905050905060c082901b8060071a60f81b826000815181106114f457fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060061a60f81b8260018151811061153757fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060051a60f81b8260028151811061157a57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060041a60f81b826003815181106115bd57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060031a60f81b8260048151811061160057fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060021a60f81b8260058151811061164357fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060011a60f81b8260068151811061168657fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060001a60f81b826007815181106116c957fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535050919050565b6000808585111561170d578182fd5b83861115611719578182fd5b505082019391909203915056fe4465706f736974436f6e74726163743a206d65726b6c6520747265652066756c6c4465706f736974436f6e74726163743a207265636f6e7374727563746564204465706f7369744461746120646f6573206e6f74206d6174636820737570706c696564206465706f7369745f646174615f726f6f744465706f736974436f6e74726163743a20696e76616c6964207769746864726177616c5f63726564656e7469616c73206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c7565206e6f74206d756c7469706c65206f6620677765694465706f736974436f6e74726163743a20696e76616c6964207075626b6579206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f20686967684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f206c6f774465706f736974436f6e74726163743a20696e76616c6964207369676e6174757265206c656e677468a2646970667358221220dceca8706b29e917dacf25fceef95acac8d90d765ac926663ce4096195952b6164736f6c634300060b0033", + "storage": { + "0x22": "0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b", + "0x23": "0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71", + "0x24": "0xc78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c", + "0x25": "0x536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c", + "0x26": "0x9efde052aa15429fae05bad4d0b1d7c64da64d03d7a1854a588c2cb8430c0d30", + "0x27": "0xd88ddfeed400a8755596b21942c1497e114c302e6118290f91e6772976041fa1", + "0x28": "0x87eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2c", + "0x29": "0x26846476fd5fc54a5d43385167c95144f2643f533cc85bb9d16b782f8d7db193", + "0x2a": "0x506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e1", + "0x2b": "0xffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b", + "0x2c": "0x6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220", + "0x2d": "0xb7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5f", + "0x2e": "0xdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85e", + "0x2f": "0xb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784", + "0x30": "0xd49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb", + "0x31": "0x8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb", + "0x32": "0x8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab", + "0x33": "0x95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4", + "0x34": "0xf893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17f", + "0x35": "0xcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa", + "0x36": "0x8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9c", + "0x37": "0xfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167", + "0x38": "0xe71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d7", + "0x39": "0x31206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc0", + "0x3a": "0x21352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544", + "0x3b": "0x619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765", + "0x3c": "0x7cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4", + "0x3d": "0x848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe1", + "0x3e": "0x8869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636", + "0x3f": "0xb5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c", + "0x40": "0x985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7" + } + }, + "0x00000961ef480eb55e80d19ad83579a64c007002": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe1460cb5760115f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff146101f457600182026001905f5b5f82111560685781019083028483029004916001019190604d565b909390049250505036603814608857366101f457346101f4575f5260205ff35b34106101f457600154600101600155600354806003026004013381556001015f35815560010160203590553360601b5f5260385f601437604c5fa0600101600355005b6003546002548082038060101160df575060105b5f5b8181146101835782810160030260040181604c02815460601b8152601401816001015481526020019060020154807fffffffffffffffffffffffffffffffff00000000000000000000000000000000168252906010019060401c908160381c81600701538160301c81600601538160281c81600501538160201c81600401538160181c81600301538160101c81600201538160081c81600101535360010160e1565b910180921461019557906002556101a0565b90505f6002555f6003555b5f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff14156101cd57505f5b6001546002828201116101e25750505f6101e8565b01600290035b5f555f600155604c025ff35b5f5ffd", + "storage": {} + }, + "0x0000bbddc7ce488642fb579f8b00f3a590007251": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe1460d35760115f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1461019a57600182026001905f5b5f82111560685781019083028483029004916001019190604d565b9093900492505050366060146088573661019a573461019a575f5260205ff35b341061019a57600154600101600155600354806004026004013381556001015f358155600101602035815560010160403590553360601b5f5260605f60143760745fa0600101600355005b6003546002548082038060021160e7575060025b5f5b8181146101295782810160040260040181607402815460601b815260140181600101548152602001816002015481526020019060030154905260010160e9565b910180921461013b5790600255610146565b90505f6002555f6003555b5f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff141561017357505f5b6001546001828201116101885750505f61018e565b01600190035b5f555f6001556074025ff35b5f5ffd", + "storage": {} + }, + "0x0000f90827f1c53a10cb7a02335b175320002935": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500", + "storage": {} + }, + "0x000f3df6d732807ef1319fb7b8bb8522d0beac02": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500", + "storage": {} + }, + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "nonce": "0x00", + "balance": "0x3635c9adc5dea00000", + "code": "0x", + "storage": {} + }, + "0x0000000000000000000000000000000000001000": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x60016000540160005500", + "storage": {} + } + }, + "postState": { + "0x00000000219ab540356cbb839cbe05303d7705fa": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x60806040526004361061003f5760003560e01c806301ffc9a71461004457806322895118146100a4578063621fd130146101ba578063c5f2892f14610244575b600080fd5b34801561005057600080fd5b506100906004803603602081101561006757600080fd5b50357fffffffff000000000000000000000000000000000000000000000000000000001661026b565b604080519115158252519081900360200190f35b6101b8600480360360808110156100ba57600080fd5b8101906020810181356401000000008111156100d557600080fd5b8201836020820111156100e757600080fd5b8035906020019184600183028401116401000000008311171561010957600080fd5b91939092909160208101903564010000000081111561012757600080fd5b82018360208201111561013957600080fd5b8035906020019184600183028401116401000000008311171561015b57600080fd5b91939092909160208101903564010000000081111561017957600080fd5b82018360208201111561018b57600080fd5b803590602001918460018302840111640100000000831117156101ad57600080fd5b919350915035610304565b005b3480156101c657600080fd5b506101cf6110b5565b6040805160208082528351818301528351919283929083019185019080838360005b838110156102095781810151838201526020016101f1565b50505050905090810190601f1680156102365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561025057600080fd5b506102596110c7565b60408051918252519081900360200190f35b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a70000000000000000000000000000000000000000000000000000000014806102fe57507fffffffff0000000000000000000000000000000000000000000000000000000082167f8564090700000000000000000000000000000000000000000000000000000000145b92915050565b6030861461035d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118056026913960400191505060405180910390fd5b602084146103b6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603681526020018061179c6036913960400191505060405180910390fd5b6060821461040f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260298152602001806118786029913960400191505060405180910390fd5b670de0b6b3a7640000341015610470576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118526026913960400191505060405180910390fd5b633b9aca003406156104cd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260338152602001806117d26033913960400191505060405180910390fd5b633b9aca00340467ffffffffffffffff811115610535576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061182b6027913960400191505060405180910390fd5b6060610540826114ba565b90507f649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c589898989858a8a6105756020546114ba565b6040805160a0808252810189905290819060208201908201606083016080840160c085018e8e80828437600083820152601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690910187810386528c815260200190508c8c808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690920188810386528c5181528c51602091820193918e019250908190849084905b83811015610648578181015183820152602001610630565b50505050905090810190601f1680156106755780820380516001836020036101000a031916815260200191505b5086810383528881526020018989808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169092018881038452895181528951602091820193918b019250908190849084905b838110156106ef5781810151838201526020016106d7565b50505050905090810190601f16801561071c5780820380516001836020036101000a031916815260200191505b509d505050505050505050505050505060405180910390a1600060028a8a600060801b604051602001808484808284377fffffffffffffffffffffffffffffffff0000000000000000000000000000000090941691909301908152604080517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0818403018152601090920190819052815191955093508392506020850191508083835b602083106107fc57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016107bf565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610859573d6000803e3d6000fd5b5050506040513d602081101561086e57600080fd5b5051905060006002806108846040848a8c6116fe565b6040516020018083838082843780830192505050925050506040516020818303038152906040526040518082805190602001908083835b602083106108f857805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016108bb565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610955573d6000803e3d6000fd5b5050506040513d602081101561096a57600080fd5b5051600261097b896040818d6116fe565b60405160009060200180848480828437919091019283525050604080518083038152602092830191829052805190945090925082918401908083835b602083106109f457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016109b7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610a51573d6000803e3d6000fd5b5050506040513d6020811015610a6657600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610ada57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610a9d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610b37573d6000803e3d6000fd5b5050506040513d6020811015610b4c57600080fd5b50516040805160208101858152929350600092600292839287928f928f92018383808284378083019250505093505050506040516020818303038152906040526040518082805190602001908083835b60208310610bd957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610b9c565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610c36573d6000803e3d6000fd5b5050506040513d6020811015610c4b57600080fd5b50516040518651600291889160009188916020918201918291908601908083835b60208310610ca957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610c6c565b6001836020036101000a0380198251168184511680821785525050505050509050018367ffffffffffffffff191667ffffffffffffffff1916815260180182815260200193505050506040516020818303038152906040526040518082805190602001908083835b60208310610d4e57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610d11565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610dab573d6000803e3d6000fd5b5050506040513d6020811015610dc057600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610e3457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610df7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610e91573d6000803e3d6000fd5b5050506040513d6020811015610ea657600080fd5b50519050858114610f02576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260548152602001806117486054913960600191505060405180910390fd5b60205463ffffffff11610f60576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806117276021913960400191505060405180910390fd5b602080546001019081905560005b60208110156110a9578160011660011415610fa0578260008260208110610f9157fe5b0155506110ac95505050505050565b600260008260208110610faf57fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061102557805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610fe8565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015611082573d6000803e3d6000fd5b5050506040513d602081101561109757600080fd5b50519250600282049150600101610f6e565b50fe5b50505050505050565b60606110c26020546114ba565b905090565b6020546000908190815b60208110156112f05781600116600114156111e6576002600082602081106110f557fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061116b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161112e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156111c8573d6000803e3d6000fd5b5050506040513d60208110156111dd57600080fd5b505192506112e2565b600283602183602081106111f657fe5b015460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061126b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161122e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156112c8573d6000803e3d6000fd5b5050506040513d60208110156112dd57600080fd5b505192505b6002820491506001016110d1565b506002826112ff6020546114ba565b600060401b6040516020018084815260200183805190602001908083835b6020831061135a57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161131d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790527fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000095909516920191825250604080518083037ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8018152601890920190819052815191955093508392850191508083835b6020831061143f57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101611402565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa15801561149c573d6000803e3d6000fd5b5050506040513d60208110156114b157600080fd5b50519250505090565b60408051600880825281830190925260609160208201818036833701905050905060c082901b8060071a60f81b826000815181106114f457fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060061a60f81b8260018151811061153757fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060051a60f81b8260028151811061157a57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060041a60f81b826003815181106115bd57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060031a60f81b8260048151811061160057fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060021a60f81b8260058151811061164357fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060011a60f81b8260068151811061168657fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060001a60f81b826007815181106116c957fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535050919050565b6000808585111561170d578182fd5b83861115611719578182fd5b505082019391909203915056fe4465706f736974436f6e74726163743a206d65726b6c6520747265652066756c6c4465706f736974436f6e74726163743a207265636f6e7374727563746564204465706f7369744461746120646f6573206e6f74206d6174636820737570706c696564206465706f7369745f646174615f726f6f744465706f736974436f6e74726163743a20696e76616c6964207769746864726177616c5f63726564656e7469616c73206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c7565206e6f74206d756c7469706c65206f6620677765694465706f736974436f6e74726163743a20696e76616c6964207075626b6579206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f20686967684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f206c6f774465706f736974436f6e74726163743a20696e76616c6964207369676e6174757265206c656e677468a2646970667358221220dceca8706b29e917dacf25fceef95acac8d90d765ac926663ce4096195952b6164736f6c634300060b0033", + "storage": { + "0x22": "0xf5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b", + "0x23": "0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71", + "0x24": "0xc78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c", + "0x25": "0x536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c", + "0x26": "0x9efde052aa15429fae05bad4d0b1d7c64da64d03d7a1854a588c2cb8430c0d30", + "0x27": "0xd88ddfeed400a8755596b21942c1497e114c302e6118290f91e6772976041fa1", + "0x28": "0x87eb0ddba57e35f6d286673802a4af5975e22506c7cf4c64bb6be5ee11527f2c", + "0x29": "0x26846476fd5fc54a5d43385167c95144f2643f533cc85bb9d16b782f8d7db193", + "0x2a": "0x506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e1", + "0x2b": "0xffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b", + "0x2c": "0x6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220", + "0x2d": "0xb7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5f", + "0x2e": "0xdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85e", + "0x2f": "0xb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784", + "0x30": "0xd49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb", + "0x31": "0x8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb", + "0x32": "0x8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab", + "0x33": "0x95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4", + "0x34": "0xf893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17f", + "0x35": "0xcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa", + "0x36": "0x8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9c", + "0x37": "0xfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167", + "0x38": "0xe71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d7", + "0x39": "0x31206fa80a50bb6abe29085058f16212212a60eec8f049fecb92d8c8e0a84bc0", + "0x3a": "0x21352bfecbeddde993839f614c3dac0a3ee37543f9b412b16199dc158e23b544", + "0x3b": "0x619e312724bb6d7c3153ed9de791d764a366b389af13c58bf8a8d90481a46765", + "0x3c": "0x7cdd2986268250628d0c10e385c58c6191e6fbe05191bcc04f133f2cea72c1c4", + "0x3d": "0x848930bd7ba8cac54661072113fb278869e07bb8587f91392933374d017bcbe1", + "0x3e": "0x8869ff2c22b28cc10510d9853292803328be4fb0e80495e8bb8d271f5b889636", + "0x3f": "0xb5fe28e79f1b850f8658246ce9b6a1e7b49fc06db7143e8fe0b4f2b0c5523a5c", + "0x40": "0x985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7" + } + }, + "0x00000961ef480eb55e80d19ad83579a64c007002": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe1460cb5760115f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff146101f457600182026001905f5b5f82111560685781019083028483029004916001019190604d565b909390049250505036603814608857366101f457346101f4575f5260205ff35b34106101f457600154600101600155600354806003026004013381556001015f35815560010160203590553360601b5f5260385f601437604c5fa0600101600355005b6003546002548082038060101160df575060105b5f5b8181146101835782810160030260040181604c02815460601b8152601401816001015481526020019060020154807fffffffffffffffffffffffffffffffff00000000000000000000000000000000168252906010019060401c908160381c81600701538160301c81600601538160281c81600501538160201c81600401538160181c81600301538160101c81600201538160081c81600101535360010160e1565b910180921461019557906002556101a0565b90505f6002555f6003555b5f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff14156101cd57505f5b6001546002828201116101e25750505f6101e8565b01600290035b5f555f600155604c025ff35b5f5ffd", + "storage": {} + }, + "0x0000bbddc7ce488642fb579f8b00f3a590007251": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe1460d35760115f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1461019a57600182026001905f5b5f82111560685781019083028483029004916001019190604d565b9093900492505050366060146088573661019a573461019a575f5260205ff35b341061019a57600154600101600155600354806004026004013381556001015f358155600101602035815560010160403590553360601b5f5260605f60143760745fa0600101600355005b6003546002548082038060021160e7575060025b5f5b8181146101295782810160040260040181607402815460601b815260140181600101548152602001816002015481526020019060030154905260010160e9565b910180921461013b5790600255610146565b90505f6002555f6003555b5f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff141561017357505f5b6001546001828201116101885750505f61018e565b01600190035b5f555f6001556074025ff35b5f5ffd", + "storage": {} + }, + "0x0000f90827f1c53a10cb7a02335b175320002935": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500", + "storage": { + "0x00": "0x04b688c25df122da84e0b1a21b90dd8f898e6005cdc2d1ac6c60ded9ff9b2de4" + } + }, + "0x000f3df6d732807ef1319fb7b8bb8522d0beac02": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500", + "storage": { + "0x1a98": "0x3a97" + } + }, + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "nonce": "0x01", + "balance": "0x3635c9adc5de996bf0", + "code": "0x", + "storage": {} + }, + "0x0000000000000000000000000000000000001000": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x60016000540160005500", + "storage": { + "0x00": "0x01" + } + }, + "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba": { + "nonce": "0x00", + "balance": "0x01f938", + "code": "0x", + "storage": {} + } + }, + "lastblockhash": "0xd4c4adfc3e91b0f8855851d598b43c9aa24e46dc03463a1e6e39154a3b9baf11", + "config": { + "network": "PragueToOsakaAtTime15k", + "chainid": "0x01", + "blobSchedule": { + "Cancun": { + "target": "0x03", + "max": "0x06", + "baseFeeUpdateFraction": "0x32f0ed" + }, + "Prague": { + "target": "0x06", + "max": "0x09", + "baseFeeUpdateFraction": "0x4c6964" + }, + "Osaka": { + "target": "0x06", + "max": "0x09", + "baseFeeUpdateFraction": "0x4c6964" + } + } + }, + "genesisRLP": "0xf9025df90257a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0fe13aa0b3a4ea731b1715a429c1cf100db415262a5bdd49478dc7b9e61cbf1dfa056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000808084044aa200808000a0000000000000000000000000000000000000000000000000000000000000000088000000000000000007a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a00000000000000000000000000000000000000000000000000000000000000000a0e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855c0c0c0", + "blocks": [ + { + "blockHeader": { + "parentHash": "0x04b688c25df122da84e0b1a21b90dd8f898e6005cdc2d1ac6c60ded9ff9b2de4", + "uncleHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "coinbase": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "stateRoot": "0xc1f2dd64894ad795674b904a05d8b1e25e44c1bcab551f891561505cf9d23ec0", + "transactionsTrie": "0x62a7a0c935742f0a198a50f095a7936080f099512e3c0f55cf245251e52b8956", + "receiptTrie": "0x06f890d54ec65d8650b6c73eefd1fbc39f78b5b25f4e1ec10885c9f29f84ee98", + "bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "difficulty": "0x00", + "number": "0x01", + "gasLimit": "0x044aa200", + "gasUsed": "0xa868", + "timestamp": "0x3a97", + "extraData": "0x", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0000000000000000", + "baseFeePerGas": "0x07", + "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "blobGasUsed": "0x00", + "excessBlobGas": "0x00", + "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "requestsHash": "0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "hash": "0xd4c4adfc3e91b0f8855851d598b43c9aa24e46dc03463a1e6e39154a3b9baf11" + }, + "transactions": [ + { + "type": "0x00", + "chainId": "0x01", + "nonce": "0x00", + "gasPrice": "0x0a", + "gasLimit": "0x01c9c381", + "to": "0x0000000000000000000000000000000000001000", + "value": "0x00", + "data": "0x", + "v": "0x26", + "r": "0xf6fc2259158f1ab63eef05e3a8c55ca90621f289349ed04f0bdb90190aea976d", + "s": "0x1298f50281fe143647f01741fb7e68f0dc82e47a05fe5a9b6193c1270c5e3658", + "sender": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" + } + ], + "uncleHeaders": [], + "withdrawals": [], + "rlp": "0xf902c5f9025ba004b688c25df122da84e0b1a21b90dd8f898e6005cdc2d1ac6c60ded9ff9b2de4a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa0c1f2dd64894ad795674b904a05d8b1e25e44c1bcab551f891561505cf9d23ec0a062a7a0c935742f0a198a50f095a7936080f099512e3c0f55cf245251e52b8956a006f890d54ec65d8650b6c73eefd1fbc39f78b5b25f4e1ec10885c9f29f84ee98b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800184044aa20082a868823a9780a0000000000000000000000000000000000000000000000000000000000000000088000000000000000007a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a00000000000000000000000000000000000000000000000000000000000000000a0e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855f863f861800a8401c9c381940000000000000000000000000000000000001000808026a0f6fc2259158f1ab63eef05e3a8c55ca90621f289349ed04f0bdb90190aea976da01298f50281fe143647f01741fb7e68f0dc82e47a05fe5a9b6193c1270c5e3658c0c0", + "blocknumber": "1" + }, + { + "rlp": "0xf902c3f90259a0d4c4adfc3e91b0f8855851d598b43c9aa24e46dc03463a1e6e39154a3b9baf11a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa050671d216437ec3e13a03ff464ed91146fd61165e8adc1622e58d9be953ae4a5a04657b722e50dc4184f522f842bee4abeae676efe01b43d96ca1ca68695982cfda056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800284044aa20080823a9880a0000000000000000000000000000000000000000000000000000000000000000088000000000000000007a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a00000000000000000000000000000000000000000000000000000000000000000a0e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855f863f861010a8401c9c381940000000000000000000000000000000000001000808025a032fd9f592d89b3468bcee7034fab624e546d8efb4f27d62a53e4f7b1382430cca06563508d39899cc529f5d0686a530ac145d68393c858f08edaada21f17e704b7c0c0", + "expectException": "TransactionException.GAS_LIMIT_EXCEEDS_MAXIMUM", + "rlp_decoded": { + "blockHeader": { + "parentHash": "0xd4c4adfc3e91b0f8855851d598b43c9aa24e46dc03463a1e6e39154a3b9baf11", + "uncleHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", + "coinbase": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", + "stateRoot": "0x50671d216437ec3e13a03ff464ed91146fd61165e8adc1622e58d9be953ae4a5", + "transactionsTrie": "0x4657b722e50dc4184f522f842bee4abeae676efe01b43d96ca1ca68695982cfd", + "receiptTrie": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "difficulty": "0x00", + "number": "0x02", + "gasLimit": "0x044aa200", + "gasUsed": "0x00", + "timestamp": "0x3a98", + "extraData": "0x", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0000000000000000", + "baseFeePerGas": "0x07", + "withdrawalsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "blobGasUsed": "0x00", + "excessBlobGas": "0x00", + "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "requestsHash": "0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "hash": "0x64bf114af5b2312b734d3f7a94f15858210602292527a869c05e4f3083585385" + }, + "transactions": [ + { + "type": "0x00", + "chainId": "0x01", + "nonce": "0x01", + "gasPrice": "0x0a", + "gasLimit": "0x01c9c381", + "to": "0x0000000000000000000000000000000000001000", + "value": "0x00", + "data": "0x", + "v": "0x25", + "r": "0x32fd9f592d89b3468bcee7034fab624e546d8efb4f27d62a53e4f7b1382430cc", + "s": "0x6563508d39899cc529f5d0686a530ac145d68393c858f08edaada21f17e704b7", + "sender": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" + } + ], + "uncleHeaders": [], + "withdrawals": [], + "blocknumber": "2" + } + } + ], + "sealEngine": "NoProof", + "_info": { + "hash": "0xa19f6207b969ec0c5baa2aa6218e6410818c163eb88be6b39e61955ed4cc50c5", + "comment": "`execution-spec-tests` generated test", + "filling-transition-tool": "ethereum-spec-evm-resolver 0.0.5", + "description": "Test transaction gas limit cap behavior at the Osaka transition.\n\n Before timestamp 15000: No gas limit cap (transactions with gas > 30M are valid)\n At/after timestamp 15000: Gas limit cap of 30M is enforced", + "url": "https://github.com/ethereum/execution-spec-tests/blob/fusaka-devnet-2@v1.2.0/tests/osaka/eip7825_transaction_gas_limit_cap/test_tx_gas_limit.py#L118", + "fixture-format": "blockchain_test", + "reference-spec": "https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7825.md", + "reference-spec-version": "47cbfed315988c0bd4d10002c110ae402504cd94", + "eels-resolution": { + "git-url": "https://github.com/spencer-tb/execution-specs.git", + "branch": "forks/osaka", + "commit": "bc829598ff1923f9215a6a407ef74621077fd3bb" + } + } + } +} \ No newline at end of file diff --git a/crates/statetest-types/src/env.rs b/crates/statetest-types/src/env.rs index 36995efe2c..ae99f6538a 100644 --- a/crates/statetest-types/src/env.rs +++ b/crates/statetest-types/src/env.rs @@ -31,12 +31,6 @@ pub struct Env { /// Current withdrawals root pub current_withdrawals_root: Option, - /// Parent block blob gas used (EIP-4844) - pub parent_blob_gas_used: Option, - /// Parent block excess blob gas (EIP-4844) - pub parent_excess_blob_gas: Option, - /// Parent block target blobs per block (EIP-4844) - pub parent_target_blobs_per_block: Option, /// Current block excess blob gas (EIP-4844) pub current_excess_blob_gas: Option, } diff --git a/crates/statetest-types/src/lib.rs b/crates/statetest-types/src/lib.rs index e407782de8..2a62090310 100644 --- a/crates/statetest-types/src/lib.rs +++ b/crates/statetest-types/src/lib.rs @@ -7,6 +7,8 @@ //! test cases, and transaction data used in Ethereum state tests. mod account_info; +/// Blockchain test types +pub mod blockchain; mod deserializer; mod env; mod error; diff --git a/crates/statetest-types/src/test_unit.rs b/crates/statetest-types/src/test_unit.rs index f92caf4fe6..c877fe2bcd 100644 --- a/crates/statetest-types/src/test_unit.rs +++ b/crates/statetest-types/src/test_unit.rs @@ -1,17 +1,12 @@ -use serde::Deserialize; -use std::collections::{BTreeMap, HashMap}; - use crate::{AccountInfo, Env, SpecName, Test, TransactionParts}; use revm::{ context::{block::BlockEnv, cfg::CfgEnv}, - context_interface::block::calc_excess_blob_gas, database::CacheState, - primitives::{ - eip4844::TARGET_BLOB_GAS_PER_BLOCK_CANCUN, hardfork::SpecId, keccak256, Address, Bytes, - B256, - }, + primitives::{hardfork::SpecId, keccak256, Address, Bytes, HashMap, B256}, state::Bytecode, }; +use serde::Deserialize; +use std::collections::BTreeMap; /// Single test unit struct #[derive(Debug, PartialEq, Eq, Deserialize)] @@ -122,21 +117,6 @@ impl TestUnit { current_excess_blob_gas.to(), revm::primitives::eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN, ); - } else if let (Some(parent_blob_gas_used), Some(parent_excess_blob_gas)) = ( - self.env.parent_blob_gas_used, - self.env.parent_excess_blob_gas, - ) { - block.set_blob_excess_gas_and_price( - calc_excess_blob_gas( - parent_blob_gas_used.to(), - parent_excess_blob_gas.to(), - self.env - .parent_target_blobs_per_block - .map(|i| i.to()) - .unwrap_or(TARGET_BLOB_GAS_PER_BLOCK_CANCUN), - ), - revm::primitives::eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN, - ); } // Set default prevrandao for merge diff --git a/examples/cheatcode_inspector/src/main.rs b/examples/cheatcode_inspector/src/main.rs index 0e5edb9f28..fd3fd7b0c4 100644 --- a/examples/cheatcode_inspector/src/main.rs +++ b/examples/cheatcode_inspector/src/main.rs @@ -8,7 +8,9 @@ use revm::{ context::{ - result::InvalidTransaction, BlockEnv, Cfg, CfgEnv, ContextTr, Evm, LocalContext, TxEnv, + journaled_state::{AccountInfoLoad, JournalLoadError}, + result::InvalidTransaction, + BlockEnv, Cfg, CfgEnv, ContextTr, Evm, LocalContext, TxEnv, }, context_interface::{ journaled_state::{AccountLoad, JournalCheckpoint, TransferError}, @@ -58,6 +60,7 @@ impl Backend { impl JournalTr for Backend { type Database = InMemoryDB; type State = EvmState; + type JournalEntry = JournalEntry; fn new(database: InMemoryDB) -> Self { Self::new(SpecId::default(), database) @@ -108,13 +111,11 @@ impl JournalTr for Backend { self.journaled_state.selfdestruct(address, target) } - 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) { @@ -146,15 +147,24 @@ impl JournalTr for Backend { self.journaled_state.transfer(from, to, balance) } - fn load_account(&mut self, address: Address) -> Result, Infallible> { + fn transfer_loaded( + &mut self, + from: Address, + to: Address, + balance: U256, + ) -> Option { + self.journaled_state.transfer_loaded(from, to, balance) + } + + 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( @@ -254,6 +264,53 @@ impl JournalTr for Backend { fn discard_tx(&mut self) { self.journaled_state.discard_tx() } + + fn sload_skip_cold_load( + &mut self, + address: Address, + key: StorageKey, + skip_cold_load: bool, + ) -> Result, JournalLoadError<::Error>> + { + self.journaled_state + .sload_skip_cold_load(address, key, skip_cold_load) + } + + fn sstore_skip_cold_load( + &mut self, + address: Address, + key: StorageKey, + value: StorageValue, + skip_cold_load: bool, + ) -> Result, JournalLoadError<::Error>> + { + self.journaled_state + .sstore_skip_cold_load(address, key, value, skip_cold_load) + } + + fn load_account_info_skip_cold_load( + &mut self, + address: Address, + load_code: bool, + skip_cold_load: bool, + ) -> Result, JournalLoadError<::Error>> { + self.journaled_state + .load_account_info_skip_cold_load(address, load_code, skip_cold_load) + } + + fn load_account_mut_optional_code( + &mut self, + address: Address, + load_code: bool, + ) -> Result< + StateLoad< + revm::context::journaled_state::account::JournaledAccount<'_, Self::JournalEntry>, + >, + ::Error, + > { + self.journaled_state + .load_account_mut_optional_code(address, load_code) + } } impl JournalExt for Backend { diff --git a/examples/custom_precompile_journal/src/custom_evm.rs b/examples/custom_precompile_journal/src/custom_evm.rs index 02222c8fd2..47b1f59344 100644 --- a/examples/custom_precompile_journal/src/custom_evm.rs +++ b/examples/custom_precompile_journal/src/custom_evm.rs @@ -67,24 +67,26 @@ where type Precompiles = CustomPrecompileProvider; type Frame = EthFrame; - fn ctx(&mut self) -> &mut Self::Context { - &mut self.0.ctx - } - - fn ctx_ref(&self) -> &Self::Context { - self.0.ctx_ref() - } - - fn ctx_instructions(&mut self) -> (&mut Self::Context, &mut Self::Instructions) { - self.0.ctx_instructions() - } - - fn ctx_precompiles(&mut self) -> (&mut Self::Context, &mut Self::Precompiles) { - self.0.ctx_precompiles() + fn all( + &self, + ) -> ( + &Self::Context, + &Self::Instructions, + &Self::Precompiles, + &FrameStack, + ) { + self.0.all() } - fn frame_stack(&mut self) -> &mut FrameStack { - self.0.frame_stack() + fn all_mut( + &mut self, + ) -> ( + &mut Self::Context, + &mut Self::Instructions, + &mut Self::Precompiles, + &mut FrameStack, + ) { + self.0.all_mut() } fn frame_init( @@ -124,28 +126,27 @@ where { type Inspector = INSP; - fn inspector(&mut self) -> &mut Self::Inspector { - self.0.inspector() - } - - fn ctx_inspector(&mut self) -> (&mut Self::Context, &mut Self::Inspector) { - self.0.ctx_inspector() - } - - fn ctx_inspector_frame( - &mut self, - ) -> (&mut Self::Context, &mut Self::Inspector, &mut Self::Frame) { - self.0.ctx_inspector_frame() + fn all_inspector( + &self, + ) -> ( + &Self::Context, + &Self::Instructions, + &Self::Precompiles, + &FrameStack, + &Self::Inspector, + ) { + self.0.all_inspector() } - fn ctx_inspector_frame_instructions( + fn all_mut_inspector( &mut self, ) -> ( &mut Self::Context, - &mut Self::Inspector, - &mut Self::Frame, &mut Self::Instructions, + &mut Self::Precompiles, + &mut FrameStack, + &mut Self::Inspector, ) { - self.0.ctx_inspector_frame_instructions() + self.0.all_mut_inspector() } } diff --git a/examples/custom_precompile_journal/src/precompile_provider.rs b/examples/custom_precompile_journal/src/precompile_provider.rs index caab5682fd..7e4214ff66 100644 --- a/examples/custom_precompile_journal/src/precompile_provider.rs +++ b/examples/custom_precompile_journal/src/precompile_provider.rs @@ -4,7 +4,7 @@ use revm::{ context::Cfg, context_interface::{ContextTr, JournalTr, LocalContextTr, Transaction}, handler::{EthPrecompiles, PrecompileProvider}, - interpreter::{Gas, InputsImpl, InstructionResult, InterpreterResult}, + interpreter::{CallInputs, Gas, InstructionResult, InterpreterResult}, precompile::{PrecompileError, PrecompileOutput, PrecompileResult}, primitives::{address, hardfork::SpecId, Address, Bytes, U256}, }; @@ -52,21 +52,15 @@ where fn run( &mut self, context: &mut CTX, - address: &Address, - inputs: &InputsImpl, - is_static: bool, - gas_limit: u64, + inputs: &CallInputs, ) -> Result, String> { // Check if this is our custom precompile - if *address == CUSTOM_PRECOMPILE_ADDRESS { - return Ok(Some(run_custom_precompile( - context, inputs, is_static, gas_limit, - )?)); + if inputs.bytecode_address == CUSTOM_PRECOMPILE_ADDRESS { + return Ok(Some(run_custom_precompile(context, inputs)?)); } // Otherwise, delegate to standard Ethereum precompiles - self.inner - .run(context, address, inputs, is_static, gas_limit) + self.inner.run(context, inputs) } fn warm_addresses(&self) -> Box> { @@ -84,9 +78,7 @@ where /// Runs our custom precompile fn run_custom_precompile( context: &mut CTX, - inputs: &InputsImpl, - is_static: bool, - gas_limit: u64, + inputs: &CallInputs, ) -> Result { let input_bytes = match &inputs.input { revm::interpreter::CallInput::SharedBuffer(range) => { @@ -105,13 +97,13 @@ fn run_custom_precompile( let result = if input_bytes.is_empty() { // Read storage operation - handle_read_storage(context, gas_limit) + handle_read_storage(context, inputs.gas_limit) } else if input_bytes.len() == 32 { - if is_static { + if inputs.is_static { return Err("Cannot modify state in static context".to_string()); } // Write storage operation - handle_write_storage(context, &input_bytes, gas_limit) + handle_write_storage(context, &input_bytes, inputs.gas_limit) } else { Err(PrecompileError::Other("Invalid input length".to_string())) }; @@ -124,7 +116,7 @@ fn run_custom_precompile( } else { InstructionResult::Return }, - gas: Gas::new(gas_limit), + gas: Gas::new(inputs.gas_limit), output: output.bytes, }; let underflow = interpreter_result.gas.record_cost(output.gas_used); @@ -133,15 +125,23 @@ fn run_custom_precompile( } Ok(interpreter_result) } - Err(e) => Ok(InterpreterResult { - result: if e.is_oog() { - InstructionResult::PrecompileOOG - } else { - InstructionResult::PrecompileError - }, - gas: Gas::new(gas_limit), - output: Bytes::new(), - }), + Err(e) => { + // If this is a top-level precompile call and error is non-OOG, record the message + if !e.is_oog() && context.journal().depth() == 1 { + context + .local_mut() + .set_precompile_error_context(e.to_string()); + } + Ok(InterpreterResult { + result: if e.is_oog() { + InstructionResult::PrecompileOOG + } else { + InstructionResult::PrecompileError + }, + gas: Gas::new(inputs.gas_limit), + output: Bytes::new(), + }) + } } } diff --git a/examples/erc20_gas/src/handler.rs b/examples/erc20_gas/src/handler.rs index eeeed76fad..b1ff47d890 100644 --- a/examples/erc20_gas/src/handler.rs +++ b/examples/erc20_gas/src/handler.rs @@ -1,19 +1,16 @@ use revm::{ context::Cfg, - context_interface::{ - result::{HaltReason, InvalidTransaction}, - Block, ContextTr, JournalTr, Transaction, - }, + context_interface::{result::HaltReason, Block, ContextTr, JournalTr, Transaction}, handler::{ - pre_execution::validate_account_nonce_and_code, EvmTr, EvmTrError, FrameResult, FrameTr, - Handler, + pre_execution::{calculate_caller_fee, validate_account_nonce_and_code_with_components}, + EvmTr, EvmTrError, FrameResult, FrameTr, Handler, }, interpreter::interpreter_action::FrameInit, primitives::{hardfork::SpecId, U256}, state::EvmState, }; -use crate::{erc_address_storage, token_operation, TOKEN, TREASURY}; +use crate::{erc_address_storage, TOKEN}; /// Custom handler that implements ERC20 token gas payment. /// Instead of paying gas in ETH, transactions pay gas using ERC20 tokens. @@ -49,77 +46,31 @@ where type HaltReason = HaltReason; fn validate_against_state_and_deduct_caller(&self, evm: &mut Self::Evm) -> Result<(), ERROR> { - let context = evm.ctx(); - let basefee = context.block().basefee() as u128; - let blob_price = context.block().blob_gasprice().unwrap_or_default(); - let is_balance_check_disabled = context.cfg().is_balance_check_disabled(); - let is_eip3607_disabled = context.cfg().is_eip3607_disabled(); - let is_nonce_check_disabled = context.cfg().is_nonce_check_disabled(); - let caller = context.tx().caller(); - let value = context.tx().value(); + let (block, tx, cfg, journal, _, _) = evm.ctx_mut().all_mut(); - let (tx, journal) = context.tx_journal_mut(); + // load TOKEN contract + 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(), - is_eip3607_disabled, - is_nonce_check_disabled, - )?; + validate_account_nonce_and_code_with_components(&caller_account.info, tx, cfg)?; + // make changes to the account. Account balance stays the same + caller_account.touch(); if tx.kind().is_call() { - caller_account.info.nonce = caller_account.info.nonce.saturating_add(1); + caller_account.bump_nonce(); } - // Touch account so we know it is changed. - caller_account.mark_touch(); - - let max_balance_spending = tx.max_balance_spending()?; - let effective_balance_spending = tx - .effective_balance_spending(basefee, blob_price) - .expect("effective balance is always smaller than max balance so it can't overflow"); - let account_balance_slot = erc_address_storage(tx.caller()); - context.journal_mut().load_account(TOKEN)?.data.mark_touch(); - let account_balance = context - .journal_mut() - .sload(TOKEN, account_balance_slot) - .map(|v| v.data) - .unwrap_or_default(); - - if account_balance < max_balance_spending && !is_balance_check_disabled { - return Err(InvalidTransaction::LackOfFundForMaxFee { - fee: Box::new(max_balance_spending), - balance: Box::new(account_balance), - } - .into()); - }; + // load account balance + let account_balance = journal.sload(TOKEN, account_balance_slot)?.data; - // 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 { - // ignore balance check. - } else if max_balance_spending > account_balance { - return Err(InvalidTransaction::LackOfFundForMaxFee { - fee: Box::new(max_balance_spending), - balance: Box::new(account_balance), - } - .into()); - } else { - // subtracting max balance spending with value that is going to be deducted later in the call. - let gas_balance_spending = effective_balance_spending - value; - - token_operation::( - context, - caller, - TREASURY, - gas_balance_spending, - )?; - } + let new_balance = calculate_caller_fee(account_balance, tx, block, cfg)?; + + // store deducted balance. + journal.sstore(TOKEN, account_balance_slot, new_balance)?; Ok(()) } @@ -138,11 +89,19 @@ where let reimbursement = effective_gas_price.saturating_mul((gas.remaining() + gas.refunded() as u64) as u128); - token_operation::( - context, - TREASURY, - caller, - U256::from(reimbursement), + let account_balance_slot = erc_address_storage(caller); + + // load account balance + let account_balance = context + .journal_mut() + .sload(TOKEN, account_balance_slot)? + .data; + + // reimburse caller + context.journal_mut().sstore( + TOKEN, + account_balance_slot, + account_balance + U256::from(reimbursement), )?; Ok(()) @@ -167,7 +126,17 @@ where }; let reward = coinbase_gas_price.saturating_mul(gas.used() as u128); - token_operation::(context, TREASURY, beneficiary, U256::from(reward))?; + + let beneficiary_slot = erc_address_storage(beneficiary); + // load account balance + let journal = context.journal_mut(); + let beneficiary_balance = journal.sload(TOKEN, beneficiary_slot)?.data; + // reimburse caller + journal.sstore( + TOKEN, + beneficiary_slot, + beneficiary_balance + U256::from(reward), + )?; Ok(()) } diff --git a/examples/erc20_gas/src/main.rs b/examples/erc20_gas/src/main.rs index 58e2187e7c..1f16c47f9a 100644 --- a/examples/erc20_gas/src/main.rs +++ b/examples/erc20_gas/src/main.rs @@ -10,10 +10,6 @@ use anyhow::Result; use exec::transact_erc20evm_commit; use revm::{ context::TxEnv, - context_interface::{ - result::{InvalidHeader, InvalidTransaction}, - ContextTr, JournalTr, - }, database::{AlloyDB, BlockId, CacheDB}, database_interface::WrapDatabaseAsync, primitives::{ @@ -81,49 +77,6 @@ async fn main() -> Result<()> { Ok(()) } -/// Helpers -pub fn token_operation( - context: &mut CTX, - sender: Address, - recipient: Address, - amount: U256, -) -> Result<(), ERROR> -where - CTX: ContextTr, - ERROR: From + From + From<::Error>, -{ - let sender_balance_slot = erc_address_storage(sender); - let sender_balance = context - .journal_mut() - .sload(TOKEN, sender_balance_slot)? - .data; - - if sender_balance < amount { - return Err(ERROR::from( - InvalidTransaction::MaxFeePerBlobGasNotSupported, - )); - } - // Subtract the amount from the sender's balance - let sender_new_balance = sender_balance.saturating_sub(amount); - context - .journal_mut() - .sstore(TOKEN, sender_balance_slot, sender_new_balance)?; - - // Add the amount to the recipient's balance - let recipient_balance_slot = erc_address_storage(recipient); - let recipient_balance = context - .journal_mut() - .sload(TOKEN, recipient_balance_slot)? - .data; - - let recipient_new_balance = recipient_balance.saturating_add(amount); - context - .journal_mut() - .sstore(TOKEN, recipient_balance_slot, recipient_new_balance)?; - - Ok(()) -} - fn balance_of(address: Address, alloy_db: &mut AlloyCacheDB) -> Result { let slot = erc_address_storage(address); alloy_db.storage(TOKEN, slot).map_err(From::from) diff --git a/examples/my_evm/src/evm.rs b/examples/my_evm/src/evm.rs index a5a11b34c1..133afd497d 100644 --- a/examples/my_evm/src/evm.rs +++ b/examples/my_evm/src/evm.rs @@ -62,26 +62,32 @@ where type Instructions = EthInstructions; type Precompiles = EthPrecompiles; type Frame = EthFrame; - fn ctx(&mut self) -> &mut Self::Context { - &mut self.0.ctx - } - - fn ctx_ref(&self) -> &Self::Context { - self.0.ctx_ref() - } - fn ctx_instructions(&mut self) -> (&mut Self::Context, &mut Self::Instructions) { - self.0.ctx_instructions() - } - - fn ctx_precompiles(&mut self) -> (&mut Self::Context, &mut Self::Precompiles) { - self.0.ctx_precompiles() + #[inline] + fn all( + &self, + ) -> ( + &Self::Context, + &Self::Instructions, + &Self::Precompiles, + &FrameStack, + ) { + self.0.all() } - fn frame_stack(&mut self) -> &mut FrameStack { - self.0.frame_stack() + #[inline] + fn all_mut( + &mut self, + ) -> ( + &mut Self::Context, + &mut Self::Instructions, + &mut Self::Precompiles, + &mut FrameStack, + ) { + self.0.all_mut() } + #[inline] fn frame_init( &mut self, frame_input: ::FrameInit, @@ -92,6 +98,7 @@ where self.0.frame_init(frame_input) } + #[inline] fn frame_run( &mut self, ) -> Result< @@ -101,6 +108,7 @@ where self.0.frame_run() } + #[inline] fn frame_return_result( &mut self, frame_result: ::FrameResult, @@ -119,28 +127,27 @@ where { type Inspector = INSP; - fn inspector(&mut self) -> &mut Self::Inspector { - self.0.inspector() - } - - fn ctx_inspector(&mut self) -> (&mut Self::Context, &mut Self::Inspector) { - self.0.ctx_inspector() - } - - fn ctx_inspector_frame( - &mut self, - ) -> (&mut Self::Context, &mut Self::Inspector, &mut Self::Frame) { - self.0.ctx_inspector_frame() + fn all_inspector( + &self, + ) -> ( + &Self::Context, + &Self::Instructions, + &Self::Precompiles, + &FrameStack, + &Self::Inspector, + ) { + self.0.all_inspector() } - fn ctx_inspector_frame_instructions( + fn all_mut_inspector( &mut self, ) -> ( &mut Self::Context, - &mut Self::Inspector, - &mut Self::Frame, &mut Self::Instructions, + &mut Self::Precompiles, + &mut FrameStack, + &mut Self::Inspector, ) { - self.0.ctx_inspector_frame_instructions() + self.0.all_mut_inspector() } } diff --git a/scripts/run-tests.sh b/scripts/run-tests.sh index 0f813f9b8b..4cc64460e5 100755 --- a/scripts/run-tests.sh +++ b/scripts/run-tests.sh @@ -4,19 +4,29 @@ set -eo pipefail # Usage: ./scripts/run-tests.sh --help # Version for the execution spec tests -VERSION="v4.4.0" -DEVELOP_VERSION="fusaka-devnet-3@v1.0.0" +MAIN_VERSION="v5.3.0" +DEVNET_VERSION="fusaka-devnet-5@v2.1.0" -# Directories +### Directories ### FIXTURES_DIR="test-fixtures" -STABLE_DIR="$FIXTURES_DIR/stable" -DEVELOP_DIR="$FIXTURES_DIR/develop" + +MAIN_DIR="$FIXTURES_DIR/main" +MAIN_STABLE_DIR="$MAIN_DIR/stable" +MAIN_DEVELOP_DIR="$MAIN_DIR/develop" + +DEVNET_DIR="$FIXTURES_DIR/devnet" +DEVNET_DEVELOP_DIR="$DEVNET_DIR/develop" + LEGACY_DIR="$FIXTURES_DIR/legacytests" -# URL and filenames +### URL and filenames ### FIXTURES_URL="https://github.com/ethereum/execution-spec-tests/releases/download" -STABLE_TAR="fixtures_stable.tar.gz" -DEVELOP_TAR="fixtures_fusaka-devnet-3.tar.gz" + +MAIN_STABLE_TAR="fixtures_stable.tar.gz" +MAIN_DEVELOP_TAR="fixtures_develop.tar.gz" + +DEVNET_TAR="fixtures_fusaka-devnet-5.tar.gz" + LEGACY_REPO_URL="https://github.com/ethereum/legacytests.git" # Print usage information and exit @@ -62,7 +72,7 @@ clean() { # Check if all required fixture directories exist check_fixtures() { - if [ -d "$STABLE_DIR" ] && [ -d "$DEVELOP_DIR" ] && [ -d "$LEGACY_DIR" ]; then + if [ -d "$MAIN_STABLE_DIR" ] && [ -d "$MAIN_DEVELOP_DIR" ] && [ -d "$DEVNET_DIR" ] && [ -d "$LEGACY_DIR" ]; then return 0 else return 1 @@ -78,7 +88,8 @@ download_and_extract() { local version="$4" echo "Downloading ${label} fixtures..." - curl -L "${FIXTURES_URL}/${version}/${tar_file}" -o "${FIXTURES_DIR}/${tar_file}" + # Use -fsSL to fail on HTTP errors; add small retry for transient network issues + curl -fsSL --retry 3 --retry-delay 2 "${FIXTURES_URL}/${version}/${tar_file}" -o "${FIXTURES_DIR}/${tar_file}" echo "Extracting ${label} fixtures..." # strip-components=1 removes the first top level directory from the flepath # This is needed because when we extract the tar, it is placed under an @@ -89,13 +100,14 @@ download_and_extract() { # Download all fixtures download_fixtures() { echo "Creating fixtures directory structure..." - mkdir -p "$STABLE_DIR" "$DEVELOP_DIR" "$LEGACY_DIR" + mkdir -p "$MAIN_STABLE_DIR" "$MAIN_DEVELOP_DIR" "$DEVNET_DIR" "$LEGACY_DIR" - download_and_extract "$STABLE_DIR" "$STABLE_TAR" "stable" "$VERSION" - download_and_extract "$DEVELOP_DIR" "$DEVELOP_TAR" "develop" "$DEVELOP_VERSION" + 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}/${STABLE_TAR}" "${FIXTURES_DIR}/${DEVELOP_TAR}" + rm "${FIXTURES_DIR}/${MAIN_STABLE_TAR}" "${FIXTURES_DIR}/${MAIN_DEVELOP_TAR}" "${FIXTURES_DIR}/${DEVNET_TAR}" # Clone legacytests repository echo "Cloning legacytests repository..." @@ -127,14 +139,23 @@ build_cargo_options() { # Run tests for each set of fixtures using the chosen runner. run_tests() { - echo "Running stable statetests..." - $RUST_RUNNER run $CARGO_OPTS -p revme -- statetest "$STABLE_DIR/state_tests" + echo "Running main stable statetests..." + $RUST_RUNNER run $CARGO_OPTS -p revme -- statetest "$MAIN_STABLE_DIR/state_tests" + + echo "Running main develop statetests..." + $RUST_RUNNER run $CARGO_OPTS -p revme -- statetest "$MAIN_DEVELOP_DIR/state_tests" + + echo "Running devnet statetests..." + $RUST_RUNNER run $CARGO_OPTS -p revme -- statetest "$DEVNET_DIR/state_tests" - echo "Running develop statetests..." - $RUST_RUNNER run $CARGO_OPTS -p revme -- statetest "$DEVELOP_DIR/state_tests" - echo "Running legacy tests..." $RUST_RUNNER run $CARGO_OPTS -p revme -- statetest "$LEGACY_DIR/Cancun/GeneralStateTests" + + echo "Running main develop blockchain tests..." + $RUST_RUNNER run $CARGO_OPTS -p revme -- btest "$MAIN_DEVELOP_DIR/blockchain_tests" + + echo "Running main stable blockchain tests..." + $RUST_RUNNER run $CARGO_OPTS -p revme -- btest "$MAIN_STABLE_DIR/blockchain_tests" } ##############################