From e51d128cbea4e99091b9fd23aee2a38b7a13b840 Mon Sep 17 00:00:00 2001 From: rakita Date: Thu, 5 Dec 2024 20:02:41 +0100 Subject: [PATCH] feat: Restucturing Part7 Handler and Context rework (#1865) * feat: Restructure Part7 Handler Wire * wip * wip of frames * work on Frame calls * Wip Frame and Exec wire * compile handler wires * fmt * Hand from Handler replacement * Interpreter traits * interpreter popn calls * wip Interpreter refactor, all instructions compile * compiles without precompiles * precompile and instruction table wip * precompile run wip * simple revm statetest run * Example of inspector in revme * small cleanup for presentation * context interface * handler and context interface crates * small cleanup * fmt * Inspector wip * rename and wip Inspector * fixes * fix bugs, mv spec to cfg * fix for tests * fix eof bugs * no_std compile fix * few fixes * clippy * cleanup * remvme bench, builder for context * inline somethings * clippy * example of block traces * enable examples * op handlers * Add op precompiles * some ci fixes * fix tests * fix docs * simplifications, cleanup * enable evm run cmd * must use on chained fn * cleanup --- .github/workflows/ethereum-tests.yml | 12 +- CHANGELOG.md | 2 +- Cargo.lock | 325 ++++--- Cargo.toml | 33 +- bins/revme/src/cmd/bench/analysis.rs | 25 +- bins/revme/src/cmd/bench/burntpix.rs | 25 +- bins/revme/src/cmd/bench/snailtracer.rs | 17 +- bins/revme/src/cmd/bench/transfer.rs | 15 +- bins/revme/src/cmd/evmrunner.rs | 26 +- bins/revme/src/cmd/statetest/runner.rs | 252 +++--- crates/bytecode/src/bytecode.rs | 43 +- crates/bytecode/src/eof.rs | 3 +- crates/bytecode/src/eof/body.rs | 52 +- crates/bytecode/src/eof/verification.rs | 4 +- crates/bytecode/src/legacy/jump_map.rs | 2 +- crates/bytecode/src/opcode.rs | 6 + crates/context/Cargo.toml | 23 +- .../{gas => context/interface}/CHANGELOG.md | 0 .../{wiring => context/interface}/Cargo.toml | 42 +- .../interface}/src/block.rs | 21 +- .../interface}/src/block/blob.rs | 0 crates/context/interface/src/cfg.rs | 210 +++++ crates/context/interface/src/errors.rs | 6 + crates/context/interface/src/host.rs | 127 +++ .../context/interface/src/journaled_state.rs | 248 +++++ crates/context/interface/src/lib.rs | 20 + .../interface}/src/result.rs | 60 +- .../interface}/src/transaction.rs | 35 +- .../interface/src/transaction}/access_list.rs | 4 +- .../interface/src/transaction}/common.rs | 2 + .../interface/src/transaction}/eip1559.rs | 5 +- .../interface/src/transaction}/eip2930.rs | 4 +- .../interface/src/transaction}/eip4844.rs | 4 +- .../interface/src/transaction}/eip7702.rs | 14 +- .../interface/src/transaction}/legacy.rs | 4 +- .../src/transaction}/transaction_type.rs | 0 .../src/default => context/src}/block.rs | 16 +- crates/context/src/cfg.rs | 1 + crates/context/src/context.rs | 513 +++++++++++ crates/context/src/default.rs | 3 + .../{revm => context}/src/journaled_state.rs | 375 +++++--- crates/context/src/lib.rs | 10 + .../{wiring/src/default => context/src}/tx.rs | 13 +- crates/database/Cargo.toml | 2 +- crates/database/interface/Cargo.toml | 8 +- crates/database/interface/src/async_db.rs | 6 +- crates/database/interface/src/empty_db.rs | 6 +- crates/database/interface/src/lib.rs | 24 +- crates/database/src/alloydb.rs | 24 +- crates/database/src/in_memory_db.rs | 4 - crates/database/src/states/state_builder.rs | 4 +- crates/gas/Cargo.toml | 41 - crates/gas/LICENSE | 21 - crates/gas/src/lib.rs | 8 - crates/{interface => handler}/CHANGELOG.md | 0 .../transaction => handler}/Cargo.toml | 24 +- .../interface}/CHANGELOG.md | 0 crates/{ => handler}/interface/Cargo.toml | 20 +- crates/handler/interface/src/execution.rs | 57 ++ crates/handler/interface/src/frame.rs | 31 + crates/handler/interface/src/handler.rs | 13 + crates/handler/interface/src/lib.rs | 24 + .../handler/interface/src/post_execution.rs | 51 ++ crates/handler/interface/src/pre_execution.rs | 10 + .../interface/src/precompile_provider.rs | 25 + crates/handler/interface/src/util.rs | 24 + crates/handler/interface/src/validation.rs | 13 + crates/handler/src/execution.rs | 215 +++++ crates/handler/src/frame.rs | 830 +++++++++++++++++ crates/handler/src/frame_data.rs | 145 +++ crates/handler/src/lib.rs | 139 +++ crates/handler/src/post_execution.rs | 197 ++++ crates/handler/src/pre_execution.rs | 225 +++++ crates/handler/src/precompile_provider.rs | 81 ++ .../mainnet => handler/src}/validation.rs | 328 +++---- crates/inspector/Cargo.toml | 4 +- crates/inspector/src/customprinter.rs | 162 ---- crates/inspector/src/eip3155.rs | 143 +-- crates/inspector/src/gas.rs | 356 ++++---- crates/inspector/src/handler_register.rs | 408 --------- crates/inspector/src/inspector.rs | 708 ++++++++++++++- crates/inspector/src/lib.rs | 8 +- crates/inspector/src/noop.rs | 12 +- crates/interface/LICENSE | 21 - crates/interface/src/lib.rs | 8 - crates/interpreter/Cargo.toml | 18 +- crates/interpreter/src/gas.rs | 63 ++ crates/interpreter/src/gas/calc.rs | 48 +- crates/interpreter/src/host.rs | 184 +--- crates/interpreter/src/host/dummy.rs | 53 +- crates/interpreter/src/instruction_result.rs | 46 +- crates/interpreter/src/instructions.rs | 272 +++--- .../src/instructions/arithmetic.rs | 88 +- .../interpreter/src/instructions/bitwise.rs | 136 ++- .../src/instructions/block_info.rs | 76 +- .../interpreter/src/instructions/contract.rs | 443 +++++---- .../src/instructions/contract/call_helpers.rs | 31 +- .../interpreter/src/instructions/control.rs | 269 ++++-- crates/interpreter/src/instructions/data.rs | 81 +- crates/interpreter/src/instructions/host.rs | 237 +++-- crates/interpreter/src/instructions/macros.rs | 316 ++----- crates/interpreter/src/instructions/memory.rs | 53 +- crates/interpreter/src/instructions/stack.rs | 140 ++- crates/interpreter/src/instructions/system.rs | 186 ++-- .../interpreter/src/instructions/tx_info.rs | 39 +- .../interpreter/src/instructions/utility.rs | 110 ++- crates/interpreter/src/interpreter.rs | 542 ++++------- .../interpreter/src/interpreter/contract.rs | 104 --- .../src/interpreter/ext_bytecode.rs | 157 ++++ crates/interpreter/src/interpreter/input.rs | 27 + .../src/interpreter/loop_control.rs | 46 + .../src/interpreter/return_data.rs | 15 + .../src/interpreter/runtime_flags.rs | 28 + crates/interpreter/src/interpreter/serde.rs | 234 ++--- .../src/interpreter/shared_memory.rs | 77 +- crates/interpreter/src/interpreter/stack.rs | 179 ++-- .../subroutine_stack.rs} | 65 +- crates/interpreter/src/interpreter_action.rs | 17 +- .../src/interpreter_action/create_inputs.rs | 2 +- crates/interpreter/src/interpreter_types.rs | 220 +++++ crates/interpreter/src/lib.rs | 16 +- crates/interpreter/src/table.rs | 161 ++-- crates/optimism/Cargo.toml | 7 +- crates/optimism/src/bn128.rs | 16 +- crates/optimism/src/evm.rs | 45 + crates/optimism/src/fast_lz.rs | 174 ++-- crates/optimism/src/handler.rs | 811 +++++++++++++++++ crates/optimism/src/handler/precompiles.rs | 121 +++ crates/optimism/src/handler_register.rs | 785 ---------------- crates/optimism/src/l1block.rs | 112 ++- crates/optimism/src/lib.rs | 11 +- crates/optimism/src/result.rs | 2 +- crates/optimism/src/spec.rs | 854 +++--------------- .../optimism/src/transaction/abstraction.rs | 24 +- crates/optimism/src/transaction/deposit.rs | 2 +- crates/optimism/src/transaction/error.rs | 4 +- crates/optimism/src/wiring.rs | 92 -- crates/precompile/Cargo.toml | 9 +- crates/precompile/benches/bench.rs | 6 +- crates/precompile/src/blake2.rs | 7 +- crates/precompile/src/bls12_381/g1_add.rs | 4 +- crates/precompile/src/bls12_381/g1_msm.rs | 4 +- crates/precompile/src/bls12_381/g1_mul.rs | 4 +- crates/precompile/src/bls12_381/g2_add.rs | 4 +- crates/precompile/src/bls12_381/g2_msm.rs | 4 +- crates/precompile/src/bls12_381/g2_mul.rs | 4 +- .../precompile/src/bls12_381/map_fp2_to_g2.rs | 4 +- .../precompile/src/bls12_381/map_fp_to_g1.rs | 4 +- crates/precompile/src/bls12_381/pairing.rs | 5 +- crates/precompile/src/bn128.rs | 51 +- crates/precompile/src/fatal_precompile.rs | 36 - crates/precompile/src/hash.rs | 12 +- crates/precompile/src/identity.rs | 6 +- crates/precompile/src/interface.rs | 150 +-- crates/precompile/src/kzg_point_evaluation.rs | 35 +- crates/precompile/src/lib.rs | 40 +- crates/precompile/src/modexp.rs | 10 +- crates/precompile/src/secp256k1.rs | 8 +- crates/precompile/src/secp256r1.rs | 5 +- crates/revm/Cargo.toml | 52 +- crates/revm/benches/bench.rs | 140 --- crates/revm/src/builder.rs | 694 -------------- crates/revm/src/context.rs | 188 ---- .../revm/src/context/context_precompiles.rs | 242 ----- crates/revm/src/context/evm_context.rs | 653 ------------- crates/revm/src/context/inner_evm_context.rs | 433 --------- crates/revm/src/evm.rs | 616 +++++++------ crates/revm/src/evm_wiring.rs | 35 - crates/revm/src/exec.rs | 19 + crates/revm/src/frame.rs | 297 ------ crates/revm/src/handler.rs | 209 ----- crates/revm/src/handler/handle_types.rs | 24 - .../src/handler/handle_types/execution.rs | 289 ------ .../revm/src/handler/handle_types/generic.rs | 10 - .../handler/handle_types/post_execution.rs | 110 --- .../src/handler/handle_types/pre_execution.rs | 76 -- .../src/handler/handle_types/validation.rs | 65 -- crates/revm/src/handler/mainnet.rs | 23 - crates/revm/src/handler/mainnet/execution.rs | 302 ------- .../src/handler/mainnet/post_execution.rs | 141 --- .../revm/src/handler/mainnet/pre_execution.rs | 196 ---- crates/revm/src/handler/register.rs | 32 - crates/revm/src/lib.rs | 33 +- crates/revm/src/test_utils.rs | 2 - crates/specification/src/constants.rs | 3 + crates/specification/src/eip4844.rs | 2 +- crates/specification/src/hardfork.rs | 281 ++---- crates/state/src/lib.rs | 2 +- crates/statetest-types/src/transaction.rs | 2 +- crates/wiring/LICENSE | 21 - crates/wiring/src/default.rs | 311 ------- crates/wiring/src/evm_wiring.rs | 60 -- crates/wiring/src/kzg.rs | 47 - crates/wiring/src/lib.rs | 31 - crates/wiring/src/precompile.rs | 250 ----- crates/wiring/transaction/CHANGELOG.md | 0 crates/wiring/transaction/LICENSE | 21 - crates/wiring/transaction/src/lib.rs | 23 - documentation/src/crates/primitives.md | 2 +- documentation/src/crates/revm/builder.md | 2 +- examples/block_traces/src/main.rs | 117 +-- examples/contract_deployment/src/main.rs | 39 +- examples/custom_opcodes/src/lib.rs | 6 +- examples/database_components/Cargo.toml | 2 +- examples/database_components/src/lib.rs | 6 +- examples/database_ref/Cargo.toml | 30 - examples/database_ref/src/main.rs | 91 -- examples/uniswap_get_reserves/src/main.rs | 43 +- examples/uniswap_v2_usdc_swap/src/main.rs | 129 +-- graph.png | Bin 0 -> 334539 bytes 210 files changed, 10026 insertions(+), 11659 deletions(-) rename crates/{gas => context/interface}/CHANGELOG.md (100%) rename crates/{wiring => context/interface}/Cargo.toml (51%) rename crates/{wiring => context/interface}/src/block.rs (82%) rename crates/{wiring => context/interface}/src/block/blob.rs (100%) create mode 100644 crates/context/interface/src/cfg.rs create mode 100644 crates/context/interface/src/errors.rs create mode 100644 crates/context/interface/src/host.rs create mode 100644 crates/context/interface/src/journaled_state.rs create mode 100644 crates/context/interface/src/lib.rs rename crates/{wiring => context/interface}/src/result.rs (93%) rename crates/{wiring/transaction => context/interface}/src/transaction.rs (87%) rename crates/{wiring/transaction/src => context/interface/src/transaction}/access_list.rs (92%) rename crates/{wiring/transaction/src => context/interface/src/transaction}/common.rs (91%) rename crates/{wiring/transaction/src => context/interface/src/transaction}/eip1559.rs (82%) rename crates/{wiring/transaction/src => context/interface/src/transaction}/eip2930.rs (82%) rename crates/{wiring/transaction/src => context/interface/src/transaction}/eip4844.rs (92%) rename crates/{wiring/transaction/src => context/interface/src/transaction}/eip7702.rs (91%) rename crates/{wiring/transaction/src => context/interface/src/transaction}/legacy.rs (82%) rename crates/{wiring/transaction/src => context/interface/src/transaction}/transaction_type.rs (100%) rename crates/{wiring/src/default => context/src}/block.rs (87%) create mode 100644 crates/context/src/cfg.rs create mode 100644 crates/context/src/context.rs create mode 100644 crates/context/src/default.rs rename crates/{revm => context}/src/journaled_state.rs (81%) rename crates/{wiring/src/default => context/src}/tx.rs (96%) delete mode 100644 crates/gas/Cargo.toml delete mode 100644 crates/gas/LICENSE delete mode 100644 crates/gas/src/lib.rs rename crates/{interface => handler}/CHANGELOG.md (100%) rename crates/{wiring/transaction => handler}/Cargo.toml (61%) rename crates/{wiring => handler/interface}/CHANGELOG.md (100%) rename crates/{ => handler}/interface/Cargo.toml (62%) create mode 100644 crates/handler/interface/src/execution.rs create mode 100644 crates/handler/interface/src/frame.rs create mode 100644 crates/handler/interface/src/handler.rs create mode 100644 crates/handler/interface/src/lib.rs create mode 100644 crates/handler/interface/src/post_execution.rs create mode 100644 crates/handler/interface/src/pre_execution.rs create mode 100644 crates/handler/interface/src/precompile_provider.rs create mode 100644 crates/handler/interface/src/util.rs create mode 100644 crates/handler/interface/src/validation.rs create mode 100644 crates/handler/src/execution.rs create mode 100644 crates/handler/src/frame.rs create mode 100644 crates/handler/src/frame_data.rs create mode 100644 crates/handler/src/lib.rs create mode 100644 crates/handler/src/post_execution.rs create mode 100644 crates/handler/src/pre_execution.rs create mode 100644 crates/handler/src/precompile_provider.rs rename crates/{revm/src/handler/mainnet => handler/src}/validation.rs (51%) delete mode 100644 crates/inspector/src/customprinter.rs delete mode 100644 crates/inspector/src/handler_register.rs delete mode 100644 crates/interface/LICENSE delete mode 100644 crates/interface/src/lib.rs delete mode 100644 crates/interpreter/src/interpreter/contract.rs create mode 100644 crates/interpreter/src/interpreter/ext_bytecode.rs create mode 100644 crates/interpreter/src/interpreter/input.rs create mode 100644 crates/interpreter/src/interpreter/loop_control.rs create mode 100644 crates/interpreter/src/interpreter/return_data.rs create mode 100644 crates/interpreter/src/interpreter/runtime_flags.rs rename crates/interpreter/src/{function_stack.rs => interpreter/subroutine_stack.rs} (56%) create mode 100644 crates/interpreter/src/interpreter_types.rs create mode 100644 crates/optimism/src/evm.rs create mode 100644 crates/optimism/src/handler.rs create mode 100644 crates/optimism/src/handler/precompiles.rs delete mode 100644 crates/optimism/src/handler_register.rs delete mode 100644 crates/optimism/src/wiring.rs delete mode 100644 crates/precompile/src/fatal_precompile.rs delete mode 100644 crates/revm/benches/bench.rs delete mode 100644 crates/revm/src/builder.rs delete mode 100644 crates/revm/src/context.rs delete mode 100644 crates/revm/src/context/context_precompiles.rs delete mode 100644 crates/revm/src/context/evm_context.rs delete mode 100644 crates/revm/src/context/inner_evm_context.rs delete mode 100644 crates/revm/src/evm_wiring.rs create mode 100644 crates/revm/src/exec.rs delete mode 100644 crates/revm/src/frame.rs delete mode 100644 crates/revm/src/handler.rs delete mode 100644 crates/revm/src/handler/handle_types.rs delete mode 100644 crates/revm/src/handler/handle_types/execution.rs delete mode 100644 crates/revm/src/handler/handle_types/generic.rs delete mode 100644 crates/revm/src/handler/handle_types/post_execution.rs delete mode 100644 crates/revm/src/handler/handle_types/pre_execution.rs delete mode 100644 crates/revm/src/handler/handle_types/validation.rs delete mode 100644 crates/revm/src/handler/mainnet.rs delete mode 100644 crates/revm/src/handler/mainnet/execution.rs delete mode 100644 crates/revm/src/handler/mainnet/post_execution.rs delete mode 100644 crates/revm/src/handler/mainnet/pre_execution.rs delete mode 100644 crates/revm/src/handler/register.rs delete mode 100644 crates/revm/src/test_utils.rs delete mode 100644 crates/wiring/LICENSE delete mode 100644 crates/wiring/src/default.rs delete mode 100644 crates/wiring/src/evm_wiring.rs delete mode 100644 crates/wiring/src/kzg.rs delete mode 100644 crates/wiring/src/lib.rs delete mode 100644 crates/wiring/src/precompile.rs delete mode 100644 crates/wiring/transaction/CHANGELOG.md delete mode 100644 crates/wiring/transaction/LICENSE delete mode 100644 crates/wiring/transaction/src/lib.rs delete mode 100644 examples/database_ref/Cargo.toml delete mode 100644 examples/database_ref/src/main.rs create mode 100644 graph.png diff --git a/.github/workflows/ethereum-tests.yml b/.github/workflows/ethereum-tests.yml index d038e1f549..fb530c8a13 100644 --- a/.github/workflows/ethereum-tests.yml +++ b/.github/workflows/ethereum-tests.yml @@ -52,10 +52,10 @@ jobs: tests/eof_suite/eest/state_tests \ tests/eof_suite/evmone/state_tests \ tests/prague_suite/state_tests - - name: Run EOF validation tests - run: | - cross run --target ${{matrix.target}} --profile ${{ matrix.profile }} -p revme -- eof-validation \ - ethtests/EOFTests \ - tests/eof_suite/eest/eof_tests/prague \ - tests/eof_suite/evmone/eof_tests + # - name: Run EOF validation tests + # run: | + # cross run --target ${{matrix.target}} --profile ${{ matrix.profile }} -p revme -- eof-validation \ + # ethtests/EOFTests \ + # tests/eof_suite/eest/eof_tests/prague \ + # tests/eof_suite/evmone/eof_tests diff --git a/CHANGELOG.md b/CHANGELOG.md index 611feda0f1..7c8c1d42e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -235,7 +235,7 @@ Bigger release. Cancun support, revm State added and some cleanup refactoring. # v24 tag date: 03.05.2023 -Cosnensus bug inside journal and some small changes. +Consensus bug inside journal and some small changes. * revm: v3.3.0 * revm-precompile: v2.0.3 diff --git a/Cargo.lock b/Cargo.lock index 5a98d1e54f..a20a7947de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -266,7 +266,7 @@ checksum = "4d0f2d905ebd295e7effec65e5f6868d153936130ae718352771de3e7d03c75c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] @@ -346,7 +346,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] @@ -362,7 +362,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", "syn-solidity", "tiny-keccak", ] @@ -378,7 +378,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", "syn-solidity", ] @@ -389,7 +389,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc85178909a49c8827ffccfc9103a7ce1767ae66a801b69bdc326913870bf8e6" dependencies = [ "serde", - "winnow 0.6.18", + "winnow", ] [[package]] @@ -447,9 +447,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -468,30 +468,30 @@ checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -540,7 +540,7 @@ dependencies = [ "num-bigint 0.4.6", "num-traits", "paste", - "rustc_version 0.4.0", + "rustc_version 0.4.1", "zeroize", ] @@ -655,7 +655,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] @@ -666,7 +666,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] @@ -693,7 +693,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] @@ -929,7 +929,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] @@ -940,9 +940,9 @@ checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "console" @@ -954,7 +954,7 @@ dependencies = [ "lazy_static", "libc", "unicode-width", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1133,7 +1133,7 @@ checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] @@ -1144,7 +1144,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] @@ -1155,7 +1155,7 @@ checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] @@ -1175,7 +1175,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", "unicode-xid", ] @@ -1206,12 +1206,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" -[[package]] -name = "dyn-clone" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" - [[package]] name = "ecdsa" version = "0.16.9" @@ -1274,7 +1268,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] @@ -1290,7 +1284,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1415,16 +1409,6 @@ dependencies = [ "revm", ] -[[package]] -name = "example-database-ref" -version = "0.0.0" -dependencies = [ - "anyhow", - "revm", - "revm-database", - "revm-inspector", -] - [[package]] name = "example-uniswap-get-reserves" version = "0.0.0" @@ -1612,7 +1596,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] @@ -2005,7 +1989,7 @@ checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ "hermit-abi", "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -2181,7 +2165,7 @@ dependencies = [ "hermit-abi", "libc", "wasi", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -2314,7 +2298,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] @@ -2392,7 +2376,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] @@ -2526,7 +2510,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] @@ -2555,7 +2539,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] @@ -2660,9 +2644,9 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ "toml_edit", ] @@ -2686,7 +2670,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] @@ -2726,7 +2710,7 @@ checksum = "6ff7ff745a347b87471d859a377a9a404361e7efc2a971d73424a6d183c0fc77" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] @@ -2737,9 +2721,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -2905,23 +2889,22 @@ dependencies = [ "alloy-sol-types", "anyhow", "criterion", - "derive-where", - "dyn-clone", "ethers-contract", "indicatif", "reqwest", "revm-bytecode", + "revm-context", + "revm-context-interface", "revm-database", "revm-database-interface", + "revm-handler", + "revm-handler-interface", "revm-interpreter", "revm-precompile", "revm-primitives", "revm-specification", "revm-state", - "revm-transaction", - "revm-wiring", - "rstest", - "serde", + "rstest 0.22.0", ] [[package]] @@ -2936,6 +2919,36 @@ dependencies = [ "serde", ] +[[package]] +name = "revm-context" +version = "1.0.0" +dependencies = [ + "derive-where", + "revm-bytecode", + "revm-context-interface", + "revm-database", + "revm-database-interface", + "revm-interpreter", + "revm-primitives", + "revm-specification", + "revm-state", + "serde", +] + +[[package]] +name = "revm-context-interface" +version = "1.0.0" +dependencies = [ + "auto_impl", + "cfg-if", + "revm-database", + "revm-database-interface", + "revm-primitives", + "revm-specification", + "revm-state", + "serde", +] + [[package]] name = "revm-database" version = "1.0.0" @@ -2949,11 +2962,11 @@ dependencies = [ "criterion", "indicatif", "revm-bytecode", + "revm-context-interface", "revm-database-interface", "revm-primitives", "revm-state", - "revm-wiring", - "rstest", + "rstest 0.22.0", "serde", "serde_json", "tokio", @@ -2970,11 +2983,36 @@ dependencies = [ "indicatif", "revm-primitives", "revm-state", - "rstest", + "rstest 0.22.0", "serde", "tokio", ] +[[package]] +name = "revm-handler" +version = "1.0.0" +dependencies = [ + "revm-bytecode", + "revm-context-interface", + "revm-database", + "revm-handler-interface", + "revm-interpreter", + "revm-precompile", + "revm-primitives", + "revm-specification", + "revm-state", + "serde", +] + +[[package]] +name = "revm-handler-interface" +version = "1.0.0" +dependencies = [ + "revm-database", + "revm-interpreter", + "revm-primitives", +] + [[package]] name = "revm-inspector" version = "1.0.0" @@ -2992,13 +3030,11 @@ name = "revm-interpreter" version = "10.0.1" dependencies = [ "bincode", - "derive-where", "revm-bytecode", + "revm-context-interface", "revm-database-interface", "revm-primitives", "revm-specification", - "revm-transaction", - "revm-wiring", "serde", "serde_json", "walkdir", @@ -3011,12 +3047,13 @@ dependencies = [ "alloy-sol-types", "anyhow", "criterion", - "enumn", "indicatif", + "once_cell", "revm", "revm-database", + "revm-inspector", "revm-precompile", - "rstest", + "rstest 0.23.0", "serde", ] @@ -3029,18 +3066,17 @@ dependencies = [ "c-kzg", "cfg-if", "criterion", - "dyn-clone", "eyre", "k256", "kzg-rs", "once_cell", "p256", "rand", + "revm-context-interface", "revm-primitives", "revm-specification", - "revm-wiring", "ripemd", - "rstest", + "rstest 0.22.0", "secp256k1", "serde", "serde_derive", @@ -3088,33 +3124,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "revm-transaction" -version = "1.0.0" -dependencies = [ - "auto_impl", - "revm-primitives", - "revm-specification", - "serde", -] - -[[package]] -name = "revm-wiring" -version = "1.0.0" -dependencies = [ - "c-kzg", - "cfg-if", - "dyn-clone", - "kzg-rs", - "once_cell", - "revm-database-interface", - "revm-primitives", - "revm-specification", - "revm-state", - "revm-transaction", - "serde", -] - [[package]] name = "revme" version = "0.10.1" @@ -3163,7 +3172,7 @@ dependencies = [ "libc", "spin", "untrusted", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -3205,8 +3214,20 @@ checksum = "7b423f0e62bdd61734b67cd21ff50871dfaeb9cc74f869dcd6af974fbcb19936" dependencies = [ "futures", "futures-timer", - "rstest_macros", - "rustc_version 0.4.0", + "rstest_macros 0.22.0", + "rustc_version 0.4.1", +] + +[[package]] +name = "rstest" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a2c585be59b6b5dd66a9d2084aa1d8bd52fbdb806eafdeffb52791147862035" +dependencies = [ + "futures", + "futures-timer", + "rstest_macros 0.23.0", + "rustc_version 0.4.1", ] [[package]] @@ -3222,8 +3243,26 @@ dependencies = [ "quote", "regex", "relative-path", - "rustc_version 0.4.0", - "syn 2.0.70", + "rustc_version 0.4.1", + "syn 2.0.87", + "unicode-ident", +] + +[[package]] +name = "rstest_macros" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "825ea780781b15345a146be27eaefb05085e337e869bff01b4306a4fd4a9ad5a" +dependencies = [ + "cfg-if", + "glob", + "proc-macro-crate", + "proc-macro2", + "quote", + "regex", + "relative-path", + "rustc_version 0.4.1", + "syn 2.0.87", "unicode-ident", ] @@ -3290,9 +3329,9 @@ dependencies = [ [[package]] name = "rustc_version" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver 1.0.23", ] @@ -3307,7 +3346,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -3413,7 +3452,7 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -3519,7 +3558,7 @@ checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] @@ -3626,7 +3665,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -3705,7 +3744,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] @@ -3740,9 +3779,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.70" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0209b68b3613b093e0ec905354eccaedcfe83b8cb37cbdeae64026c3064c16" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -3758,7 +3797,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] @@ -3812,7 +3851,7 @@ dependencies = [ "cfg-if", "fastrand", "rustix", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -3832,7 +3871,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] @@ -3891,7 +3930,7 @@ dependencies = [ "pin-project-lite", "socket2", "tokio-macros", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -3902,7 +3941,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] @@ -3953,19 +3992,19 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" [[package]] name = "toml_edit" -version = "0.21.1" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap", "toml_datetime", - "winnow 0.5.40", + "winnow", ] [[package]] @@ -4028,7 +4067,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] @@ -4094,9 +4133,9 @@ checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unicode-normalization" @@ -4215,7 +4254,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", "wasm-bindgen-shared", ] @@ -4249,7 +4288,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4276,7 +4315,7 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -4318,6 +4357,15 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -4382,15 +4430,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "winnow" -version = "0.5.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" -dependencies = [ - "memchr", -] - [[package]] name = "winnow" version = "0.6.18" @@ -4426,7 +4465,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] [[package]] @@ -4446,5 +4485,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.87", ] diff --git a/Cargo.toml b/Cargo.toml index 768098dcb6..fba5545a7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,5 @@ [workspace] -resolver = "2" -default-members = ["crates/revm"] members = [ - # binary - "bins/revme", - # libraries "crates/revm", "crates/primitives", @@ -15,23 +10,32 @@ members = [ "crates/database/interface", "crates/bytecode", "crates/state", - "crates/wiring", - "crates/wiring/transaction", "crates/specification", - "crates/statetest-types", + "crates/context", + "crates/context/interface", + "crates/handler/interface", + "crates/handler", + + # binary + "bins/revme", # variants "crates/optimism", + # utility + "crates/statetest-types", + + # examples "examples/block_traces", "examples/contract_deployment", "examples/database_components", - "examples/database_ref", "examples/uniswap_get_reserves", "examples/uniswap_v2_usdc_swap", #"examples/custom_opcodes", ] +resolver = "2" +default-members = ["crates/revm"] [workspace.dependencies] # revm @@ -42,12 +46,19 @@ database = { path = "crates/database", package = "revm-database", version = "1.0 database-interface = { path = "crates/database/interface", package = "revm-database-interface", version = "1.0.0", default-features = false } specification = { path = "crates/specification", package = "revm-specification", version = "1.0.0", default-features = false } state = { path = "crates/state", package = "revm-state", version = "1.0.0", default-features = false } -wiring = { path = "crates/wiring", package = "revm-wiring", version = "1.0.0", default-features = false } -transaction = { path = "crates/wiring/transaction", package = "revm-transaction", version = "1.0.0", default-features = false } interpreter = { path = "crates/interpreter", package = "revm-interpreter", version = "10.0.1", default-features = false } inspector = { path = "crates/inspector", package = "revm-inspector", version = "1.0.0", default-features = false } precompile = { path = "crates/precompile", package = "revm-precompile", version = "11.0.1", default-features = false } statetest-types = { path = "crates/statetest-types", package = "revm-statetest-types", version = "1.0.0", default-features = false } +context = { path = "crates/context", package = "revm-context", version = "1.0.0", default-features = false } +context-interface = { path = "crates/context/interface", package = "revm-context-interface", version = "1.0.0", default-features = false } +handler = { path = "crates/handler", package = "revm-handler", version = "1.0.0", default-features = false } +handler-interface = { path = "crates/handler/interface", package = "revm-handler-interface", version = "1.0.0", default-features = false } + +# mics +cfg-if = { version = "1.0", default-features = false } +auto_impl = { version = "1.2.0" } +derive-where = { version = "1.2.7", default-features = false } [workspace.package] license = "MIT" diff --git a/bins/revme/src/cmd/bench/analysis.rs b/bins/revme/src/cmd/bench/analysis.rs index 8fc494f7d6..3eacf5e2a7 100644 --- a/bins/revme/src/cmd/bench/analysis.rs +++ b/bins/revme/src/cmd/bench/analysis.rs @@ -1,8 +1,9 @@ -use database::{BenchmarkDB, EthereumBenchmarkWiring}; +use database::BenchmarkDB; use revm::{ bytecode::Bytecode, + handler::EthHandler, primitives::{address, bytes, hex, Bytes, TxKind}, - Evm, + Context, MainEvm, }; use std::time::Instant; @@ -10,20 +11,19 @@ pub fn run() { let contract_data : Bytes = hex::decode( "6060604052341561000f57600080fd5b604051610dd1380380610dd18339810160405280805190602001909190805182019190602001805190602001909190805182019190505083600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508360008190555082600390805190602001906100a79291906100e3565b5081600460006101000a81548160ff021916908360ff16021790555080600590805190602001906100d99291906100e3565b5050505050610188565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061012457805160ff1916838001178555610152565b82800160010185558215610152579182015b82811115610151578251825591602001919060010190610136565b5b50905061015f9190610163565b5090565b61018591905b80821115610181576000816000905550600101610169565b5090565b90565b610c3a806101976000396000f3006060604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b4578063095ea7b31461014257806318160ddd1461019c57806323b872dd146101c557806327e235e31461023e578063313ce5671461028b5780635c658165146102ba57806370a082311461032657806395d89b4114610373578063a9059cbb14610401578063dd62ed3e1461045b575b600080fd5b34156100bf57600080fd5b6100c76104c7565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101075780820151818401526020810190506100ec565b50505050905090810190601f1680156101345780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561014d57600080fd5b610182600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610565565b604051808215151515815260200191505060405180910390f35b34156101a757600080fd5b6101af610657565b6040518082815260200191505060405180910390f35b34156101d057600080fd5b610224600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061065d565b604051808215151515815260200191505060405180910390f35b341561024957600080fd5b610275600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506108f7565b6040518082815260200191505060405180910390f35b341561029657600080fd5b61029e61090f565b604051808260ff1660ff16815260200191505060405180910390f35b34156102c557600080fd5b610310600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610922565b6040518082815260200191505060405180910390f35b341561033157600080fd5b61035d600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610947565b6040518082815260200191505060405180910390f35b341561037e57600080fd5b610386610990565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103c65780820151818401526020810190506103ab565b50505050905090810190601f1680156103f35780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561040c57600080fd5b610441600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610a2e565b604051808215151515815260200191505060405180910390f35b341561046657600080fd5b6104b1600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610b87565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561055d5780601f106105325761010080835404028352916020019161055d565b820191906000526020600020905b81548152906001019060200180831161054057829003601f168201915b505050505081565b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60005481565b600080600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015801561072e5750828110155b151561073957600080fd5b82600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156108865782600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a360019150509392505050565b60016020528060005260406000206000915090505481565b600460009054906101000a900460ff1681565b6002602052816000526040600020602052806000526040600020600091509150505481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a265780601f106109fb57610100808354040283529160200191610a26565b820191906000526020600020905b815481529060010190602001808311610a0957829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610a7e57600080fd5b81600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a36001905092915050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050929150505600a165627a7a72305820df254047bc8f2904ad3e966b6db116d703bebd40efadadb5e738c836ffc8f58a0029").unwrap().into(); let bytecode_raw = Bytecode::new_raw(contract_data.clone()); - let bytecode_analysed = Bytecode::new_raw(contract_data).into_analyzed(); + let bytecode_analysed = Bytecode::new_raw(contract_data); // BenchmarkDB is dummy state that implements Database trait. - let mut evm = Evm::::builder() - .modify_tx_env(|tx| { + let context = Context::builder() + .with_db(BenchmarkDB::new_bytecode(bytecode_raw)) + .modify_tx_chained(|tx| { // execution globals block hash/gas_limit/coinbase/timestamp.. tx.caller = address!("1000000000000000000000000000000000000000"); tx.transact_to = TxKind::Call(address!("0000000000000000000000000000000000000000")); //evm.env.tx.data = Bytes::from(hex::decode("30627b7c").unwrap()); tx.data = bytes!("8035F0CE"); - }) - .with_db(BenchmarkDB::new_bytecode(bytecode_raw)) - .with_default_ext_ctx() - .build(); + }); + let mut evm = MainEvm::new(context, EthHandler::default()); // Just to warm up the processor. for _ in 0..10000 { @@ -36,11 +36,8 @@ pub fn run() { } println!("Raw elapsed time: {:?}", timer.elapsed()); - let mut evm = evm - .modify() - .with_db(BenchmarkDB::new_bytecode(bytecode_analysed)) - .with_default_ext_ctx() - .build(); + evm.context + .modify_db(|db| *db = BenchmarkDB::new_bytecode(bytecode_analysed)); let timer = Instant::now(); for _ in 0..30000 { diff --git a/bins/revme/src/cmd/bench/burntpix.rs b/bins/revme/src/cmd/bench/burntpix.rs index c7f0755bf8..88f9201a13 100644 --- a/bins/revme/src/cmd/bench/burntpix.rs +++ b/bins/revme/src/cmd/bench/burntpix.rs @@ -10,14 +10,12 @@ use alloy_sol_macro::sol; use alloy_sol_types::SolCall; use database::CacheDB; use revm::{ + context_interface::result::{ExecutionResult, Output}, database_interface::EmptyDB, + handler::EthHandler, primitives::{address, hex, keccak256, Address, Bytes, TxKind, B256, U256}, state::{AccountInfo, Bytecode}, - wiring::{ - result::{ExecutionResult, Output}, - EthereumWiring, - }, - Evm, + Context, MainEvm, }; use std::fs::File; @@ -32,8 +30,6 @@ sol! { } } -type EthereumCacheDbWiring = EthereumWiring, ()>; - pub fn run() { let (seed, iterations) = try_init_env_vars().expect("Failed to parse env vars"); @@ -41,15 +37,12 @@ pub fn run() { let db = init_db(); - let mut evm = Evm::::builder() - .modify_tx_env(|tx| { - tx.caller = address!("1000000000000000000000000000000000000000"); - tx.transact_to = TxKind::Call(BURNTPIX_MAIN_ADDRESS); - tx.data = run_call_data.clone().into(); - }) - .with_db(db) - .with_default_ext_ctx() - .build(); + let context = Context::builder().with_db(db).modify_tx_chained(|tx| { + tx.caller = address!("1000000000000000000000000000000000000000"); + tx.transact_to = TxKind::Call(BURNTPIX_MAIN_ADDRESS); + tx.data = run_call_data.clone().into(); + }); + let mut evm = MainEvm::new(context, EthHandler::default()); let started = Instant::now(); let tx_result = evm.transact().unwrap().result; diff --git a/bins/revme/src/cmd/bench/snailtracer.rs b/bins/revme/src/cmd/bench/snailtracer.rs index 72aa904a33..1d1f20c976 100644 --- a/bins/revme/src/cmd/bench/snailtracer.rs +++ b/bins/revme/src/cmd/bench/snailtracer.rs @@ -1,24 +1,23 @@ -use database::{BenchmarkDB, EthereumBenchmarkWiring}; +use database::BenchmarkDB; use revm::{ bytecode::Bytecode, + handler::EthHandler, primitives::{address, bytes, Bytes, TxKind}, - Evm, + Context, MainEvm, }; pub fn simple_example() { - let bytecode = Bytecode::new_raw(CONTRACT_DATA.clone()).into_analyzed(); + let bytecode = Bytecode::new_raw(CONTRACT_DATA.clone()); - // BenchmarkDB is dummy state that implements Database trait. - let mut evm = Evm::::builder() + let context = Context::builder() .with_db(BenchmarkDB::new_bytecode(bytecode.clone())) - .with_default_ext_ctx() - .modify_tx_env(|tx| { + .modify_tx_chained(|tx| { // execution globals block hash/gas_limit/coinbase/timestamp.. tx.caller = address!("1000000000000000000000000000000000000000"); tx.transact_to = TxKind::Call(address!("0000000000000000000000000000000000000000")); tx.data = bytes!("30627b7c"); - }) - .build(); + }); + let mut evm = MainEvm::new(context, EthHandler::default()); let _ = evm.transact().unwrap(); } diff --git a/bins/revme/src/cmd/bench/transfer.rs b/bins/revme/src/cmd/bench/transfer.rs index 027f1775e8..e5a50b9167 100644 --- a/bins/revme/src/cmd/bench/transfer.rs +++ b/bins/revme/src/cmd/bench/transfer.rs @@ -1,17 +1,16 @@ -use database::{BenchmarkDB, EthereumBenchmarkWiring}; +use database::BenchmarkDB; use revm::{ bytecode::Bytecode, + handler::EthHandler, primitives::{TxKind, U256}, - Evm, + Context, MainEvm, }; use std::time::Duration; pub fn run() { - // BenchmarkDB is dummy state that implements Database trait. - let mut evm = Evm::::builder() + let context = Context::builder() .with_db(BenchmarkDB::new_bytecode(Bytecode::new())) - .with_default_ext_ctx() - .modify_tx_env(|tx| { + .modify_tx_chained(|tx| { // execution globals block hash/gas_limit/coinbase/timestamp.. tx.caller = "0x0000000000000000000000000000000000000001" .parse() @@ -22,8 +21,8 @@ pub fn run() { .parse() .unwrap(), ); - }) - .build(); + }); + let mut evm = MainEvm::new(context, EthHandler::default()); // Microbenchmark let bench_options = microbench::Options::default().time(Duration::from_secs(3)); diff --git a/bins/revme/src/cmd/evmrunner.rs b/bins/revme/src/cmd/evmrunner.rs index cd95ca39cf..8f1d6affbf 100644 --- a/bins/revme/src/cmd/evmrunner.rs +++ b/bins/revme/src/cmd/evmrunner.rs @@ -1,11 +1,11 @@ use clap::Parser; use database::BenchmarkDB; -use inspector::{inspector_handle_register, inspectors::TracerEip3155}; +use inspector::{inspector_handler, inspectors::TracerEip3155, InspectorContext, InspectorMainEvm}; use revm::{ bytecode::{Bytecode, BytecodeDecodeError}, + handler::EthHandler, primitives::{address, hex, Address, TxKind}, - wiring::EthereumWiring, - Database, Evm, + Context, Database, MainEvm, }; use std::io::Error as IoError; use std::path::PathBuf; @@ -81,16 +81,15 @@ impl Cmd { // BenchmarkDB is dummy state that implements Database trait. // the bytecode is deployed at zero address. - let mut evm = Evm::>::builder() - .with_db(db) - .modify_tx_env(|tx| { - // execution globals block hash/gas_limit/coinbase/timestamp.. + let mut evm = MainEvm::new( + Context::builder().with_db(db).modify_tx_chained(|tx| { tx.caller = CALLER; tx.transact_to = TxKind::Call(Address::ZERO); tx.data = input; tx.nonce = nonce; - }) - .build(); + }), + EthHandler::default(), + ); if self.bench { // Microbenchmark @@ -104,11 +103,10 @@ impl Cmd { } let out = if self.trace { - let mut evm = evm - .modify() - .with_external_context(TracerEip3155::new(Box::new(std::io::stdout()))) - .append_handler_register(inspector_handle_register) - .build(); + let mut evm = InspectorMainEvm::new( + InspectorContext::new(evm.context, TracerEip3155::new(Box::new(std::io::stdout()))), + inspector_handler(), + ); evm.transact().map_err(|_| Errors::EVMError)? } else { diff --git a/bins/revme/src/cmd/statetest/runner.rs b/bins/revme/src/cmd/statetest/runner.rs index 9dc709ac41..e484cefa06 100644 --- a/bins/revme/src/cmd/statetest/runner.rs +++ b/bins/revme/src/cmd/statetest/runner.rs @@ -4,26 +4,28 @@ use super::{ }; use database::State; use indicatif::{ProgressBar, ProgressDrawTarget}; -use inspector::{inspector_handle_register, inspectors::TracerEip3155}; +use inspector::{inspector_handler, inspectors::TracerEip3155, InspectorContext, InspectorMainEvm}; use revm::{ bytecode::Bytecode, + context::{block::BlockEnv, tx::TxEnv}, + context_interface::{ + block::calc_excess_blob_gas, + result::{EVMError, ExecutionResult, HaltReason, InvalidTransaction}, + Cfg, CfgEnv, + }, database_interface::EmptyDB, + handler::EthHandler, primitives::{keccak256, Bytes, TxKind, B256}, specification::{eip7702::AuthorizationList, hardfork::SpecId}, - wiring::{ - block::calc_excess_blob_gas, - default::EnvWiring, - result::{EVMResultGeneric, ExecutionResult, HaltReason}, - EthereumWiring, - }, - Evm, + Context, DatabaseCommit, EvmCommit, MainEvm, }; use serde_json::json; use statetest_types::{SpecName, Test, TestSuite}; use std::{ + convert::Infallible, fmt::Debug, - io::{stderr, stdout}, + io::stdout, path::{Path, PathBuf}, sync::{ atomic::{AtomicBool, AtomicUsize, Ordering}, @@ -34,13 +36,11 @@ use std::{ use thiserror::Error; use walkdir::{DirEntry, WalkDir}; -type ExecEvmWiring<'a> = EthereumWiring<&'a mut State, ()>; -type TraceEvmWiring<'a> = EthereumWiring<&'a mut State, TracerEip3155>; - #[derive(Debug, Error)] -#[error("Test {name} failed: {kind}")] +#[error("Path: {path}\nName: {name}\nError: {kind}")] pub struct TestError { pub name: String, + pub path: String, pub kind: TestErrorKind, } @@ -145,19 +145,17 @@ fn skip_test(path: &Path) -> bool { ) } -fn check_evm_execution( +fn check_evm_execution( test: &Test, expected_output: Option<&Bytes>, test_name: &str, - exec_result: &EVMResultGeneric< - ExecutionResult, - EthereumWiring<&mut State, EXT>, - >, - evm: &Evm<'_, EthereumWiring<&mut State, EXT>>, + exec_result: &Result, EVMError>, + db: &mut State, + spec: SpecId, print_json_outcome: bool, -) -> Result<(), TestError> { +) -> Result<(), TestErrorKind> { let logs_root = log_rlp_hash(exec_result.as_ref().map(|r| r.logs()).unwrap_or_default()); - let state_root = state_merkle_trie_root(evm.context.evm.db.cache.trie_account()); + let state_root = state_merkle_trie_root(db.cache.trie_account()); let print_json_output = |error: Option| { if print_json_outcome { @@ -177,7 +175,7 @@ fn check_evm_execution( Err(e) => e.to_string(), }, "postLogsHash": logs_root, - "fork": evm.handler.spec_id(), + "fork": spec, "test": test_name, "d": test.indexes.data, "g": test.indexes.gas, @@ -208,10 +206,7 @@ fn check_evm_execution( got_output: result.output().cloned(), }; print_json_output(Some(kind.to_string())); - return Err(TestError { - name: test_name.to_string(), - kind, - }); + return Err(kind); } } } @@ -223,10 +218,7 @@ fn check_evm_execution( got_exception: exec_result.clone().err().map(|e| e.to_string()), }; print_json_output(Some(kind.to_string())); - return Err(TestError { - name: test_name.to_string(), - kind, - }); + return Err(kind); } } @@ -236,10 +228,7 @@ fn check_evm_execution( expected: test.logs, }; print_json_output(Some(kind.to_string())); - return Err(TestError { - name: test_name.to_string(), - kind, - }); + return Err(kind); } if state_root != test.hash { @@ -248,10 +237,7 @@ fn check_evm_execution( expected: test.hash, }; print_json_output(Some(kind.to_string())); - return Err(TestError { - name: test_name.to_string(), - kind, - }); + return Err(kind); } print_json_output(None); @@ -270,8 +256,10 @@ pub fn execute_test_suite( } let s = std::fs::read_to_string(path).unwrap(); + let path = path.to_string_lossy().into_owned(); let suite: TestSuite = serde_json::from_str(&s).map_err(|e| TestError { - name: path.to_string_lossy().into_owned(), + name: "Unknown".to_string(), + path: path.clone(), kind: e.into(), })?; @@ -280,7 +268,7 @@ pub fn execute_test_suite( let mut cache_state = database::CacheState::new(false); for (address, info) in unit.pre { let code_hash = keccak256(&info.code); - let bytecode = Bytecode::new_raw(info.code).into_analyzed(); + let bytecode = Bytecode::new_raw(info.code); let acc_info = revm::state::AccountInfo { balance: info.balance, code_hash, @@ -290,73 +278,73 @@ pub fn execute_test_suite( cache_state.insert_account_with_storage(address, acc_info, info.storage); } - let mut env = Box::>::default(); + let mut cfg = CfgEnv::default(); + let mut block = BlockEnv::default(); + let mut tx = TxEnv::default(); // for mainnet - env.cfg.chain_id = 1; - // env.cfg.spec_id is set down the road + cfg.chain_id = 1; // block env - env.block.number = unit.env.current_number; - env.block.coinbase = unit.env.current_coinbase; - env.block.timestamp = unit.env.current_timestamp; - env.block.gas_limit = unit.env.current_gas_limit; - env.block.basefee = unit.env.current_base_fee.unwrap_or_default(); - env.block.difficulty = unit.env.current_difficulty; + block.number = unit.env.current_number; + block.beneficiary = unit.env.current_coinbase; + block.timestamp = unit.env.current_timestamp; + block.gas_limit = unit.env.current_gas_limit; + block.basefee = unit.env.current_base_fee.unwrap_or_default(); + block.difficulty = unit.env.current_difficulty; // after the Merge prevrandao replaces mix_hash field in block and replaced difficulty opcode in EVM. - env.block.prevrandao = unit.env.current_random; + block.prevrandao = unit.env.current_random; // EIP-4844 if let Some(current_excess_blob_gas) = unit.env.current_excess_blob_gas { - env.block - .set_blob_excess_gas_and_price(current_excess_blob_gas.to()); + block.set_blob_excess_gas_and_price(current_excess_blob_gas.to()); } else if let (Some(parent_blob_gas_used), Some(parent_excess_blob_gas)) = ( unit.env.parent_blob_gas_used, unit.env.parent_excess_blob_gas, ) { - env.block - .set_blob_excess_gas_and_price(calc_excess_blob_gas( - parent_blob_gas_used.to(), - parent_excess_blob_gas.to(), - )); + block.set_blob_excess_gas_and_price(calc_excess_blob_gas( + parent_blob_gas_used.to(), + parent_excess_blob_gas.to(), + )); } // tx env - env.tx.caller = if let Some(address) = unit.transaction.sender { + tx.caller = if let Some(address) = unit.transaction.sender { address } else { recover_address(unit.transaction.secret_key.as_slice()).ok_or_else(|| TestError { name: name.clone(), + path: path.clone(), kind: TestErrorKind::UnknownPrivateKey(unit.transaction.secret_key), })? }; - env.tx.gas_price = unit + tx.gas_price = unit .transaction .gas_price .or(unit.transaction.max_fee_per_gas) .unwrap_or_default(); - env.tx.gas_priority_fee = unit.transaction.max_priority_fee_per_gas; + tx.gas_priority_fee = unit.transaction.max_priority_fee_per_gas; // EIP-4844 - env.tx.blob_hashes = unit.transaction.blob_versioned_hashes.clone(); - env.tx.max_fee_per_blob_gas = unit.transaction.max_fee_per_blob_gas; + tx.blob_hashes = unit.transaction.blob_versioned_hashes.clone(); + tx.max_fee_per_blob_gas = unit.transaction.max_fee_per_blob_gas; // post and execution for (spec_name, tests) in unit.post { // Constantinople was immediately extended by Petersburg. // There isn't any production Constantinople transaction // so we don't support it and skip right to Petersburg. - if spec_name == SpecName::Constantinople || spec_name == SpecName::Osaka { + if spec_name == SpecName::Constantinople { continue; } // Enable EOF in Prague tests. - let spec_id = if spec_name == SpecName::Prague { + cfg.spec = if spec_name == SpecName::Prague { SpecId::PRAGUE_EOF } else { spec_name.to_spec_id() }; - if spec_id.is_enabled_in(SpecId::MERGE) && env.block.prevrandao.is_none() { + if cfg.spec.is_enabled_in(SpecId::MERGE) && block.prevrandao.is_none() { // if spec is merge and prevrandao is not set, set it to default - env.block.prevrandao = Some(B256::default()); + block.prevrandao = Some(B256::default()); } for (index, test) in tests.into_iter().enumerate() { @@ -369,21 +357,21 @@ pub fn execute_test_suite( } }; - env.tx.tx_type = tx_type; + tx.tx_type = tx_type; - env.tx.gas_limit = unit.transaction.gas_limit[test.indexes.gas].saturating_to(); + tx.gas_limit = unit.transaction.gas_limit[test.indexes.gas].saturating_to(); - env.tx.data = unit + tx.data = unit .transaction .data .get(test.indexes.data) .unwrap() .clone(); - env.tx.nonce = u64::try_from(unit.transaction.nonce).unwrap(); - env.tx.value = unit.transaction.value[test.indexes.value]; + tx.nonce = u64::try_from(unit.transaction.nonce).unwrap(); + tx.value = unit.transaction.value[test.indexes.value]; - env.tx.access_list = unit + tx.access_list = unit .transaction .access_lists .get(test.indexes.data) @@ -392,7 +380,7 @@ pub fn execute_test_suite( .unwrap_or_default() .into(); - env.tx.authorization_list = unit + tx.authorization_list = unit .transaction .authorization_list .as_ref() @@ -407,61 +395,72 @@ pub fn execute_test_suite( Some(add) => TxKind::Call(add), None => TxKind::Create, }; - env.tx.transact_to = to; + tx.transact_to = to; let mut cache = cache_state.clone(); - cache.set_state_clear_flag(SpecId::enabled(spec_id, SpecId::SPURIOUS_DRAGON)); + cache.set_state_clear_flag(cfg.spec.is_enabled_in(SpecId::SPURIOUS_DRAGON)); let mut state = database::State::builder() .with_cached_prestate(cache) .with_bundle_update() .build(); - let mut evm = Evm::::builder() - .with_db(&mut state) - .with_default_ext_ctx() - .modify_env(|e| e.clone_from(&env)) - .with_spec_id(spec_id) - .build(); + let mut evm = MainEvm::new( + Context::builder() + .with_block(&block) + .with_tx(&tx) + .with_cfg(&cfg) + .with_db(&mut state), + EthHandler::default(), + ); // do the deed let (e, exec_result) = if trace { - let mut evm = evm - .modify() - .reset_handler_with_external_context::>() - .with_external_context( - TracerEip3155::new(Box::new(stderr())).without_summary(), - ) - .with_spec_id(spec_id) - .append_handler_register(inspector_handle_register) - .build(); + let mut evm = InspectorMainEvm::new( + InspectorContext::new( + Context::builder() + .with_block(&block) + .with_tx(&tx) + .with_cfg(&cfg) + .with_db(&mut state), + TracerEip3155::new(Box::new(stdout())), + ), + inspector_handler(), + ); let timer = Instant::now(); - let res = evm.transact_commit(); + let res = evm.exec_commit(); *elapsed.lock().unwrap() += timer.elapsed(); - let Err(e) = check_evm_execution( + let spec = cfg.spec(); + let db = evm.context.inner.journaled_state.database; + // dump state and traces if test failed + let output = check_evm_execution( &test, unit.out.as_ref(), &name, &res, - &evm, + db, + spec, print_json_outcome, - ) else { + ); + let Err(e) = output else { continue; }; - // reset external context (e, res) } else { let timer = Instant::now(); - let res = evm.transact_commit(); + let res = evm.exec_commit(); *elapsed.lock().unwrap() += timer.elapsed(); + let spec = cfg.spec(); + let db = evm.context.journaled_state.database; // dump state and traces if test failed let output = check_evm_execution( &test, unit.out.as_ref(), &name, &res, - &evm, + db, + spec, print_json_outcome, ); let Err(e) = output else { @@ -474,39 +473,59 @@ pub fn execute_test_suite( // if we are already in trace mode, just return error static FAILED: AtomicBool = AtomicBool::new(false); if trace || FAILED.swap(true, Ordering::SeqCst) { - return Err(e); + return Err(TestError { + name: name.clone(), + path: path.clone(), + kind: e, + }); } // re build to run with tracing let mut cache = cache_state.clone(); - cache.set_state_clear_flag(SpecId::enabled(spec_id, SpecId::SPURIOUS_DRAGON)); + cache.set_state_clear_flag(cfg.spec.is_enabled_in(SpecId::SPURIOUS_DRAGON)); let mut state = database::State::builder() .with_cached_prestate(cache) .with_bundle_update() .build(); - let path = path.display(); println!("\nTraces:"); - let mut evm = Evm::::builder() - .with_db(&mut state) - .with_spec_id(spec_id) - .with_env(env.clone()) - .reset_handler_with_external_context::>() - .with_external_context(TracerEip3155::new(Box::new(stdout())).without_summary()) - .with_spec_id(spec_id) - .append_handler_register(inspector_handle_register) - .build(); - let _ = evm.transact_commit(); + + let mut evm = InspectorMainEvm::new( + InspectorContext::new( + Context::builder() + .with_db(&mut state) + .with_block(&block) + .with_tx(&tx) + .with_cfg(&cfg), + TracerEip3155::new(Box::new(stdout())), + ), + inspector_handler(), + ); + + let res = evm.transact(); + let _ = res.map(|r| { + evm.context.inner.journaled_state.database.commit(r.state); + r.result + }); println!("\nExecution result: {exec_result:#?}"); println!("\nExpected exception: {:?}", test.expect_exception); println!("\nState before: {cache_state:#?}"); - println!("\nState after: {:#?}", evm.context.evm.db.cache); - println!("\nSpecification: {spec_id:?}"); - println!("\nEnvironment: {env:#?}"); - println!("\nTest name: {name:?} (index: {index}, path: {path}) failed:\n{e}"); - - return Err(e); + println!( + "\nState after: {:#?}", + evm.context.inner.journaled_state.database.cache + ); + println!("\nSpecification: {:?}", cfg.spec); + println!("\nTx: {tx:#?}"); + println!("Block: {block:#?}"); + println!("Cfg: {cfg:#?}"); + println!("\nTest name: {name:?} (index: {index}, path: {path:?}) failed:\n{e}"); + + return Err(TestError { + path: path.clone(), + name: name.clone(), + kind: e, + }); } } } @@ -590,6 +609,7 @@ pub fn run( Ok(Err(e)) => thread_errors.push(e), Err(_) => thread_errors.push(TestError { name: format!("thread {i} panicked"), + path: "".to_string(), kind: TestErrorKind::Panic, }), } diff --git a/crates/bytecode/src/bytecode.rs b/crates/bytecode/src/bytecode.rs index f1a4a58ae2..e0dcfddde6 100644 --- a/crates/bytecode/src/bytecode.rs +++ b/crates/bytecode/src/bytecode.rs @@ -11,8 +11,6 @@ use std::sync::Arc; #[derive(Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum Bytecode { - /// No analysis has been performed. - LegacyRaw(LegacyRawBytecode), /// The bytecode has been analyzed for valid jump destinations. LegacyAnalyzed(LegacyAnalyzedBytecode), /// Ethereum Object Format @@ -77,7 +75,7 @@ impl Bytecode { /// Creates a new legacy [`Bytecode`]. #[inline] pub fn new_legacy(raw: Bytes) -> Self { - Self::LegacyRaw(raw.into()) + Self::LegacyAnalyzed(LegacyRawBytecode(raw).into_analyzed()) } /// Creates a new raw [`Bytecode`]. @@ -100,35 +98,21 @@ impl Bytecode { /// /// Returns an error on incorrect Bytecode format. #[inline] - pub fn new_raw_checked(bytecode: Bytes) -> Result { - let prefix = bytecode.get(..2); + pub fn new_raw_checked(bytes: Bytes) -> Result { + let prefix = bytes.get(..2); match prefix { Some(prefix) if prefix == &EOF_MAGIC_BYTES => { - let eof = Eof::decode(bytecode)?; + let eof = Eof::decode(bytes)?; Ok(Self::Eof(Arc::new(eof))) } Some(prefix) if prefix == &EIP7702_MAGIC_BYTES => { - let eip7702 = Eip7702Bytecode::new_raw(bytecode)?; + let eip7702 = Eip7702Bytecode::new_raw(bytes)?; Ok(Self::Eip7702(eip7702)) } - _ => Ok(Self::LegacyRaw(bytecode.into())), + _ => Ok(Self::new_legacy(bytes)), } } - /// Perform bytecode analysis. - /// - /// The analysis finds and caches valid jump destinations for later execution as an optimization step. - /// - /// If the bytecode is already analyzed, it is returned as-is. - #[inline] - pub fn into_analyzed(self) -> Bytecode { - let Bytecode::LegacyRaw(bytecode) = self else { - return self; - }; - - Bytecode::LegacyAnalyzed(bytecode.into_analyzed()) - } - /// Create new checked bytecode. /// /// # Safety @@ -149,25 +133,16 @@ impl Bytecode { /// Returns a reference to the bytecode. /// - /// In case of EOF this will be the first code section. + /// In case of EOF this will be the all code sections. #[inline] pub fn bytecode(&self) -> &Bytes { match self { - Self::LegacyRaw(bytes) => bytes, Self::LegacyAnalyzed(analyzed) => analyzed.bytecode(), - Self::Eof(eof) => eof - .body - .code(0) - .expect("Valid EOF has at least one code section"), + Self::Eof(eof) => &eof.body.code, Self::Eip7702(code) => code.raw(), } } - /// Returns false if bytecode can't be executed in Interpreter. - pub fn is_execution_ready(&self) -> bool { - !matches!(self, Self::LegacyRaw(_)) - } - /// Returns bytes #[inline] pub fn bytes(&self) -> Bytes { @@ -190,7 +165,6 @@ impl Bytecode { #[inline] pub fn original_bytes(&self) -> Bytes { match self { - Self::LegacyRaw(bytes) => bytes.0.clone(), Self::LegacyAnalyzed(analyzed) => analyzed.original_bytes(), Self::Eof(eof) => eof.raw().clone(), Self::Eip7702(eip7702) => eip7702.raw().clone(), @@ -201,7 +175,6 @@ impl Bytecode { #[inline] pub fn original_byte_slice(&self) -> &[u8] { match self { - Self::LegacyRaw(bytes) => bytes, Self::LegacyAnalyzed(analyzed) => analyzed.original_byte_slice(), Self::Eof(eof) => eof.raw(), Self::Eip7702(eip7702) => eip7702.raw(), diff --git a/crates/bytecode/src/eof.rs b/crates/bytecode/src/eof.rs index 6aa7a00597..be74b36e22 100644 --- a/crates/bytecode/src/eof.rs +++ b/crates/bytecode/src/eof.rs @@ -40,8 +40,9 @@ impl Default for Eof { let body = EofBody { // types section with zero inputs, zero outputs and zero max stack size. types_section: vec![TypesSection::default()], + code_section: vec![1], // One code section with a STOP byte. - code_section: vec![Bytes::from_static(&[0x00])], + code: Bytes::from_static(&[0x00]), container_section: vec![], data_section: Bytes::new(), is_data_filled: true, diff --git a/crates/bytecode/src/eof/body.rs b/crates/bytecode/src/eof/body.rs index 66ccd9707c..05bb4180a6 100644 --- a/crates/bytecode/src/eof/body.rs +++ b/crates/bytecode/src/eof/body.rs @@ -10,8 +10,11 @@ use std::vec::Vec; #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct EofBody { + /// Code information pub types_section: Vec, - pub code_section: Vec, + /// Index of the last byte of each code section. + pub code_section: Vec, + pub code: Bytes, pub container_section: Vec, pub data_section: Bytes, pub is_data_filled: bool, @@ -19,23 +22,38 @@ pub struct EofBody { impl EofBody { /// Returns the code section at the given index. - pub fn code(&self, index: usize) -> Option<&Bytes> { - self.code_section.get(index) + pub fn code(&self, index: usize) -> Option { + if index == 0 { + // There should be at least one code section. + return Some(self.code.slice(..self.code_section[0])); + } + self.code_section + .get(index) + .map(|end| self.code.slice(self.code_section[index - 1]..*end)) } /// Creates an EOF container from this body. pub fn into_eof(self) -> Eof { // TODO add bounds checks. + let mut prev_value = 0; let header = EofHeader { types_size: self.types_section.len() as u16 * 4, - code_sizes: self.code_section.iter().map(|x| x.len() as u16).collect(), + code_sizes: self + .code_section + .iter() + .map(|x| { + let ret = (x - prev_value) as u16; + prev_value = *x; + ret + }) + .collect(), container_sizes: self .container_section .iter() .map(|x| x.len() as u16) .collect(), data_size: self.data_section.len() as u16, - sum_code_sizes: self.code_section.iter().map(|x| x.len()).sum(), + sum_code_sizes: self.code.len(), sum_container_sizes: self.container_section.iter().map(|x| x.len()).sum(), }; let mut buffer = Vec::new(); @@ -48,15 +66,24 @@ impl EofBody { } } + /// Returns offset of the start of indexed code section. + /// + /// First code section starts at 0. + pub fn eof_code_section_start(&self, idx: usize) -> Option { + // starting code section start with 0. + if idx == 0 { + return Some(0); + } + self.code_section.get(idx - 1).cloned() + } + /// Encodes this body into the given buffer. pub fn encode(&self, buffer: &mut Vec) { for types_section in &self.types_section { types_section.encode(buffer); } - for code_section in &self.code_section { - buffer.extend_from_slice(code_section); - } + buffer.extend_from_slice(&self.code); for container_section in &self.container_section { buffer.extend_from_slice(container_section); @@ -90,13 +117,16 @@ impl EofBody { } // extract code section - let mut start = header_len + header.types_size as usize; + let start = header_len + header.types_size as usize; + let mut code_end = 0; for size in header.code_sizes.iter().map(|x| *x as usize) { - body.code_section.push(input.slice(start..start + size)); - start += size; + code_end += size; + body.code_section.push(code_end); } + body.code = input.slice(start..start + header.sum_code_sizes); // extract container section + let mut start = start + header.sum_code_sizes; for size in header.container_sizes.iter().map(|x| *x as usize) { body.container_section .push(input.slice(start..start + size)); diff --git a/crates/bytecode/src/eof/verification.rs b/crates/bytecode/src/eof/verification.rs index dd14a216b6..516ec6a32c 100644 --- a/crates/bytecode/src/eof/verification.rs +++ b/crates/bytecode/src/eof/verification.rs @@ -102,9 +102,9 @@ pub fn validate_eof_codes( while let Some(index) = tracker.processing_stack.pop() { // assume index is correct. - let code = &eof.body.code_section[index]; + let code = eof.body.code(index).unwrap(); validate_eof_code( - code, + &code, eof.header.data_size as usize, index, eof.body.container_section.len(), diff --git a/crates/bytecode/src/legacy/jump_map.rs b/crates/bytecode/src/legacy/jump_map.rs index bd791646fa..262aa7ad16 100644 --- a/crates/bytecode/src/legacy/jump_map.rs +++ b/crates/bytecode/src/legacy/jump_map.rs @@ -31,6 +31,6 @@ impl JumpTable { /// Check if `pc` is a valid jump destination. #[inline] pub fn is_valid(&self, pc: usize) -> bool { - pc < self.0.len() && self.0[pc] + pc < self.0.len() && unsafe { *self.0.get_unchecked(pc) } } } diff --git a/crates/bytecode/src/opcode.rs b/crates/bytecode/src/opcode.rs index 54c2a3321e..d96cf5ea49 100644 --- a/crates/bytecode/src/opcode.rs +++ b/crates/bytecode/src/opcode.rs @@ -138,6 +138,12 @@ impl OpCode { } } + /// Returns the opcode as a usize. + #[inline] + pub const fn as_usize(&self) -> usize { + self.0 as usize + } + /// Returns the opcode information. #[inline] pub const fn info(&self) -> OpCodeInfo { diff --git a/crates/context/Cargo.toml b/crates/context/Cargo.toml index 83b0064a81..1fc05fe7cb 100644 --- a/crates/context/Cargo.toml +++ b/crates/context/Cargo.toml @@ -22,14 +22,35 @@ rust_2018_idioms = "deny" all = "warn" [dependencies] +# revm +interpreter.workspace = true +context-interface.workspace = true +primitives.workspace = true +database-interface.workspace = true +state.workspace = true +specification.workspace = true +bytecode.workspace = true + +# misc +derive-where.workspace = true + # Optional serde = { version = "1.0", default-features = false, features = [ "derive", "rc", ], optional = true } +[dev-dependencies] +database.workspace = true + [features] default = ["std"] std = ["serde?/std"] -serde = ["dep:serde"] +serde = [ + "dep:serde", + "primitives/serde", + "specification/serde", + "state/serde", + "context-interface/serde", +] serde-json = ["serde"] diff --git a/crates/gas/CHANGELOG.md b/crates/context/interface/CHANGELOG.md similarity index 100% rename from crates/gas/CHANGELOG.md rename to crates/context/interface/CHANGELOG.md diff --git a/crates/wiring/Cargo.toml b/crates/context/interface/Cargo.toml similarity index 51% rename from crates/wiring/Cargo.toml rename to crates/context/interface/Cargo.toml index 17cb501f58..49d0a71bc0 100644 --- a/crates/wiring/Cargo.toml +++ b/crates/context/interface/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "revm-wiring" -description = "Revm generic types wiring" +name = "revm-context-interface" +description = "Revm context interface crates" version = "1.0.0" authors.workspace = true edition.workspace = true @@ -25,12 +25,12 @@ all = "warn" # revm primitives.workspace = true database-interface.workspace = true -specification.workspace = true state.workspace = true -transaction.workspace = true +specification.workspace = true # mics -dyn-clone = "1.0" +cfg-if.workspace = true +auto_impl.workspace = true # Optional serde = { version = "1.0", default-features = false, features = [ @@ -38,38 +38,17 @@ serde = { version = "1.0", default-features = false, features = [ "rc", ], optional = true } - -# For setting the CfgEnv KZGSettings. Enabled by c-kzg flag. -# Optionally use `kzg-rs`(Not audited) for a pure Rust implementation of KZG. -c-kzg = { version = "1.0.3", default-features = false, optional = true, features = [ - "ethereum_kzg_settings", -] } -kzg-rs = { version = "0.2.3", default-features = false, optional = true } -cfg-if = { version = "1", default-features = false, optional = true } -once_cell = { version = "1.19", default-features = false, optional = true, features = [ - "alloc", -] } - [dev-dependencies] +database.workspace = true [features] -default = ["std", "portable"] +default = ["std"] std = ["serde?/std"] -serde = [ - "dep:serde", - "primitives/serde", - "transaction/serde", - "specification/serde", - "state/serde", -] -portable = ["c-kzg?/portable"] - -c-kzg = ["dep:c-kzg", "dep:cfg-if"] -# `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", "dep:cfg-if", "dep:once_cell"] +serde = ["dep:serde", "primitives/serde", "specification/serde", "state/serde"] +serde-json = ["serde"] # Enable additional features for development +# They add functions to Cfg trait. dev = [ "memory_limit", "optional_balance_check", @@ -77,6 +56,7 @@ dev = [ "optional_eip3607", "optional_gas_refund", "optional_no_base_fee", + ] memory_limit = [] optional_balance_check = [] diff --git a/crates/wiring/src/block.rs b/crates/context/interface/src/block.rs similarity index 82% rename from crates/wiring/src/block.rs rename to crates/context/interface/src/block.rs index 2215635f19..57dfaae712 100644 --- a/crates/wiring/src/block.rs +++ b/crates/context/interface/src/block.rs @@ -2,17 +2,19 @@ pub mod blob; pub use blob::{calc_blob_gasprice, calc_excess_blob_gas, BlobExcessGasAndPrice}; +use auto_impl::auto_impl; use primitives::{Address, B256, U256}; -/// Trait for retrieving block information required for execution. +/// Trait for retrieving block information required for execution.\ +#[auto_impl(&, &mut, Box, Arc)] pub trait Block { /// The number of ancestor blocks of this block (block height). fn number(&self) -> &U256; - /// Coinbase or miner or address that created and signed the block. + /// Beneficiary (Coinbase, miner) is a address that have signed the block. /// - /// This is the receiver address of all the gas spent in the block. - fn coinbase(&self) -> &Address; + /// This is the receiver address of priority gas rewards. + fn beneficiary(&self) -> &Address; /// The timestamp of the block in seconds since the UNIX epoch. fn timestamp(&self) -> &U256; @@ -66,3 +68,14 @@ pub trait Block { self.blob_excess_gas_and_price().map(|a| a.excess_blob_gas) } } + +#[auto_impl(&, &mut, Box, Arc)] +pub trait BlockGetter { + type Block: Block; + + fn block(&self) -> &Self::Block; +} + +pub trait BlockSetter: BlockGetter { + fn set_block(&mut self, block: ::Block); +} diff --git a/crates/wiring/src/block/blob.rs b/crates/context/interface/src/block/blob.rs similarity index 100% rename from crates/wiring/src/block/blob.rs rename to crates/context/interface/src/block/blob.rs diff --git a/crates/context/interface/src/cfg.rs b/crates/context/interface/src/cfg.rs new file mode 100644 index 0000000000..5fc8cbe6ad --- /dev/null +++ b/crates/context/interface/src/cfg.rs @@ -0,0 +1,210 @@ +use auto_impl::auto_impl; +use core::fmt::Debug; +use core::hash::Hash; +use primitives::{TxKind, U256}; +use specification::{constants::MAX_CODE_SIZE, hardfork::SpecId}; + +#[auto_impl(&, &mut, Box, Arc)] +pub trait Cfg { + type Spec: Into; + + fn chain_id(&self) -> u64; + + // TODO Make SpecId a associated type but for faster development we use impl Into. + fn spec(&self) -> Self::Spec; + + fn max_code_size(&self) -> usize; + + fn is_eip3607_disabled(&self) -> bool; + + fn is_balance_check_disabled(&self) -> bool; + + fn is_gas_refund_disabled(&self) -> bool; + + fn is_block_gas_limit_disabled(&self) -> bool; + + fn is_nonce_check_disabled(&self) -> bool; + + fn is_base_fee_check_disabled(&self) -> bool; +} + +/// What bytecode analysis to perform. +#[derive(Clone, Default, Debug, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum AnalysisKind { + /// Do not perform bytecode analysis. + Raw, + /// Perform bytecode analysis. + #[default] + Analyse, +} + +/// EVM configuration. +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, Eq, PartialEq)] +#[non_exhaustive] +pub struct CfgEnv = SpecId> { + /// Chain ID of the EVM, it will be compared to the transaction's Chain ID. + /// Chain ID is introduced EIP-155 + pub chain_id: u64, + /// Specification for EVM represent the hardfork. + pub spec: SPEC, + /// If some it will effects EIP-170: Contract code size limit. Useful to increase this because of tests. + /// By default it is 0x6000 (~25kb). + pub limit_contract_code_size: Option, + /// Skips the nonce validation against the account's nonce. + pub disable_nonce_check: bool, + /// A hard memory limit in bytes beyond which [crate::result::OutOfGasError::Memory] cannot be resized. + /// + /// In cases where the gas limit may be extraordinarily high, it is recommended to set this to + /// a sane value to prevent memory allocation panics. Defaults to `2^32 - 1` bytes per + /// EIP-1985. + #[cfg(feature = "memory_limit")] + pub memory_limit: u64, + /// Skip balance checks if true. Adds transaction cost to balance to ensure execution doesn't fail. + #[cfg(feature = "optional_balance_check")] + pub disable_balance_check: bool, + /// There are use cases where it's allowed to provide a gas limit that's higher than a block's gas limit. To that + /// end, you can disable the block gas limit validation. + /// By default, it is set to `false`. + #[cfg(feature = "optional_block_gas_limit")] + pub disable_block_gas_limit: bool, + /// EIP-3607 rejects transactions from senders with deployed code. In development, it can be desirable to simulate + /// calls from contracts, which this setting allows. + /// By default, it is set to `false`. + #[cfg(feature = "optional_eip3607")] + pub disable_eip3607: bool, + /// Disables all gas refunds. This is useful when using chains that have gas refunds disabled e.g. Avalanche. + /// Reasoning behind removing gas refunds can be found in EIP-3298. + /// By default, it is set to `false`. + #[cfg(feature = "optional_gas_refund")] + pub disable_gas_refund: bool, + /// Disables base fee checks for EIP-1559 transactions. + /// This is useful for testing method calls with zero gas price. + /// By default, it is set to `false`. + #[cfg(feature = "optional_no_base_fee")] + pub disable_base_fee: bool, +} + +impl CfgEnv { + pub fn with_chain_id(mut self, chain_id: u64) -> Self { + self.chain_id = chain_id; + self + } +} + +impl + Copy> Cfg for CfgEnv { + type Spec = SPEC; + + fn chain_id(&self) -> u64 { + self.chain_id + } + + fn spec(&self) -> Self::Spec { + self.spec + } + + fn max_code_size(&self) -> usize { + self.limit_contract_code_size.unwrap_or(MAX_CODE_SIZE) + } + + fn is_eip3607_disabled(&self) -> bool { + cfg_if::cfg_if! { + if #[cfg(feature = "optional_eip3607")] { + self.disable_eip3607 + } else { + false + } + } + } + + fn is_balance_check_disabled(&self) -> bool { + cfg_if::cfg_if! { + if #[cfg(feature = "optional_balance_check")] { + self.disable_balance_check + } else { + false + } + } + } + + fn is_gas_refund_disabled(&self) -> bool { + cfg_if::cfg_if! { + if #[cfg(feature = "optional_gas_refund")] { + self.disable_gas_refund + } else { + false + } + } + } + + fn is_block_gas_limit_disabled(&self) -> bool { + cfg_if::cfg_if! { + if #[cfg(feature = "optional_block_gas_limit")] { + self.disable_block_gas_limit + } else { + false + } + } + } + + fn is_nonce_check_disabled(&self) -> bool { + self.disable_nonce_check + } + + fn is_base_fee_check_disabled(&self) -> bool { + cfg_if::cfg_if! { + if #[cfg(feature = "optional_no_base_fee")] { + self.disable_base_fee + } else { + false + } + } + } +} + +impl Default for CfgEnv { + fn default() -> Self { + Self { + chain_id: 1, + limit_contract_code_size: None, + spec: SpecId::PRAGUE, + disable_nonce_check: false, + #[cfg(feature = "memory_limit")] + memory_limit: (1 << 32) - 1, + #[cfg(feature = "optional_balance_check")] + disable_balance_check: false, + #[cfg(feature = "optional_block_gas_limit")] + disable_block_gas_limit: false, + #[cfg(feature = "optional_eip3607")] + disable_eip3607: false, + #[cfg(feature = "optional_gas_refund")] + disable_gas_refund: false, + #[cfg(feature = "optional_no_base_fee")] + disable_base_fee: false, + } + } +} + +/// Transaction destination +pub type TransactTo = TxKind; + +/// Create scheme. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum CreateScheme { + /// Legacy create scheme of `CREATE`. + Create, + /// Create scheme of `CREATE2`. + Create2 { + /// Salt. + salt: U256, + }, +} + +#[auto_impl(&, &mut, Box, Arc)] +pub trait CfgGetter { + type Cfg: Cfg; + + fn cfg(&self) -> &Self::Cfg; +} diff --git a/crates/context/interface/src/errors.rs b/crates/context/interface/src/errors.rs new file mode 100644 index 0000000000..731f438f61 --- /dev/null +++ b/crates/context/interface/src/errors.rs @@ -0,0 +1,6 @@ +/// TODO change name of the trait +pub trait ErrorGetter { + type Error; + + fn take_error(&mut self) -> Result<(), Self::Error>; +} diff --git a/crates/context/interface/src/host.rs b/crates/context/interface/src/host.rs new file mode 100644 index 0000000000..9c79893ef1 --- /dev/null +++ b/crates/context/interface/src/host.rs @@ -0,0 +1,127 @@ +use crate::{ + journaled_state::{AccountLoad, Eip7702CodeLoad, StateLoad}, + Block, CfgEnv, Transaction, +}; +use primitives::{Address, Bytes, Log, B256, U256}; + +/// EVM context host. +/// TODO move to context-interface +pub trait Host { + /// Chain specification. + type BLOCK: Block; + type TX: Transaction; + + /// Returns a reference to the environment. + fn tx(&self) -> &Self::TX; + + /// Returns a mutable reference to the environment. + fn block(&self) -> &Self::BLOCK; + + /// TODO make it generic in future + fn cfg(&self) -> &CfgEnv; + + /// Load an account code. + fn load_account_delegated(&mut self, address: Address) -> Option; + + /// Get the block hash of the given block `number`. + fn block_hash(&mut self, number: u64) -> Option; + + /// Get balance of `address` and if the account is cold. + fn balance(&mut self, address: Address) -> Option>; + + /// Get code of `address` and if the account is cold. + fn code(&mut self, address: Address) -> Option>; + + /// Get code hash of `address` and if the account is cold. + fn code_hash(&mut self, address: Address) -> Option>; + + /// Get storage value of `address` at `index` and if the account is cold. + fn sload(&mut self, address: Address, index: U256) -> Option>; + + /// Set 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: U256, + value: U256, + ) -> Option>; + + /// Get the transient storage value of `address` at `index`. + fn tload(&mut self, address: Address, index: U256) -> U256; + + /// Set the transient storage value of `address` at `index`. + fn tstore(&mut self, address: Address, index: U256, value: U256); + + /// Emit a log owned by `address` with given `LogData`. + fn log(&mut self, log: Log); + + /// Mark `address` to be deleted, with funds transferred to `target`. + fn selfdestruct( + &mut self, + address: Address, + target: Address, + ) -> Option>; +} + +/// Represents the result of an `sstore` operation. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct SStoreResult { + /// Value of the storage when it is first read + pub original_value: U256, + /// Current value of the storage + pub present_value: U256, + /// New value that is set + pub new_value: U256, +} + +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 + } + + /// 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 + } + + /// 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 + } + + /// Returns `true` if the original value is zero. + #[inline] + pub fn is_original_zero(&self) -> bool { + self.original_value.is_zero() + } + + /// Returns `true` if the present value is zero. + #[inline] + pub fn is_present_zero(&self) -> bool { + self.present_value.is_zero() + } + + /// Returns `true` if the new value is zero. + #[inline] + pub fn is_new_zero(&self) -> bool { + self.new_value.is_zero() + } +} + +/// Result of a selfdestruct action. +/// +/// Value returned are needed to calculate the gas spent. +#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct SelfDestructResult { + pub had_value: bool, + pub target_exists: bool, + pub previously_destroyed: bool, +} diff --git a/crates/context/interface/src/journaled_state.rs b/crates/context/interface/src/journaled_state.rs new file mode 100644 index 0000000000..ce805dac76 --- /dev/null +++ b/crates/context/interface/src/journaled_state.rs @@ -0,0 +1,248 @@ +use auto_impl::auto_impl; +use core::ops::{Deref, DerefMut}; +use database_interface::Database; +use primitives::{Address, B256, U256}; +use specification::hardfork::SpecId; +use state::{Account, Bytecode}; + +pub trait JournaledState { + type Database: Database; + type FinalOutput; + + fn warm_account_and_storage( + &mut self, + address: Address, + storage_keys: impl IntoIterator, + ) -> Result<(), ::Error>; + + fn warm_account(&mut self, address: Address); + + fn set_spec_id(&mut self, spec_id: SpecId); + + fn touch_account(&mut self, address: Address); + + /// TODO instruction result is not known + fn transfer( + &mut self, + from: &Address, + to: &Address, + balance: U256, + ) -> Result, ::Error>; + + fn inc_account_nonce( + &mut self, + address: Address, + ) -> Result, ::Error>; + + fn load_account( + &mut self, + address: Address, + ) -> Result, ::Error>; + + fn load_account_code( + &mut self, + address: Address, + ) -> Result, ::Error>; + + fn load_account_delegated( + &mut self, + address: Address, + ) -> Result::Error>; + + /// Set bytecode with hash. Assume that account is warm. + fn set_code_with_hash(&mut self, address: Address, code: Bytecode, hash: B256); + + /// Assume account is warm + #[inline] + fn set_code(&mut self, address: Address, code: Bytecode) { + let hash = code.hash_slow(); + self.set_code_with_hash(address, code, hash); + } + + /// Called at the end of the transaction to clean all residue data from journal. + fn clear(&mut self); + + fn checkpoint(&mut self) -> JournalCheckpoint; + + fn checkpoint_commit(&mut self); + + fn checkpoint_revert(&mut self, checkpoint: JournalCheckpoint); + + fn create_account_checkpoint( + &mut self, + caller: Address, + address: Address, + balance: U256, + spec_id: SpecId, + ) -> Result; + + /// Does cleanup and returns modified state. + /// + /// This resets the [JournaledState] to its initial state. + fn finalize(&mut self) -> Result::Error>; +} + +/// Transfer and creation result. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum TransferError { + /// Caller does not have enough funds + OutOfFunds, + /// Overflow in target account. + OverflowPayment, + /// Create collision. + CreateCollision, +} + +/// SubRoutine checkpoint that will help us to go back from this +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct JournalCheckpoint { + pub log_i: usize, + pub journal_i: usize, +} + +/// State load information that contains the data and if the account or storage is cold loaded. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct StateLoad { + /// returned data + pub data: T, + /// True if account is cold loaded. + pub is_cold: bool, +} + +impl Deref for StateLoad { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.data + } +} + +impl DerefMut for StateLoad { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.data + } +} + +impl StateLoad { + /// Returns a new [`StateLoad`] with the given data and cold load status. + pub fn new(data: T, is_cold: bool) -> Self { + Self { data, is_cold } + } + + /// Maps the data of the [`StateLoad`] to a new value. + /// + /// Useful for transforming the data of the [`StateLoad`] without changing the cold load status. + pub fn map(self, f: F) -> StateLoad + where + F: FnOnce(T) -> B, + { + StateLoad::new(f(self.data), self.is_cold) + } +} + +/// 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 AccountLoad { + /// Is account and delegate code are loaded + pub load: Eip7702CodeLoad<()>, + /// Is account empty, if true account is not created. + pub is_empty: bool, +} + +impl Deref for AccountLoad { + type Target = Eip7702CodeLoad<()>; + + fn deref(&self) -> &Self::Target { + &self.load + } +} + +impl DerefMut for AccountLoad { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.load + } +} + +/// EIP-7702 code load result that contains optional delegation is_cold information. +/// +/// [`Self::is_delegate_account_cold`] will be [`Some`] if account has delegation. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct Eip7702CodeLoad { + /// returned data + pub state_load: StateLoad, + /// True if account has delegate code and delegated account is cold loaded. + pub is_delegate_account_cold: Option, +} + +impl Deref for Eip7702CodeLoad { + type Target = StateLoad; + + fn deref(&self) -> &Self::Target { + &self.state_load + } +} + +impl DerefMut for Eip7702CodeLoad { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.state_load + } +} + +impl Eip7702CodeLoad { + /// Returns a new [`Eip7702CodeLoad`] with the given data and without delegation. + pub fn new_state_load(state_load: StateLoad) -> Self { + Self { + state_load, + is_delegate_account_cold: None, + } + } + + /// Returns a new [`Eip7702CodeLoad`] with the given data and without delegation. + pub fn new_not_delegated(data: T, is_cold: bool) -> Self { + Self { + state_load: StateLoad::new(data, is_cold), + is_delegate_account_cold: None, + } + } + + /// Deconstructs the [`Eip7702CodeLoad`] by extracting data and + /// returning a new [`Eip7702CodeLoad`] with empty data. + pub fn into_components(self) -> (T, Eip7702CodeLoad<()>) { + let is_cold = self.is_cold; + ( + self.state_load.data, + Eip7702CodeLoad { + state_load: StateLoad::new((), is_cold), + is_delegate_account_cold: self.is_delegate_account_cold, + }, + ) + } + + /// Sets the delegation cold load status. + pub fn set_delegate_load(&mut self, is_delegate_account_cold: bool) { + self.is_delegate_account_cold = Some(is_delegate_account_cold); + } + + /// Returns a new [`Eip7702CodeLoad`] with the given data and delegation cold load status. + pub fn new(state_load: StateLoad, is_delegate_account_cold: bool) -> Self { + Self { + state_load, + is_delegate_account_cold: Some(is_delegate_account_cold), + } + } +} + +/// Helper that extracts database error from [`JournalStateGetter`]. +pub type JournalStateGetterDBError = + <<::Journal as JournaledState>::Database as Database>::Error; + +#[auto_impl(&mut, Box)] +pub trait JournalStateGetter { + type Journal: JournaledState; + + fn journal(&mut self) -> &mut Self::Journal; +} diff --git a/crates/context/interface/src/lib.rs b/crates/context/interface/src/lib.rs new file mode 100644 index 0000000000..8d1090fd9c --- /dev/null +++ b/crates/context/interface/src/lib.rs @@ -0,0 +1,20 @@ +//! Optimism-specific constants, types, and helpers. +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(not(feature = "std"))] +extern crate alloc as std; + +pub mod block; +pub mod cfg; +pub mod errors; +pub mod journaled_state; +pub mod result; +pub mod transaction; + +pub use block::{Block, BlockGetter}; +pub use cfg::{Cfg, CfgEnv, CfgGetter, CreateScheme, TransactTo}; +pub use database_interface::{DBErrorMarker, Database, DatabaseGetter}; +pub use errors::ErrorGetter; +pub use journaled_state::{JournalStateGetter, JournalStateGetterDBError, JournaledState}; +pub use transaction::{Transaction, TransactionGetter, TransactionType}; diff --git a/crates/wiring/src/result.rs b/crates/context/interface/src/result.rs similarity index 93% rename from crates/wiring/src/result.rs rename to crates/context/interface/src/result.rs index 0897add59d..e3569cbfd6 100644 --- a/crates/wiring/src/result.rs +++ b/crates/context/interface/src/result.rs @@ -1,24 +1,17 @@ -use crate::{evm_wiring::HaltReasonTrait, EvmWiring}; +use crate::transaction::TransactionError; use core::fmt::{self, Debug}; -use database_interface::Database; +use database_interface::DBErrorMarker; use primitives::{Address, Bytes, Log, U256}; use specification::eip7702::InvalidAuthorization; use state::EvmState; use std::{boxed::Box, string::String, vec::Vec}; -use transaction::{Transaction, TransactionError}; -/// Result of EVM execution. -pub type EVMResult = - EVMResultGeneric::HaltReason>, EvmWiringT>; +pub trait HaltReasonTrait: Clone + Debug + PartialEq + Eq + From {} -/// Generic result of EVM execution. Used to represent error and generic output. -pub type EVMResultGeneric = core::result::Result>; - -/// EVM error type for a specific chain. -pub type EVMErrorForChain = EVMError< - <::Database as Database>::Error, - <::Transaction as Transaction>::TransactionError, ->; +impl HaltReasonTrait for HaltReasonT where + HaltReasonT: Clone + Debug + PartialEq + Eq + From +{ +} #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -89,7 +82,7 @@ impl ExecutionResult { /// Returns the logs if execution is successful, or an empty list otherwise. pub fn logs(&self) -> &[Log] { match self { - Self::Success { logs, .. } => logs, + Self::Success { logs, .. } => logs.as_slice(), _ => &[], } } @@ -146,11 +139,6 @@ impl Output { } } -pub type EVMErrorWiring = EVMError< - <::Database as Database>::Error, - <::Transaction as Transaction>::TransactionError, ->; - /// Main EVM error. #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -169,6 +157,28 @@ pub enum EVMError { Precompile(String), } +impl From for EVMError { + fn from(value: DBError) -> Self { + Self::Database(value) + } +} + +pub trait FromStringError { + fn from_string(value: String) -> Self; +} + +impl FromStringError for EVMError { + fn from_string(value: String) -> Self { + Self::Custom(value) + } +} + +impl> From for EVMError { + fn from(value: InvalidTransaction) -> Self { + Self::Transaction(value.into()) + } +} + impl EVMError { /// Maps a `DBError` to a new error type using the provided closure, leaving other variants unchanged. pub fn map_db_err(self, op: F) -> EVMError @@ -217,12 +227,6 @@ where } } -impl From for EVMError { - fn from(value: InvalidTransaction) -> Self { - Self::Transaction(value) - } -} - impl From for EVMError { @@ -392,7 +396,7 @@ impl fmt::Display for InvalidTransaction { } } -/// Errors related to misconfiguration of a [`crate::default::block::BlockEnv`]. +/// Errors related to misconfiguration of a [`crate::Block`]. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum InvalidHeader { @@ -458,7 +462,7 @@ pub enum HaltReason { /// Aud data is smaller then already present data size. EofAuxDataTooSmall, /// EOF Subroutine stack overflow - EOFFunctionStackOverflow, + SubRoutineStackOverflow, /// Check for target address validity is only done inside subcall. InvalidEXTCALLTarget, } diff --git a/crates/wiring/transaction/src/transaction.rs b/crates/context/interface/src/transaction.rs similarity index 87% rename from crates/wiring/transaction/src/transaction.rs rename to crates/context/interface/src/transaction.rs index 8f241b6e7d..2f44399e9b 100644 --- a/crates/wiring/transaction/src/transaction.rs +++ b/crates/context/interface/src/transaction.rs @@ -1,7 +1,22 @@ -use crate::{ - eip1559::Eip1559CommonTxFields, AccessListTrait, CommonTxFields, Eip1559Tx, Eip2930Tx, - Eip4844Tx, Eip7702Tx, LegacyTx, TransactionType, -}; +mod access_list; +mod common; +pub mod eip1559; +pub mod eip2930; +pub mod eip4844; +pub mod eip7702; +pub mod legacy; +pub mod transaction_type; + +pub use access_list::AccessListTrait; +pub use common::CommonTxFields; +pub use eip1559::{Eip1559CommonTxFields, Eip1559Tx}; +pub use eip2930::Eip2930Tx; +pub use eip4844::Eip4844Tx; +pub use eip7702::Eip7702Tx; +pub use legacy::LegacyTx; +pub use transaction_type::TransactionType; + +use auto_impl::auto_impl; use core::cmp::min; use core::fmt::Debug; use primitives::{TxKind, U256}; @@ -15,6 +30,7 @@ pub trait TransactionError: Debug + core::error::Error {} /// /// It can be extended to support new transaction types and only transaction types can be /// deprecated by not returning tx_type. +#[auto_impl(&, Box, Arc, Rc)] pub trait Transaction { /// An error that occurs when validating a transaction. type TransactionError: TransactionError; @@ -133,3 +149,14 @@ pub trait Transaction { } } } + +#[auto_impl(&, &mut, Box, Arc)] +pub trait TransactionGetter { + type Transaction: Transaction; + + fn tx(&self) -> &Self::Transaction; +} + +pub trait TransactionSetter: TransactionGetter { + fn set_tx(&mut self, tx: ::Transaction); +} diff --git a/crates/wiring/transaction/src/access_list.rs b/crates/context/interface/src/transaction/access_list.rs similarity index 92% rename from crates/wiring/transaction/src/access_list.rs rename to crates/context/interface/src/transaction/access_list.rs index 5a83884c02..578f355cb4 100644 --- a/crates/wiring/transaction/src/access_list.rs +++ b/crates/context/interface/src/transaction/access_list.rs @@ -1,3 +1,4 @@ +use auto_impl::auto_impl; use primitives::{Address, B256}; /// Access list type is introduced in EIP-2930, and every @@ -9,7 +10,8 @@ use primitives::{Address, B256}; /// are warm loaded before transaction execution. /// /// Number of account and storage slots is used to calculate initial tx gas cost. -pub trait AccessListTrait { +#[auto_impl(&, Box, Arc, Rc)] +pub trait AccessListTrait: Clone { /// Iterate over access list. fn iter(&self) -> impl Iterator)>; diff --git a/crates/wiring/transaction/src/common.rs b/crates/context/interface/src/transaction/common.rs similarity index 91% rename from crates/wiring/transaction/src/common.rs rename to crates/context/interface/src/transaction/common.rs index 06fb0a2fe8..7bce4ece0d 100644 --- a/crates/wiring/transaction/src/common.rs +++ b/crates/context/interface/src/transaction/common.rs @@ -1,7 +1,9 @@ +use auto_impl::auto_impl; use primitives::{Address, Bytes, U256}; /// Trait that contains all common field that are shared by all transactions. /// This trait is base for Legacy, EIp2930 and Eip1559 transactions. +#[auto_impl(&, Box, Arc, Rc)] pub trait CommonTxFields { /// Caller aka Author aka transaction signer. fn caller(&self) -> Address; diff --git a/crates/wiring/transaction/src/eip1559.rs b/crates/context/interface/src/transaction/eip1559.rs similarity index 82% rename from crates/wiring/transaction/src/eip1559.rs rename to crates/context/interface/src/transaction/eip1559.rs index b797c520cc..00a4b233d3 100644 --- a/crates/wiring/transaction/src/eip1559.rs +++ b/crates/context/interface/src/transaction/eip1559.rs @@ -1,11 +1,14 @@ -use crate::{AccessListTrait, CommonTxFields}; +use super::{AccessListTrait, CommonTxFields}; +use auto_impl::auto_impl; use primitives::TxKind; +#[auto_impl(&, Box, Arc, Rc)] pub trait Eip1559Tx: Eip1559CommonTxFields { fn kind(&self) -> TxKind; } /// This trait is base for Eip1559, EIp4844 and Eip7702 transactions. +#[auto_impl(&, Box, Arc, Rc)] pub trait Eip1559CommonTxFields: CommonTxFields { /// Access list type. type AccessList: AccessListTrait; diff --git a/crates/wiring/transaction/src/eip2930.rs b/crates/context/interface/src/transaction/eip2930.rs similarity index 82% rename from crates/wiring/transaction/src/eip2930.rs rename to crates/context/interface/src/transaction/eip2930.rs index 1513eefdfc..ab222a0537 100644 --- a/crates/wiring/transaction/src/eip2930.rs +++ b/crates/context/interface/src/transaction/eip2930.rs @@ -1,7 +1,9 @@ -use crate::{AccessListTrait, CommonTxFields}; +use super::{AccessListTrait, CommonTxFields}; +use auto_impl::auto_impl; use primitives::TxKind; /// EIP-2930: Optional access lists +#[auto_impl(&, Box, Arc, Rc)] pub trait Eip2930Tx: CommonTxFields { type AccessList: AccessListTrait; diff --git a/crates/wiring/transaction/src/eip4844.rs b/crates/context/interface/src/transaction/eip4844.rs similarity index 92% rename from crates/wiring/transaction/src/eip4844.rs rename to crates/context/interface/src/transaction/eip4844.rs index 73b5037d93..ea91511505 100644 --- a/crates/wiring/transaction/src/eip4844.rs +++ b/crates/context/interface/src/transaction/eip4844.rs @@ -1,7 +1,9 @@ -use crate::eip1559::Eip1559CommonTxFields; +use super::eip1559::Eip1559CommonTxFields; +use auto_impl::auto_impl; use primitives::{Address, B256, U256}; use specification::eip4844::GAS_PER_BLOB; +#[auto_impl(&, Box, Arc, Rc)] pub trait Eip4844Tx: Eip1559CommonTxFields { /// Call destination fn destination(&self) -> Address; diff --git a/crates/wiring/transaction/src/eip7702.rs b/crates/context/interface/src/transaction/eip7702.rs similarity index 91% rename from crates/wiring/transaction/src/eip7702.rs rename to crates/context/interface/src/transaction/eip7702.rs index 8e67bd909a..0e50632975 100644 --- a/crates/wiring/transaction/src/eip7702.rs +++ b/crates/context/interface/src/transaction/eip7702.rs @@ -1,8 +1,9 @@ -use crate::Eip1559Tx; +use super::Eip1559Tx; use auto_impl::auto_impl; -use primitives::{Address, U256}; +use primitives::Address; /// EIP-7702 transaction, TODO set Trait for AuthorizationList. +#[auto_impl(&, Box, Arc, Rc)] pub trait Eip7702Tx: Eip1559Tx { /// Destination address of the call. fn destination(&self) -> Address; @@ -25,7 +26,7 @@ pub trait Eip7702Tx: Eip1559Tx { /// Authorization trait. #[auto_impl(&, Arc)] -pub trait Authorization { +pub trait Authorization: Clone { /// Authority address. /// /// # Note @@ -38,7 +39,7 @@ pub trait Authorization { fn authority(&self) -> Option
; /// Returns authorization the chain id. - fn chain_id(&self) -> U256; + fn chain_id(&self) -> u64; /// Returns the nonce. /// @@ -68,8 +69,9 @@ impl Authorization for RecoveredAuthorization { } /// Returns authorization the chain id. - fn chain_id(&self) -> U256 { - self.inner().chain_id() + fn chain_id(&self) -> u64 { + // TODO chain_id is set as u64 in newest EIP-7702 spec + self.inner().chain_id().try_into().unwrap() } /// Returns the nonce. diff --git a/crates/wiring/transaction/src/legacy.rs b/crates/context/interface/src/transaction/legacy.rs similarity index 82% rename from crates/wiring/transaction/src/legacy.rs rename to crates/context/interface/src/transaction/legacy.rs index 3506141b1c..8a1c1fd0b0 100644 --- a/crates/wiring/transaction/src/legacy.rs +++ b/crates/context/interface/src/transaction/legacy.rs @@ -1,7 +1,9 @@ -use crate::CommonTxFields; +use super::CommonTxFields; +use auto_impl::auto_impl; use primitives::TxKind; /// Legacy transaction trait before introduction of EIP-2929 +#[auto_impl(&, Box, Arc, Rc)] pub trait LegacyTx: CommonTxFields { /// Transaction kind. fn kind(&self) -> TxKind; diff --git a/crates/wiring/transaction/src/transaction_type.rs b/crates/context/interface/src/transaction/transaction_type.rs similarity index 100% rename from crates/wiring/transaction/src/transaction_type.rs rename to crates/context/interface/src/transaction/transaction_type.rs diff --git a/crates/wiring/src/default/block.rs b/crates/context/src/block.rs similarity index 87% rename from crates/wiring/src/default/block.rs rename to crates/context/src/block.rs index 6c8765dd6c..dadf6873f6 100644 --- a/crates/wiring/src/default/block.rs +++ b/crates/context/src/block.rs @@ -1,4 +1,4 @@ -use crate::block::{BlobExcessGasAndPrice, Block}; +use context_interface::block::{BlobExcessGasAndPrice, Block}; use primitives::{Address, B256, U256}; /// The block environment. @@ -7,10 +7,10 @@ use primitives::{Address, B256, U256}; pub struct BlockEnv { /// The number of ancestor blocks of this block (block height). pub number: U256, - /// Coinbase or miner or address that created and signed the block. + /// Beneficiary (Coinbase or miner) is a address that have signed the block. /// /// This is the receiver address of all the gas spent in the block. - pub coinbase: Address, + pub beneficiary: Address, /// The timestamp of the block in seconds since the UNIX epoch. pub timestamp: U256, @@ -33,8 +33,8 @@ pub struct BlockEnv { /// [EIP-4399]: https://eips.ethereum.org/EIPS/eip-4399 pub prevrandao: Option, /// Excess blob gas and blob gasprice. - /// See also [`crate::block::calc_excess_blob_gas`] - /// and [`crate::block::blob::calc_blob_gasprice`]. + /// See also [`context_interface::block::calc_excess_blob_gas`] + /// and [`context_interface::block::blob::calc_blob_gasprice`]. /// /// Incorporated as part of the Cancun upgrade via [EIP-4844]. /// @@ -57,8 +57,8 @@ impl Block for BlockEnv { } #[inline] - fn coinbase(&self) -> &Address { - &self.coinbase + fn beneficiary(&self) -> &Address { + &self.beneficiary } #[inline] @@ -96,7 +96,7 @@ impl Default for BlockEnv { fn default() -> Self { Self { number: U256::ZERO, - coinbase: Address::ZERO, + beneficiary: Address::ZERO, timestamp: U256::from(1), gas_limit: U256::MAX, basefee: U256::ZERO, diff --git a/crates/context/src/cfg.rs b/crates/context/src/cfg.rs new file mode 100644 index 0000000000..a4a82a1e92 --- /dev/null +++ b/crates/context/src/cfg.rs @@ -0,0 +1 @@ +pub use context_interface::{Cfg, CfgEnv}; diff --git a/crates/context/src/context.rs b/crates/context/src/context.rs new file mode 100644 index 0000000000..59d1801d07 --- /dev/null +++ b/crates/context/src/context.rs @@ -0,0 +1,513 @@ +use crate::{block::BlockEnv, journaled_state::JournaledState as JournaledStateImpl, tx::TxEnv}; +use bytecode::{Bytecode, EOF_MAGIC_BYTES, EOF_MAGIC_HASH}; +use context_interface::{ + block::BlockSetter, + journaled_state::{AccountLoad, Eip7702CodeLoad}, + result::EVMError, + transaction::TransactionSetter, + Block, BlockGetter, Cfg, CfgEnv, CfgGetter, DatabaseGetter, ErrorGetter, JournalStateGetter, + Transaction, TransactionGetter, +}; +use database_interface::{Database, EmptyDB}; +use derive_where::derive_where; +use interpreter::{as_u64_saturated, Host, SStoreResult, SelfDestructResult, StateLoad}; +use primitives::{Address, Bytes, Log, B256, BLOCK_HASH_HISTORY, U256}; +use specification::hardfork::SpecId; + +/// EVM context contains data that EVM needs for execution. +#[derive_where(Clone, Debug; BLOCK, CFG, CHAIN, TX, DB, ::Error)] +pub struct Context { + /// Transaction information. + pub tx: TX, + /// Block information. + pub block: BLOCK, + /// Configurations. + pub cfg: CFG, + /// EVM State with journaling support and database. + pub journaled_state: JournaledStateImpl, + /// Inner context. + pub chain: CHAIN, + /// Error that happened during execution. + pub error: Result<(), ::Error>, +} + +impl Default for Context { + fn default() -> Self { + Self::new(EmptyDB::new(), SpecId::LATEST) + } +} + +impl Context { + pub fn builder() -> Self { + Self::new(EmptyDB::new(), SpecId::LATEST) + } +} + +impl + Context +{ + pub fn new(db: DB, spec: SpecId) -> Self { + let mut cfg = CfgEnv::default(); + cfg.spec = spec; + Self { + tx: TX::default(), + block: BLOCK::default(), + cfg, + journaled_state: JournaledStateImpl::new(SpecId::LATEST, db), + chain: Default::default(), + error: Ok(()), + } + } +} +impl Context +where + BLOCK: Block, + TX: Transaction, + CFG: Cfg, + DB: Database, +{ + /// Return account code bytes and if address is cold loaded. + /// + /// In case of EOF account it will return `EOF_MAGIC` (0xEF00) as code. + /// + /// TODO move this in Journaled state + #[inline] + pub fn code( + &mut self, + address: Address, + ) -> Result, ::Error> { + let a = self.journaled_state.load_code(address)?; + // SAFETY: safe to unwrap as load_code will insert code if it is empty. + let code = a.info.code.as_ref().unwrap(); + if code.is_eof() { + return Ok(Eip7702CodeLoad::new_not_delegated( + EOF_MAGIC_BYTES.clone(), + a.is_cold, + )); + } + + if let Bytecode::Eip7702(code) = code { + let address = code.address(); + let is_cold = a.is_cold; + + let delegated_account = self.journaled_state.load_code(address)?; + + // SAFETY: safe to unwrap as load_code will insert code if it is empty. + let delegated_code = delegated_account.info.code.as_ref().unwrap(); + + let bytes = if delegated_code.is_eof() { + EOF_MAGIC_BYTES.clone() + } else { + delegated_code.original_bytes() + }; + + return Ok(Eip7702CodeLoad::new( + StateLoad::new(bytes, is_cold), + delegated_account.is_cold, + )); + } + + Ok(Eip7702CodeLoad::new_not_delegated( + code.original_bytes(), + a.is_cold, + )) + } + + /// Create a new context with a new database type. + pub fn with_db(self, db: ODB) -> Context { + let spec = self.cfg.spec().into(); + Context { + tx: self.tx, + block: self.block, + cfg: self.cfg, + journaled_state: JournaledStateImpl::new(spec, db), + chain: self.chain, + error: Ok(()), + } + } + + /// Create a new context with a new block type. + pub fn with_block(self, block: OB) -> Context { + Context { + tx: self.tx, + block, + cfg: self.cfg, + journaled_state: self.journaled_state, + chain: self.chain, + error: Ok(()), + } + } + + /// Create a new context with a new transaction type. + pub fn with_tx(self, tx: OTX) -> Context { + Context { + tx, + block: self.block, + cfg: self.cfg, + journaled_state: self.journaled_state, + chain: self.chain, + error: Ok(()), + } + } + + /// Create a new context with a new chain type. + pub fn with_chain(self, chain: OC) -> Context { + Context { + tx: self.tx, + block: self.block, + cfg: self.cfg, + journaled_state: self.journaled_state, + chain, + error: Ok(()), + } + } + + /// Create a new context with a new chain type. + pub fn with_cfg(mut self, cfg: OCFG) -> Context { + self.journaled_state.set_spec_id(cfg.spec().into()); + Context { + tx: self.tx, + block: self.block, + cfg, + journaled_state: self.journaled_state, + chain: self.chain, + error: Ok(()), + } + } + + /// Modify the context configuration. + #[must_use] + pub fn modify_cfg_chained(mut self, f: F) -> Self + where + F: FnOnce(&mut CFG), + { + f(&mut self.cfg); + self.journaled_state.set_spec_id(self.cfg.spec().into()); + self + } + + /// Modify the context block. + #[must_use] + pub fn modify_block_chained(mut self, f: F) -> Self + where + F: FnOnce(&mut BLOCK), + { + self.modify_block(f); + self + } + + /// Modify the context transaction. + #[must_use] + pub fn modify_tx_chained(mut self, f: F) -> Self + where + F: FnOnce(&mut TX), + { + self.modify_tx(f); + self + } + + /// Modify the context chain. + #[must_use] + pub fn modify_chain_chained(mut self, f: F) -> Self + where + F: FnOnce(&mut CHAIN), + { + self.modify_chain(f); + self + } + + /// Modify the context database. + #[must_use] + pub fn modify_db_chained(mut self, f: F) -> Self + where + F: FnOnce(&mut DB), + { + self.modify_db(f); + self + } + + /// Modify the context journal. + #[must_use] + pub fn modify_journal_chained(mut self, f: F) -> Self + where + F: FnOnce(&mut JournaledStateImpl), + { + self.modify_journal(f); + self + } + + /// Modify the context block. + pub fn modify_block(&mut self, f: F) + where + F: FnOnce(&mut BLOCK), + { + f(&mut self.block); + } + + pub fn modify_tx(&mut self, f: F) + where + F: FnOnce(&mut TX), + { + f(&mut self.tx); + } + + pub fn modify_cfg(&mut self, f: F) + where + F: FnOnce(&mut CFG), + { + f(&mut self.cfg); + self.journaled_state.set_spec_id(self.cfg.spec().into()); + } + + pub fn modify_chain(&mut self, f: F) + where + F: FnOnce(&mut CHAIN), + { + f(&mut self.chain); + } + + pub fn modify_db(&mut self, f: F) + where + F: FnOnce(&mut DB), + { + f(&mut self.journaled_state.database); + } + + pub fn modify_journal(&mut self, f: F) + where + F: FnOnce(&mut JournaledStateImpl), + { + f(&mut self.journaled_state); + } + + /// Get code hash of address. + /// + /// In case of EOF account it will return `EOF_MAGIC_HASH` + /// (the hash of `0xEF00`). + /// + /// TODO move this in Journaled state + #[inline] + pub fn code_hash( + &mut self, + address: Address, + ) -> Result, ::Error> { + let acc = self.journaled_state.load_code(address)?; + if acc.is_empty() { + return Ok(Eip7702CodeLoad::new_not_delegated(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(); + + // If bytecode is EIP-7702 then we need to load the delegated account. + if let Bytecode::Eip7702(code) = code { + let address = code.address(); + let is_cold = acc.is_cold; + + let delegated_account = self.journaled_state.load_code(address)?; + + let hash = if delegated_account.is_empty() { + B256::ZERO + } else if delegated_account.info.code.as_ref().unwrap().is_eof() { + EOF_MAGIC_HASH + } else { + delegated_account.info.code_hash + }; + + return Ok(Eip7702CodeLoad::new( + StateLoad::new(hash, is_cold), + delegated_account.is_cold, + )); + } + + let hash = if code.is_eof() { + EOF_MAGIC_HASH + } else { + acc.info.code_hash + }; + + Ok(Eip7702CodeLoad::new_not_delegated(hash, acc.is_cold)) + } +} + +impl Host + for Context +{ + type BLOCK = BLOCK; + type TX = TX; + type CFG = CFG; + + fn tx(&self) -> &Self::TX { + &self.tx + } + + fn block(&self) -> &Self::BLOCK { + &self.block + } + + fn cfg(&self) -> &Self::CFG { + &self.cfg + } + + fn block_hash(&mut self, requested_number: u64) -> Option { + let block_number = as_u64_saturated!(*self.block().number()); + + let Some(diff) = block_number.checked_sub(requested_number) else { + return Some(B256::ZERO); + }; + + // blockhash should push zero if number is same as current block number. + if diff == 0 { + return Some(B256::ZERO); + } + + if diff <= BLOCK_HASH_HISTORY { + return self + .journaled_state + .database + .block_hash(requested_number) + .map_err(|e| self.error = Err(e)) + .ok(); + } + + Some(B256::ZERO) + } + + fn load_account_delegated(&mut self, address: Address) -> Option { + self.journaled_state + .load_account_delegated(address) + .map_err(|e| self.error = Err(e)) + .ok() + } + + fn balance(&mut self, address: Address) -> Option> { + self.journaled_state + .load_account(address) + .map_err(|e| self.error = Err(e)) + .map(|acc| acc.map(|a| a.info.balance)) + .ok() + } + + fn code(&mut self, address: Address) -> Option> { + self.code(address).map_err(|e| self.error = Err(e)).ok() + } + + fn code_hash(&mut self, address: Address) -> Option> { + self.code_hash(address) + .map_err(|e| self.error = Err(e)) + .ok() + } + + fn sload(&mut self, address: Address, index: U256) -> Option> { + self.journaled_state + .sload(address, index) + .map_err(|e| self.error = Err(e)) + .ok() + } + + fn sstore( + &mut self, + address: Address, + index: U256, + value: U256, + ) -> Option> { + self.journaled_state + .sstore(address, index, value) + .map_err(|e| self.error = Err(e)) + .ok() + } + + fn tload(&mut self, address: Address, index: U256) -> U256 { + self.journaled_state.tload(address, index) + } + + fn tstore(&mut self, address: Address, index: U256, value: U256) { + self.journaled_state.tstore(address, index, value) + } + + fn log(&mut self, log: Log) { + self.journaled_state.log(log); + } + + fn selfdestruct( + &mut self, + address: Address, + target: Address, + ) -> Option> { + self.journaled_state + .selfdestruct(address, target) + .map_err(|e| self.error = Err(e)) + .ok() + } +} + +impl CfgGetter for Context { + type Cfg = CFG; + + fn cfg(&self) -> &Self::Cfg { + &self.cfg + } +} + +impl JournalStateGetter + for Context +{ + type Journal = JournaledStateImpl; + + fn journal(&mut self) -> &mut Self::Journal { + &mut self.journaled_state + } +} + +impl DatabaseGetter for Context { + type Database = DB; + + fn db(&mut self) -> &mut Self::Database { + &mut self.journaled_state.database + } +} + +impl ErrorGetter + for Context +{ + type Error = EVMError; + + fn take_error(&mut self) -> Result<(), Self::Error> { + core::mem::replace(&mut self.error, Ok(())).map_err(EVMError::Database) + } +} + +impl TransactionGetter + for Context +{ + type Transaction = TX; + + fn tx(&self) -> &Self::Transaction { + &self.tx + } +} + +impl TransactionSetter + for Context +{ + fn set_tx(&mut self, tx: ::Transaction) { + self.tx = tx; + } +} + +impl BlockGetter + for Context +{ + type Block = BLOCK; + + fn block(&self) -> &Self::Block { + &self.block + } +} + +impl BlockSetter + for Context +{ + fn set_block(&mut self, block: ::Block) { + self.block = block; + } +} diff --git a/crates/context/src/default.rs b/crates/context/src/default.rs new file mode 100644 index 0000000000..79e4dd0892 --- /dev/null +++ b/crates/context/src/default.rs @@ -0,0 +1,3 @@ +pub mod block; +pub mod cfg; +pub mod tx; diff --git a/crates/revm/src/journaled_state.rs b/crates/context/src/journaled_state.rs similarity index 81% rename from crates/revm/src/journaled_state.rs rename to crates/context/src/journaled_state.rs index fb5ccff8ec..667aabd96f 100644 --- a/crates/revm/src/journaled_state.rs +++ b/crates/context/src/journaled_state.rs @@ -1,8 +1,10 @@ use bytecode::Bytecode; -use database_interface::Database; -use interpreter::{ - AccountLoad, Eip7702CodeLoad, InstructionResult, SStoreResult, SelfDestructResult, StateLoad, +use context_interface::journaled_state::{ + AccountLoad, Eip7702CodeLoad, JournalCheckpoint, JournaledState as JournaledStateTrait, + TransferError, }; +use database_interface::Database; +use interpreter::{SStoreResult, SelfDestructResult, StateLoad}; use primitives::{ hash_map::Entry, Address, HashMap, HashSet, Log, B256, KECCAK_EMPTY, PRECOMPILE3, U256, }; @@ -10,14 +12,16 @@ use specification::hardfork::{SpecId, SpecId::*}; use state::{Account, EvmState, EvmStorageSlot, TransientStorage}; use core::mem; -use std::vec::Vec; +use std::{vec, vec::Vec}; /// A journal of state changes internal to the EVM. /// /// On each additional call, the depth of the journaled state is increased (`depth`) and a new journal is added. The journal contains every state change that happens within that call, making it possible to revert changes made in a specific call. #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct JournaledState { +pub struct JournaledState { + /// Database + pub database: DB, /// The current state. pub state: EvmState, /// Transient storage that is discarded after every transaction. @@ -49,7 +53,120 @@ pub struct JournaledState { pub warm_preloaded_addresses: HashSet
, } -impl JournaledState { +impl JournaledStateTrait for JournaledState { + type Database = DB; + // TODO make a struck here. + type FinalOutput = (EvmState, Vec); + + fn warm_account(&mut self, address: Address) { + self.warm_preloaded_addresses.insert(address); + } + + fn warm_account_and_storage( + &mut self, + address: Address, + storage_keys: impl IntoIterator, + ) -> Result<(), ::Error> { + self.initial_account_load(address, storage_keys)?; + Ok(()) + } + + fn set_spec_id(&mut self, spec_id: SpecId) { + self.spec = spec_id; + } + + fn transfer( + &mut self, + from: &Address, + to: &Address, + balance: U256, + ) -> Result, DB::Error> { + // TODO handle instruction result + self.transfer(from, to, balance) + } + + fn touch_account(&mut self, address: Address) { + self.touch(&address); + } + + fn inc_account_nonce(&mut self, address: Address) -> Result, DB::Error> { + Ok(self.inc_nonce(address)) + } + + fn load_account(&mut self, address: Address) -> Result, DB::Error> { + self.load_account(address) + } + + fn load_account_code( + &mut self, + address: Address, + ) -> Result, DB::Error> { + self.load_code(address) + } + + fn load_account_delegated(&mut self, address: Address) -> Result { + self.load_account_delegated(address) + } + + fn checkpoint(&mut self) -> JournalCheckpoint { + self.checkpoint() + } + + fn checkpoint_commit(&mut self) { + self.checkpoint_commit() + } + + fn checkpoint_revert(&mut self, checkpoint: JournalCheckpoint) { + self.checkpoint_revert(checkpoint) + } + + fn set_code_with_hash(&mut self, address: Address, code: Bytecode, hash: B256) { + self.set_code_with_hash(address, code, hash); + } + + fn clear(&mut self) { + // Clears the JournaledState. Preserving only the spec. + //let spec = self.spec; + // TODO WIRING Clear it up + //let db = self.database; + //*self = Self::new(spec, db, HashSet::default()); + } + + fn create_account_checkpoint( + &mut self, + caller: Address, + address: Address, + balance: U256, + spec_id: SpecId, + ) -> Result { + // ignore error. + self.create_account_checkpoint(caller, address, balance, spec_id) + } + + fn finalize(&mut self) -> Result::Error> { + let Self { + state, + transient_storage, + logs, + depth, + journal, + // kept, see [Self::new] + spec: _, + database: _, + warm_preloaded_addresses: _, + } = self; + + *transient_storage = TransientStorage::default(); + *journal = vec![vec![]]; + *depth = 0; + let state = mem::take(state); + let logs = mem::take(logs); + + Ok((state, logs)) + } +} + +impl JournaledState { /// Create new JournaledState. /// /// warm_preloaded_addresses is used to determine if address is considered warm loaded. @@ -59,15 +176,16 @@ impl JournaledState { /// /// This function will journal state after Spurious Dragon fork. /// And will not take into account if account is not existing or empty. - pub fn new(spec: SpecId, warm_preloaded_addresses: HashSet
) -> JournaledState { + pub fn new(spec: SpecId, database: DB) -> JournaledState { Self { + database, state: HashMap::default(), transient_storage: TransientStorage::default(), logs: Vec::new(), journal: vec![vec![]], depth: 0, spec, - warm_preloaded_addresses, + warm_preloaded_addresses: HashSet::default(), } } @@ -102,37 +220,6 @@ impl JournaledState { } } - /// Clears the JournaledState. Preserving only the spec. - pub fn clear(&mut self) { - let spec = self.spec; - *self = Self::new(spec, HashSet::default()); - } - - /// Does cleanup and returns modified state. - /// - /// This resets the [JournaledState] to its initial state in [Self::new] - #[inline] - pub fn finalize(&mut self) -> (EvmState, Vec) { - let Self { - state, - transient_storage, - logs, - depth, - journal, - // kept, see [Self::new] - spec: _, - warm_preloaded_addresses: _, - } = self; - - *transient_storage = TransientStorage::default(); - *journal = vec![vec![]]; - *depth = 0; - let state = mem::take(state); - let logs = mem::take(logs); - - (state, logs) - } - /// Returns the _loaded_ [Account] for the given address. /// /// This assumes that the account has already been loaded. @@ -198,16 +285,22 @@ impl JournaledState { /// Transfers balance from two accounts. Returns error if sender balance is not enough. #[inline] - pub fn transfer( + pub fn transfer( &mut self, from: &Address, to: &Address, balance: U256, - db: &mut DB, - ) -> Result, DB::Error> { + ) -> Result, DB::Error> { + if balance.is_zero() { + self.load_account(*to)?; + let _ = self.load_account(*to)?; + let to_account = self.state.get_mut(to).unwrap(); + Self::touch_account(self.journal.last_mut().unwrap(), to, to_account); + return Ok(None); + } // load accounts - self.load_account(*from, db)?; - self.load_account(*to, db)?; + self.load_account(*from)?; + self.load_account(*to)?; // sub balance from let from_account = &mut self.state.get_mut(from).unwrap(); @@ -215,7 +308,7 @@ impl JournaledState { let from_balance = &mut from_account.info.balance; let Some(from_balance_incr) = from_balance.checked_sub(balance) else { - return Ok(Some(InstructionResult::OutOfFunds)); + return Ok(Some(TransferError::OutOfFunds)); }; *from_balance = from_balance_incr; @@ -224,7 +317,7 @@ impl JournaledState { Self::touch_account(self.journal.last_mut().unwrap(), to, to_account); let to_balance = &mut to_account.info.balance; let Some(to_balance_decr) = to_balance.checked_add(balance) else { - return Ok(Some(InstructionResult::OverflowPayment)); + return Ok(Some(TransferError::OverflowPayment)); }; *to_balance = to_balance_decr; // Overflow of U256 balance is not possible to happen on mainnet. We don't bother to return funds from from_acc. @@ -260,59 +353,66 @@ impl JournaledState { pub fn create_account_checkpoint( &mut self, caller: Address, - address: Address, + target_address: Address, balance: U256, spec_id: SpecId, - ) -> Result { + ) -> Result { // Enter subroutine let checkpoint = self.checkpoint(); + // Fetch balance of caller. + let caller_acc = self.state.get_mut(&caller).unwrap(); + // Check if caller has enough balance to send to the created contract. + if caller_acc.info.balance < balance { + self.checkpoint_revert(checkpoint); + return Err(TransferError::OutOfFunds); + } + // Newly created account is present, as we just loaded it. - let account = self.state.get_mut(&address).unwrap(); + let target_acc = self.state.get_mut(&target_address).unwrap(); let last_journal = self.journal.last_mut().unwrap(); // New account can be created if: // Bytecode is not empty. // Nonce is not zero // Account is not precompile. - if account.info.code_hash != KECCAK_EMPTY || account.info.nonce != 0 { + if target_acc.info.code_hash != KECCAK_EMPTY || target_acc.info.nonce != 0 { self.checkpoint_revert(checkpoint); - return Err(InstructionResult::CreateCollision); + return Err(TransferError::CreateCollision); } // set account status to created. - account.mark_created(); + target_acc.mark_created(); // this entry will revert set nonce. - last_journal.push(JournalEntry::AccountCreated { address }); - account.info.code = None; + last_journal.push(JournalEntry::AccountCreated { + address: target_address, + }); + target_acc.info.code = None; + // EIP-161: State trie clearing (invariant-preserving alternative) + if spec_id.is_enabled_in(SPURIOUS_DRAGON) { + // nonce is going to be reset to zero in AccountCreated journal entry. + target_acc.info.nonce = 1; + } // touch account. This is important as for pre SpuriousDragon account could be // saved even empty. - Self::touch_account(last_journal, &address, account); + Self::touch_account(last_journal, &target_address, target_acc); // Add balance to created account, as we already have target here. - let Some(new_balance) = account.info.balance.checked_add(balance) else { + let Some(new_balance) = target_acc.info.balance.checked_add(balance) else { self.checkpoint_revert(checkpoint); - return Err(InstructionResult::OverflowPayment); + return Err(TransferError::OverflowPayment); }; - account.info.balance = new_balance; + target_acc.info.balance = new_balance; - // EIP-161: State trie clearing (invariant-preserving alternative) - if spec_id.is_enabled_in(SPURIOUS_DRAGON) { - // nonce is going to be reset to zero in AccountCreated journal entry. - account.info.nonce = 1; - } - - // Sub balance from caller - let caller_account = self.state.get_mut(&caller).unwrap(); - // Balance is already checked in `create_inner`, so it is safe to just subtract. - caller_account.info.balance -= balance; + // safe to decrement for the caller as balance check is already done. + self.state.get_mut(&caller).unwrap().info.balance -= balance; // add journal entry of transferred balance last_journal.push(JournalEntry::BalanceTransfer { from: caller, - to: address, + to: target_address, balance, }); @@ -447,7 +547,7 @@ impl JournaledState { /// Reverts all changes to state until given checkpoint. #[inline] pub fn checkpoint_revert(&mut self, checkpoint: JournalCheckpoint) { - let is_spurious_dragon_enabled = SpecId::enabled(self.spec, SPURIOUS_DRAGON); + 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; @@ -482,14 +582,13 @@ impl JournaledState { /// * /// * #[inline] - pub fn selfdestruct( + pub fn selfdestruct( &mut self, address: Address, target: Address, - db: &mut DB, ) -> Result, DB::Error> { let spec = self.spec; - let account_load = self.load_account(target, db)?; + let account_load = self.load_account(target)?; let is_cold = account_load.is_cold; let is_empty = account_load.state_clear_aware_is_empty(spec); @@ -506,7 +605,7 @@ impl JournaledState { let acc = self.state.get_mut(&address).unwrap(); let balance = acc.info.balance; let previously_destroyed = acc.is_selfdestructed(); - let is_cancun_enabled = SpecId::enabled(self.spec, CANCUN); + let is_cancun_enabled = self.spec.is_enabled_in(CANCUN); // EIP-6780 (Cancun hard-fork): selfdestruct only if contract is created in the same tx let journal_entry = if acc.is_created() || !is_cancun_enabled { @@ -549,17 +648,17 @@ impl JournaledState { /// Initial load of account. This load will not be tracked inside journal #[inline] - pub fn initial_account_load( + pub fn initial_account_load( &mut self, address: Address, storage_keys: impl IntoIterator, - db: &mut DB, ) -> Result<&mut Account, DB::Error> { // load or get account. let account = match self.state.entry(address) { Entry::Occupied(entry) => entry.into_mut(), Entry::Vacant(vac) => vac.insert( - db.basic(address)? + self.database + .basic(address)? .map(|i| i.into()) .unwrap_or(Account::new_not_existing()), ), @@ -567,7 +666,7 @@ impl JournaledState { // preload storages. for storage_key in storage_keys.into_iter() { if let Entry::Vacant(entry) = account.storage.entry(storage_key) { - let storage = db.storage(address, storage_key)?; + let storage = self.database.storage(address, storage_key)?; entry.insert(EvmStorageSlot::new(storage)); } } @@ -576,10 +675,42 @@ impl JournaledState { /// load account into memory. return if it is cold or warm accessed #[inline] - pub fn load_account( + pub fn load_account(&mut self, address: Address) -> Result, DB::Error> { + self.load_account_optional(address, false) + } + + #[inline] + pub fn load_account_delegated(&mut self, address: Address) -> Result { + let spec = self.spec; + let account = self.load_code(address)?; + let is_empty = account.state_clear_aware_is_empty(spec); + + let mut account_load = AccountLoad { + is_empty, + load: Eip7702CodeLoad::new_not_delegated((), account.is_cold), + }; + // 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(address)?; + account_load + .load + .set_delegate_load(delegate_account.is_cold); + } + + Ok(account_load) + } + + pub fn load_code(&mut self, address: Address) -> Result, DB::Error> { + self.load_account_optional(address, true) + } + + /// Loads code. + #[inline] + pub fn load_account_optional( &mut self, address: Address, - db: &mut DB, + load_code: bool, ) -> Result, DB::Error> { let load = match self.state.entry(address) { Entry::Occupied(entry) => { @@ -591,7 +722,7 @@ impl JournaledState { } } Entry::Vacant(vac) => { - let account = if let Some(account) = db.basic(address)? { + let account = if let Some(account) = self.database.basic(address)? { account.into() } else { Account::new_not_existing() @@ -606,7 +737,6 @@ impl JournaledState { } } }; - // journal loading of cold account. if load.is_cold { self.journal @@ -614,55 +744,20 @@ impl JournaledState { .unwrap() .push(JournalEntry::AccountWarmed { address }); } - - Ok(load) - } - - #[inline] - pub fn load_account_delegated( - &mut self, - address: Address, - db: &mut DB, - ) -> Result { - let spec = self.spec; - let account = self.load_code(address, db)?; - let is_empty = account.state_clear_aware_is_empty(spec); - - let mut account_load = AccountLoad { - is_empty, - load: Eip7702CodeLoad::new_not_delegated((), account.is_cold), - }; - // 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(address, db)?; - account_load - .load - .set_delegate_load(delegate_account.is_cold); - } - - Ok(account_load) - } - - /// Loads code. - #[inline] - pub fn load_code( - &mut self, - address: Address, - db: &mut DB, - ) -> Result, DB::Error> { - let account_load = self.load_account(address, db)?; - let acc = &mut account_load.data.info; - if acc.code.is_none() { - if acc.code_hash == KECCAK_EMPTY { - let empty = Bytecode::default(); - acc.code = Some(empty); - } else { - let code = db.code_by_hash(acc.code_hash)?; - acc.code = Some(code); + if load_code { + let info = &mut load.data.info; + if info.code.is_none() { + if info.code_hash == KECCAK_EMPTY { + let empty = Bytecode::default(); + info.code = Some(empty); + } else { + let code = self.database.code_by_hash(info.code_hash)?; + info.code = Some(code); + } } } - Ok(account_load) + + Ok(load) } /// Load storage slot @@ -671,12 +766,7 @@ impl JournaledState { /// /// Panics if the account is not present in the state. #[inline] - pub fn sload( - &mut self, - address: Address, - key: U256, - db: &mut DB, - ) -> Result, DB::Error> { + pub fn sload(&mut self, address: Address, key: U256) -> Result, DB::Error> { // 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. @@ -692,7 +782,7 @@ impl JournaledState { let value = if is_newly_created { U256::ZERO } else { - db.storage(address, key)? + self.database.storage(address, key)? }; vac.insert(EvmStorageSlot::new(value)); @@ -719,15 +809,14 @@ impl JournaledState { /// /// account should already be present in our state. #[inline] - pub fn sstore( + pub fn sstore( &mut self, address: Address, key: U256, new: U256, - db: &mut DB, ) -> Result, DB::Error> { // assume that acc exists and load the slot. - let present = self.sload(address, key, db)?; + let present = self.sload(address, key)?; let acc = self.state.get_mut(&address).unwrap(); // if there is no original value in dirty return present value, that is our original. @@ -890,11 +979,3 @@ pub enum JournalEntry { /// Revert: Revert to previous bytecode. CodeChange { address: Address }, } - -/// SubRoutine checkpoint that will help us to go back from this -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct JournalCheckpoint { - log_i: usize, - journal_i: usize, -} diff --git a/crates/context/src/lib.rs b/crates/context/src/lib.rs index b3ceb57058..44a5b394c0 100644 --- a/crates/context/src/lib.rs +++ b/crates/context/src/lib.rs @@ -5,4 +5,14 @@ #[cfg(not(feature = "std"))] extern crate alloc as std; +pub mod block; +pub mod cfg; +pub mod context; +pub mod journaled_state; +pub mod tx; +pub use block::BlockEnv; +pub use cfg::{Cfg, CfgEnv}; +pub use context::*; +pub use journaled_state::*; +pub use tx::TxEnv; diff --git a/crates/wiring/src/default/tx.rs b/crates/context/src/tx.rs similarity index 96% rename from crates/wiring/src/default/tx.rs rename to crates/context/src/tx.rs index 586fbda383..94e79e417b 100644 --- a/crates/wiring/src/default/tx.rs +++ b/crates/context/src/tx.rs @@ -1,13 +1,16 @@ -use crate::{result::InvalidTransaction, Transaction}; +use context_interface::{ + result::InvalidTransaction, + transaction::{ + eip7702::Authorization, CommonTxFields, Eip1559CommonTxFields, Eip1559Tx, Eip2930Tx, + Eip4844Tx, Eip7702Tx, LegacyTx, TransactionType, + }, + Transaction, +}; use core::fmt::Debug; use primitives::{Address, Bytes, TxKind, B256, U256}; use specification::eip2930::AccessList; use specification::eip7702::AuthorizationList; use std::vec::Vec; -use transaction::{ - eip7702::Authorization, CommonTxFields, Eip1559CommonTxFields, Eip1559Tx, Eip2930Tx, Eip4844Tx, - Eip7702Tx, LegacyTx, TransactionType, -}; /// The transaction environment. #[derive(Clone, Debug, PartialEq, Eq)] diff --git a/crates/database/Cargo.toml b/crates/database/Cargo.toml index 37478a2ccc..6296dfbf9d 100644 --- a/crates/database/Cargo.toml +++ b/crates/database/Cargo.toml @@ -25,7 +25,7 @@ all = "warn" state.workspace = true primitives.workspace = true database-interface.workspace = true -wiring.workspace = true +context-interface.workspace = true bytecode.workspace = true auto_impl = "1.2" diff --git a/crates/database/interface/Cargo.toml b/crates/database/interface/Cargo.toml index 64fe335906..e3d3e51533 100644 --- a/crates/database/interface/Cargo.toml +++ b/crates/database/interface/Cargo.toml @@ -22,10 +22,12 @@ rust_2018_idioms = "deny" all = "warn" [dependencies] +# revm state.workspace = true primitives.workspace = true -auto_impl = "1.2" +# mics +auto_impl.workspace = true # Optional serde = { version = "1.0", default-features = false, features = [ @@ -48,6 +50,4 @@ alloy-sol-types = "0.8" default = ["std"] std = ["serde?/std"] serde = ["dep:serde"] -asyncdb = [ - "dep:tokio", -] +asyncdb = ["dep:tokio"] diff --git a/crates/database/interface/src/async_db.rs b/crates/database/interface/src/async_db.rs index 85f2493623..1332bcc15a 100644 --- a/crates/database/interface/src/async_db.rs +++ b/crates/database/interface/src/async_db.rs @@ -4,7 +4,7 @@ use primitives::{Address, B256, U256}; use state::{AccountInfo, Bytecode}; use tokio::runtime::{Handle, Runtime}; -use crate::{Database, DatabaseRef}; +use crate::{DBErrorMarker, Database, DatabaseRef}; /// The async EVM database interface. /// @@ -13,7 +13,7 @@ use crate::{Database, DatabaseRef}; /// Use [WrapDatabaseAsync] to provide [Database] implementation for a type that only implements this trait. pub trait DatabaseAsync { /// The database error type. - type Error: Send; + type Error: Send + DBErrorMarker; /// Get basic account information. fn basic_async( @@ -48,7 +48,7 @@ pub trait DatabaseAsync { /// Use [WrapDatabaseAsync] to provide [DatabaseRef] implementation for a type that only implements this trait. pub trait DatabaseAsyncRef { /// The database error type. - type Error: Send; + type Error: Send + DBErrorMarker; /// Get basic account information. fn basic_async_ref( diff --git a/crates/database/interface/src/empty_db.rs b/crates/database/interface/src/empty_db.rs index 3c5445c405..b58cbeac9a 100644 --- a/crates/database/interface/src/empty_db.rs +++ b/crates/database/interface/src/empty_db.rs @@ -1,4 +1,4 @@ -use crate::{Database, DatabaseRef}; +use crate::{DBErrorMarker, Database, DatabaseRef}; use core::{convert::Infallible, fmt, marker::PhantomData}; use primitives::{keccak256, Address, B256, U256}; use state::{AccountInfo, Bytecode}; @@ -52,7 +52,7 @@ impl EmptyDBTyped { } } -impl Database for EmptyDBTyped { +impl Database for EmptyDBTyped { type Error = E; #[inline] @@ -76,7 +76,7 @@ impl Database for EmptyDBTyped { } } -impl DatabaseRef for EmptyDBTyped { +impl DatabaseRef for EmptyDBTyped { type Error = E; #[inline] diff --git a/crates/database/interface/src/lib.rs b/crates/database/interface/src/lib.rs index 7a4469d09d..9548827b60 100644 --- a/crates/database/interface/src/lib.rs +++ b/crates/database/interface/src/lib.rs @@ -5,6 +5,8 @@ #[cfg(not(feature = "std"))] extern crate alloc as std; +use core::convert::Infallible; + use auto_impl::auto_impl; use primitives::{Address, HashMap, B256, U256}; use state::{Account, AccountInfo, Bytecode}; @@ -17,11 +19,22 @@ pub mod empty_db; pub use async_db::{DatabaseAsync, WrapDatabaseAsync}; pub use empty_db::{EmptyDB, EmptyDBTyped}; +pub trait BytecodeTrait { + fn code(&self) -> &[u8]; +} +/// Database error marker is needed to implement From conversion for Error type. +pub trait DBErrorMarker {} + +/// Implement marker for `()`. +impl DBErrorMarker for () {} +impl DBErrorMarker for Infallible {} + /// EVM database interface. #[auto_impl(&mut, Box)] pub trait Database { /// The database error type. - type Error; + type Error: DBErrorMarker; + //type Bytecode: BytecodeTrait; /// Get basic account information. fn basic(&mut self, address: Address) -> Result, Self::Error>; @@ -52,7 +65,7 @@ pub trait DatabaseCommit { #[auto_impl(&, &mut, Box, Rc, Arc)] pub trait DatabaseRef { /// The database error type. - type Error; + type Error: DBErrorMarker; /// Get basic account information. fn basic_ref(&self, address: Address) -> Result, Self::Error>; @@ -108,3 +121,10 @@ impl DatabaseCommit for WrapDatabaseRef { self.0.commit(changes) } } + +#[auto_impl(&mut, Box)] +pub trait DatabaseGetter { + type Database: Database; + + fn db(&mut self) -> &mut Self::Database; +} diff --git a/crates/database/src/alloydb.rs b/crates/database/src/alloydb.rs index 2572a6e70f..59f43c0c56 100644 --- a/crates/database/src/alloydb.rs +++ b/crates/database/src/alloydb.rs @@ -4,10 +4,21 @@ use alloy_provider::{ Network, Provider, }; use alloy_transport::{Transport, TransportError}; -use database_interface::async_db::DatabaseAsyncRef; +use database_interface::{async_db::DatabaseAsyncRef, DBErrorMarker}; use primitives::{Address, B256, U256}; use state::{AccountInfo, Bytecode}; +#[derive(Debug)] +pub struct DBTransportError(pub TransportError); + +impl DBErrorMarker for DBTransportError {} + +impl From for DBTransportError { + fn from(e: TransportError) -> Self { + Self(e) + } +} + /// An alloy-powered REVM [database_interface::Database]. /// /// When accessing the database, it'll use the given provider to fetch the corresponding account's data. @@ -17,7 +28,7 @@ pub struct AlloyDB> { provider: P, /// The block number on which the queries will be based on. block_number: BlockId, - _marker: std::marker::PhantomData (T, N)>, + _marker: core::marker::PhantomData (T, N)>, } impl> AlloyDB { @@ -26,7 +37,7 @@ impl> AlloyDB { Self { provider, block_number, - _marker: std::marker::PhantomData, + _marker: core::marker::PhantomData, } } @@ -37,7 +48,7 @@ impl> AlloyDB { } impl> DatabaseAsyncRef for AlloyDB { - type Error = TransportError; + type Error = DBTransportError; async fn basic_async_ref(&self, address: Address) -> Result, Self::Error> { let nonce = self @@ -79,10 +90,11 @@ impl> DatabaseAsyncRef for A } async fn storage_async_ref(&self, address: Address, index: U256) -> Result { - self.provider + Ok(self + .provider .get_storage_at(address, index) .block_id(self.block_number) - .await + .await?) } } diff --git a/crates/database/src/in_memory_db.rs b/crates/database/src/in_memory_db.rs index e55d8d3bb0..a221c4f293 100644 --- a/crates/database/src/in_memory_db.rs +++ b/crates/database/src/in_memory_db.rs @@ -3,7 +3,6 @@ use database_interface::{Database, DatabaseCommit, DatabaseRef, EmptyDB}; use primitives::{hash_map::Entry, Address, HashMap, Log, B256, KECCAK_EMPTY, U256}; use state::{Account, AccountInfo, Bytecode}; use std::vec::Vec; -use wiring::EthereumWiring; /// A [Database] implementation that stores all state changes in memory. pub type InMemoryDB = CacheDB; @@ -354,9 +353,6 @@ impl AccountState { } } -/// Ethereum benchmark wiring -pub type EthereumBenchmarkWiring = EthereumWiring; - /// Custom benchmarking DB that only has account info for the zero address. /// /// Any other address will return an empty account. diff --git a/crates/database/src/states/state_builder.rs b/crates/database/src/states/state_builder.rs index 377059f067..ed67c253a9 100644 --- a/crates/database/src/states/state_builder.rs +++ b/crates/database/src/states/state_builder.rs @@ -1,5 +1,5 @@ use super::{cache::CacheState, state::DBBox, BundleState, State, TransitionState}; -use database_interface::{Database, DatabaseRef, EmptyDB, WrapDatabaseRef}; +use database_interface::{DBErrorMarker, Database, DatabaseRef, EmptyDB, WrapDatabaseRef}; use primitives::B256; use std::collections::BTreeMap; @@ -81,7 +81,7 @@ impl StateBuilder { } /// With boxed version of database. - pub fn with_database_boxed( + pub fn with_database_boxed( self, database: DBBox<'_, Error>, ) -> StateBuilder> { diff --git a/crates/gas/Cargo.toml b/crates/gas/Cargo.toml deleted file mode 100644 index e554cfba45..0000000000 --- a/crates/gas/Cargo.toml +++ /dev/null @@ -1,41 +0,0 @@ -[package] -name = "revm-gas" -description = "Revm gas constants and calculations" -version = "1.0.0" -authors.workspace = true -edition.workspace = true -keywords.workspace = true -license.workspace = true -repository.workspace = true -readme.workspace = true - -[package.metadata.docs.rs] -all-features = true -rustdoc-args = ["--cfg", "docsrs"] - -[lints.rust] -unreachable_pub = "warn" -unused_must_use = "deny" -rust_2018_idioms = "deny" - -[lints.rustdoc] -all = "warn" - -[dependencies] - -# Optional -serde = { version = "1.0", default-features = false, features = [ - "derive", - "rc", -], optional = true } - - -[dev-dependencies] - -[features] -default = ["std",] -std = [ - "serde?/std", -] -serde = ["dep:serde"] -serde-json = ["serde"] \ No newline at end of file diff --git a/crates/gas/LICENSE b/crates/gas/LICENSE deleted file mode 100644 index ad98ff22cc..0000000000 --- a/crates/gas/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2021-2024 draganrakita - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/crates/gas/src/lib.rs b/crates/gas/src/lib.rs deleted file mode 100644 index b3ceb57058..0000000000 --- a/crates/gas/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -//! Optimism-specific constants, types, and helpers. -#![cfg_attr(not(test), warn(unused_crate_dependencies))] -#![cfg_attr(not(feature = "std"), no_std)] - -#[cfg(not(feature = "std"))] -extern crate alloc as std; - - diff --git a/crates/interface/CHANGELOG.md b/crates/handler/CHANGELOG.md similarity index 100% rename from crates/interface/CHANGELOG.md rename to crates/handler/CHANGELOG.md diff --git a/crates/wiring/transaction/Cargo.toml b/crates/handler/Cargo.toml similarity index 61% rename from crates/wiring/transaction/Cargo.toml rename to crates/handler/Cargo.toml index 5dfd154e00..3037d07d0c 100644 --- a/crates/wiring/transaction/Cargo.toml +++ b/crates/handler/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "revm-transaction" -description = "Revm crate" +name = "revm-handler" +description = "Revm handler crates" version = "1.0.0" authors.workspace = true edition.workspace = true @@ -23,8 +23,14 @@ all = "warn" [dependencies] # revm -specification.workspace = true +interpreter.workspace = true +precompile.workspace = true +context-interface.workspace = true primitives.workspace = true +state.workspace = true +specification.workspace = true +bytecode.workspace = true +handler-interface.workspace = true # Optional serde = { version = "1.0", default-features = false, features = [ @@ -32,11 +38,17 @@ serde = { version = "1.0", default-features = false, features = [ "rc", ], optional = true } -# mics -auto_impl = "1.2" +[dev-dependencies] +database.workspace = true [features] default = ["std"] std = ["serde?/std"] -serde = ["dep:serde"] +serde = [ + "dep:serde", + "primitives/serde", + "specification/serde", + "state/serde", + "context-interface/serde", +] serde-json = ["serde"] diff --git a/crates/wiring/CHANGELOG.md b/crates/handler/interface/CHANGELOG.md similarity index 100% rename from crates/wiring/CHANGELOG.md rename to crates/handler/interface/CHANGELOG.md diff --git a/crates/interface/Cargo.toml b/crates/handler/interface/Cargo.toml similarity index 62% rename from crates/interface/Cargo.toml rename to crates/handler/interface/Cargo.toml index 2867aba626..402098f165 100644 --- a/crates/interface/Cargo.toml +++ b/crates/handler/interface/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "revm-interface" -description = "Revm crate" +name = "revm-handler-interface" +description = "Revm handler interface crates" version = "1.0.0" authors.workspace = true edition.workspace = true @@ -22,14 +22,14 @@ rust_2018_idioms = "deny" all = "warn" [dependencies] -# Optional -serde = { version = "1.0", default-features = false, features = [ - "derive", - "rc", -], optional = true } +# revm +primitives.workspace = true +interpreter.workspace = true + +[dev-dependencies] +database.workspace = true [features] default = ["std"] -std = ["serde?/std"] -serde = ["dep:serde"] -serde-json = ["serde"] +std = [] +serde = ["std", "primitives/serde", "interpreter/serde"] diff --git a/crates/handler/interface/src/execution.rs b/crates/handler/interface/src/execution.rs new file mode 100644 index 0000000000..6b1c9cdaa1 --- /dev/null +++ b/crates/handler/interface/src/execution.rs @@ -0,0 +1,57 @@ +use crate::util::FrameOrFrameResult; +pub use crate::{Frame, FrameOrResultGen}; +pub use std::{vec, vec::Vec}; + +pub trait ExecutionHandler { + type Context; + type Error; + type Frame: Frame; + type ExecResult; + + /// Execute call. + fn init_first_frame( + &mut self, + context: &mut Self::Context, + gas_limit: u64, + ) -> Result, Self::Error>; + + /// Execute create. + fn last_frame_result( + &self, + context: &mut Self::Context, + frame_result: ::FrameResult, + ) -> Result; + + fn run( + &self, + context: &mut Self::Context, + frame: Self::Frame, + ) -> Result { + let mut frame_stack: Vec<::Frame> = vec![frame]; + loop { + let frame = frame_stack.last_mut().unwrap(); + let call_or_result = frame.run(context)?; + + let result = match call_or_result { + FrameOrResultGen::Frame(init) => match frame.init(context, init)? { + FrameOrResultGen::Frame(new_frame) => { + frame_stack.push(new_frame); + continue; + } + // dont pop the frame as new frame was not created. + FrameOrResultGen::Result(result) => result, + }, + FrameOrResultGen::Result(result) => { + // pop frame that returned result + frame_stack.pop(); + result + } + }; + + let Some(frame) = frame_stack.last_mut() else { + return self.last_frame_result(context, result); + }; + frame.return_result(context, result)?; + } + } +} diff --git a/crates/handler/interface/src/frame.rs b/crates/handler/interface/src/frame.rs new file mode 100644 index 0000000000..ba0af0217a --- /dev/null +++ b/crates/handler/interface/src/frame.rs @@ -0,0 +1,31 @@ +use crate::FrameOrResultGen; + +/// Call frame trait. +pub trait Frame: Sized { + type Context; + type FrameInit; + type FrameResult; + type Error; + + fn init_first( + context: &mut Self::Context, + frame_input: Self::FrameInit, + ) -> Result, Self::Error>; + + fn init( + &self, + context: &mut Self::Context, + frame_input: Self::FrameInit, + ) -> Result, Self::Error>; + + fn run( + &mut self, + context: &mut Self::Context, + ) -> Result, Self::Error>; + + fn return_result( + &mut self, + context: &mut Self::Context, + result: Self::FrameResult, + ) -> Result<(), Self::Error>; +} diff --git a/crates/handler/interface/src/handler.rs b/crates/handler/interface/src/handler.rs new file mode 100644 index 0000000000..c33ead7c20 --- /dev/null +++ b/crates/handler/interface/src/handler.rs @@ -0,0 +1,13 @@ +use crate::{ExecutionHandler, PostExecutionHandler, PreExecutionHandler, ValidationHandler}; + +pub trait Handler { + type Validation: ValidationHandler; + type PreExecution: PreExecutionHandler; + type Execution: ExecutionHandler; + type PostExecution: PostExecutionHandler; + + fn validation(&mut self) -> &mut Self::Validation; + fn pre_execution(&mut self) -> &mut Self::PreExecution; + fn execution(&mut self) -> &mut Self::Execution; + fn post_execution(&mut self) -> &mut Self::PostExecution; +} diff --git a/crates/handler/interface/src/lib.rs b/crates/handler/interface/src/lib.rs new file mode 100644 index 0000000000..03e8c8818c --- /dev/null +++ b/crates/handler/interface/src/lib.rs @@ -0,0 +1,24 @@ +//! Optimism-specific constants, types, and helpers. +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(not(feature = "std"))] +extern crate alloc as std; + +pub mod execution; +pub mod frame; +pub mod handler; +pub mod post_execution; +pub mod pre_execution; +pub mod precompile_provider; +pub mod util; +pub mod validation; + +pub use execution::ExecutionHandler; +pub use frame::Frame; +pub use handler::Handler; +pub use post_execution::PostExecutionHandler; +pub use pre_execution::PreExecutionHandler; +pub use precompile_provider::PrecompileProvider; +pub use util::FrameOrResultGen; +pub use validation::ValidationHandler; diff --git a/crates/handler/interface/src/post_execution.rs b/crates/handler/interface/src/post_execution.rs new file mode 100644 index 0000000000..64d2f08a1d --- /dev/null +++ b/crates/handler/interface/src/post_execution.rs @@ -0,0 +1,51 @@ +pub trait PostExecutionHandler { + type Context; + type Error; + type ExecResult; + type Output; + + /// Calculate final refund + fn refund( + &self, + context: &mut Self::Context, + exec_result: &mut Self::ExecResult, + eip7702_refund: i64, + ); + + /// Reimburse the caller with balance it didn't spent. + fn reimburse_caller( + &self, + context: &mut Self::Context, + exec_result: &mut Self::ExecResult, + ) -> Result<(), Self::Error>; + + /// Reward beneficiary with transaction rewards. + fn reward_beneficiary( + &self, + context: &mut Self::Context, + exec_result: &mut Self::ExecResult, + ) -> Result<(), Self::Error>; + + /// Main return handle, takes state from journal and transforms internal result to [`PostExecutionHandler::Output`]. + fn output( + &self, + context: &mut Self::Context, + result: Self::ExecResult, + ) -> Result; + + /// Called when execution ends. + /// + /// End handle in comparison to output handle will be called every time after execution. + /// While [`PostExecutionHandler::output`] will be omitted in case of the error. + fn end( + &self, + _context: &mut Self::Context, + end_output: Result, + ) -> Result { + end_output + } + + /// Clean handler. This handle is called every time regardless + /// of the result of the transaction. + fn clear(&self, context: &mut Self::Context); +} diff --git a/crates/handler/interface/src/pre_execution.rs b/crates/handler/interface/src/pre_execution.rs new file mode 100644 index 0000000000..9afe508d99 --- /dev/null +++ b/crates/handler/interface/src/pre_execution.rs @@ -0,0 +1,10 @@ +pub trait PreExecutionHandler { + type Context; + type Error; + + fn load_accounts(&self, context: &mut Self::Context) -> Result<(), Self::Error>; + + fn apply_eip7702_auth_list(&self, context: &mut Self::Context) -> Result; + + fn deduct_caller(&self, context: &mut Self::Context) -> Result<(), Self::Error>; +} diff --git a/crates/handler/interface/src/precompile_provider.rs b/crates/handler/interface/src/precompile_provider.rs new file mode 100644 index 0000000000..68a1cfa918 --- /dev/null +++ b/crates/handler/interface/src/precompile_provider.rs @@ -0,0 +1,25 @@ +use interpreter::InterpreterResult; +use primitives::{Address, Bytes}; + +pub trait PrecompileProvider: Clone { + type Context; + type Error; + + /// Create a new precompile. + fn new(context: &mut Self::Context) -> Self; + + /// Run the precompile. + fn run( + &mut self, + context: &mut Self::Context, + address: &Address, + bytes: &Bytes, + gas_limit: u64, + ) -> Result, Self::Error>; + + /// Get the warm addresses. + fn warm_addresses(&self) -> impl Iterator; + + /// Check if the address is a precompile. + fn contains(&self, address: &Address) -> bool; +} diff --git a/crates/handler/interface/src/util.rs b/crates/handler/interface/src/util.rs new file mode 100644 index 0000000000..560d8341bc --- /dev/null +++ b/crates/handler/interface/src/util.rs @@ -0,0 +1,24 @@ +use crate::Frame; + +pub enum FrameOrResultGen { + Frame(Frame), + Result(Result), +} + +impl FrameOrResultGen { + pub fn map_frame(self, f: impl FnOnce(F) -> F2) -> FrameOrResultGen { + match self { + FrameOrResultGen::Frame(frame) => FrameOrResultGen::Frame(f(frame)), + FrameOrResultGen::Result(result) => FrameOrResultGen::Result(result), + } + } + + pub fn map_result(self, f: impl FnOnce(R) -> R2) -> FrameOrResultGen { + match self { + FrameOrResultGen::Frame(frame) => FrameOrResultGen::Frame(frame), + FrameOrResultGen::Result(result) => FrameOrResultGen::Result(f(result)), + } + } +} + +pub type FrameOrFrameResult = FrameOrResultGen::FrameResult>; diff --git a/crates/handler/interface/src/validation.rs b/crates/handler/interface/src/validation.rs new file mode 100644 index 0000000000..eb3d8a7197 --- /dev/null +++ b/crates/handler/interface/src/validation.rs @@ -0,0 +1,13 @@ +pub trait ValidationHandler { + type Context; + type Error; + + /// Validate env. + fn validate_env(&self, context: &Self::Context) -> Result<(), Self::Error>; + + /// Validate transactions against state. + fn validate_tx_against_state(&self, context: &mut Self::Context) -> Result<(), Self::Error>; + + /// Validate initial gas. + fn validate_initial_tx_gas(&self, context: &Self::Context) -> Result; +} diff --git a/crates/handler/src/execution.rs b/crates/handler/src/execution.rs new file mode 100644 index 0000000000..c66daa10cb --- /dev/null +++ b/crates/handler/src/execution.rs @@ -0,0 +1,215 @@ +use super::{frame_data::FrameResult, EthFrame, EthPrecompileProvider}; +use bytecode::EOF_MAGIC_BYTES; +use context_interface::{ + result::InvalidTransaction, BlockGetter, Cfg, CfgGetter, ErrorGetter, JournalStateGetter, + JournalStateGetterDBError, Transaction, TransactionGetter, +}; +use handler_interface::{util::FrameOrFrameResult, ExecutionHandler, Frame as FrameTrait}; +use interpreter::{ + interpreter::{EthInstructionProvider, EthInterpreter}, + CallInputs, CallScheme, CallValue, CreateInputs, CreateScheme, EOFCreateInputs, EOFCreateKind, + FrameInput, Gas, +}; +use primitives::TxKind; +use specification::hardfork::SpecId; +use std::boxed::Box; + +#[derive(Default)] +pub struct EthExecution< + CTX, + ERROR, + FRAME = EthFrame< + CTX, + ERROR, + EthInterpreter<()>, + EthPrecompileProvider, + EthInstructionProvider, CTX>, + >, +> { + _phantom: core::marker::PhantomData<(CTX, FRAME, ERROR)>, +} + +impl ExecutionHandler for EthExecution +where + CTX: EthExecutionContext, + ERROR: EthExecutionError, + FRAME: + FrameTrait, +{ + type Context = CTX; + type Error = ERROR; + type Frame = FRAME; + type ExecResult = FrameResult; + + fn init_first_frame( + &mut self, + context: &mut Self::Context, + gas_limit: u64, + ) -> Result, Self::Error> { + // Make new frame action. + let spec = context.cfg().spec().into(); + let tx = context.tx(); + let input = tx.common_fields().input().clone(); + + let init_frame: FrameInput = match tx.kind() { + TxKind::Call(target_address) => FrameInput::Call(Box::new(CallInputs { + input, + gas_limit, + target_address, + bytecode_address: target_address, + caller: tx.common_fields().caller(), + value: CallValue::Transfer(tx.common_fields().value()), + scheme: CallScheme::Call, + is_static: false, + is_eof: false, + return_memory_offset: 0..0, + })), + TxKind::Create => { + // if first byte of data is magic 0xEF00, then it is EOFCreate. + if spec.is_enabled_in(SpecId::PRAGUE_EOF) && input.starts_with(&EOF_MAGIC_BYTES) { + FrameInput::EOFCreate(Box::new(EOFCreateInputs::new( + tx.common_fields().caller(), + tx.common_fields().value(), + gas_limit, + EOFCreateKind::Tx { initdata: input }, + ))) + } else { + FrameInput::Create(Box::new(CreateInputs { + caller: tx.common_fields().caller(), + scheme: CreateScheme::Create, + value: tx.common_fields().value(), + init_code: input, + gas_limit, + })) + } + } + }; + FRAME::init_first(context, init_frame) + } + + fn last_frame_result( + &self, + context: &mut Self::Context, + mut frame_result: ::FrameResult, + ) -> Result { + let instruction_result = frame_result.interpreter_result().result; + let gas = frame_result.gas_mut(); + let remaining = gas.remaining(); + let refunded = gas.refunded(); + + // Spend the gas limit. Gas is reimbursed when the tx returns successfully. + *gas = Gas::new_spent(context.tx().common_fields().gas_limit()); + + if instruction_result.is_ok_or_revert() { + gas.erase_cost(remaining); + } + + if instruction_result.is_ok() { + gas.record_refund(refunded); + } + + Ok(frame_result) + } +} + +impl EthExecution { + pub fn new() -> Self { + Self { + _phantom: core::marker::PhantomData, + } + } + + pub fn new_boxed() -> Box { + Box::new(Self::new()) + } +} + +pub trait EthExecutionContext: + TransactionGetter + ErrorGetter + BlockGetter + JournalStateGetter + CfgGetter +{ +} + +impl< + ERROR, + T: TransactionGetter + + ErrorGetter + + BlockGetter + + JournalStateGetter + + CfgGetter, + > EthExecutionContext for T +{ +} + +pub trait EthExecutionError: + From + From> +{ +} + +impl< + CTX: JournalStateGetter, + T: From + From>, + > EthExecutionError for T +{ +} + +// #[cfg(test)] +// mod tests { +// use super::*; +// use crate::handler::mainnet::refund; +// use interpreter::InstructionResult; +// use primitives::Bytes; +// use specification::hardfork::CancunSpec; +// use context_interface::{default::EnvWiring, DefaultEthereumWiring}; + +// /// Creates frame result. +// fn call_last_frame_return(instruction_result: InstructionResult, gas: Gas) -> Gas { +// let mut env = Envcontext_interface::::default(); +// env.tx.gas_limit = 100; + +// let mut context = Context::builder(); +// context.evm.inner.env = Box::new(env); +// let mut first_frame = FrameResult::Call(CallOutcome::new( +// InterpreterResult { +// result: instruction_result, +// output: Bytes::new(), +// gas, +// }, +// 0..0, +// )); +// last_frame_return::(&mut context, &mut first_frame).unwrap(); +// refund::(&mut context, first_frame.gas_mut(), 0); +// *first_frame.gas() +// } + +// #[test] +// fn test_consume_gas() { +// let gas = call_last_frame_return(InstructionResult::Stop, Gas::new(90)); +// assert_eq!(gas.remaining(), 90); +// assert_eq!(gas.spent(), 10); +// assert_eq!(gas.refunded(), 0); +// } + +// #[test] +// fn test_consume_gas_with_refund() { +// let mut return_gas = Gas::new(90); +// return_gas.record_refund(30); + +// let gas = call_last_frame_return(InstructionResult::Stop, return_gas); +// assert_eq!(gas.remaining(), 90); +// assert_eq!(gas.spent(), 10); +// assert_eq!(gas.refunded(), 2); + +// let gas = call_last_frame_return(InstructionResult::Revert, return_gas); +// assert_eq!(gas.remaining(), 90); +// assert_eq!(gas.spent(), 10); +// assert_eq!(gas.refunded(), 0); +// } + +// #[test] +// fn test_revert_gas() { +// let gas = call_last_frame_return(InstructionResult::Revert, Gas::new(90)); +// assert_eq!(gas.remaining(), 90); +// assert_eq!(gas.spent(), 10); +// assert_eq!(gas.refunded(), 0); +// } +// } diff --git a/crates/handler/src/frame.rs b/crates/handler/src/frame.rs new file mode 100644 index 0000000000..4952bfe6d4 --- /dev/null +++ b/crates/handler/src/frame.rs @@ -0,0 +1,830 @@ +use super::frame_data::*; +use bytecode::{Eof, EOF_MAGIC_BYTES}; +use context_interface::{ + journaled_state::{JournalCheckpoint, JournaledState}, + BlockGetter, Cfg, CfgGetter, ErrorGetter, JournalStateGetter, JournalStateGetterDBError, + Transaction, TransactionGetter, +}; +use core::{cell::RefCell, cmp::min}; +use handler_interface::{Frame, FrameOrResultGen, PrecompileProvider}; +use interpreter::{ + gas, + interpreter::{EthInterpreter, InstructionProvider}, + interpreter_types::{LoopControl, ReturnData, RuntimeFlag}, + return_ok, return_revert, CallInputs, CallOutcome, CallValue, CreateInputs, CreateOutcome, + CreateScheme, EOFCreateInputs, EOFCreateKind, FrameInput, Gas, Host, InputsImpl, + InstructionResult, Interpreter, InterpreterAction, InterpreterResult, InterpreterTypes, + SharedMemory, +}; +use precompile::PrecompileErrors; +use primitives::{keccak256, Address, Bytes, B256, U256}; +use specification::{ + constants::CALL_STACK_LIMIT, + hardfork::SpecId::{self, HOMESTEAD, LONDON, PRAGUE_EOF, SPURIOUS_DRAGON}, +}; +use state::Bytecode; +use std::borrow::ToOwned; +use std::{rc::Rc, sync::Arc}; + +pub struct EthFrame { + _phantom: core::marker::PhantomData (CTX, ERROR)>, + data: FrameData, + // TODO include this + depth: usize, + /// Journal checkpoint. + pub checkpoint: JournalCheckpoint, + /// Interpreter. + pub interpreter: Interpreter, + /// Precompiles provider. + pub precompiles: PRECOMPILE, + /// Instruction provider. + pub instructions: INSTRUCTIONS, + // This is worth making as a generic type FrameSharedContext. + pub memory: Rc>, +} + +impl EthFrame +where + CTX: JournalStateGetter, + IW: InterpreterTypes, +{ + pub fn new( + data: FrameData, + depth: usize, + interpreter: Interpreter, + checkpoint: JournalCheckpoint, + precompiles: PRECOMP, + instructions: INST, + memory: Rc>, + ) -> Self { + Self { + _phantom: core::marker::PhantomData, + data, + depth, + interpreter, + checkpoint, + precompiles, + instructions, + memory, + } + } +} + +impl + EthFrame, PRECOMPILE, INSTRUCTION> +where + CTX: EthFrameContext, + ERROR: EthFrameError, + PRECOMPILE: PrecompileProvider, +{ + /// Make call frame + #[inline] + pub fn make_call_frame( + context: &mut CTX, + depth: usize, + memory: Rc>, + inputs: &CallInputs, + mut precompile: PRECOMPILE, + instructions: INSTRUCTION, + ) -> Result, ERROR> { + let gas = Gas::new(inputs.gas_limit); + + let return_result = |instruction_result: InstructionResult| { + Ok(FrameOrResultGen::Result(FrameResult::Call(CallOutcome { + result: InterpreterResult { + result: instruction_result, + gas, + output: Bytes::new(), + }, + memory_offset: inputs.return_memory_offset.clone(), + }))) + }; + + // Check depth + if depth > CALL_STACK_LIMIT as usize { + return return_result(InstructionResult::CallTooDeep); + } + + // Make account warm and loaded + let _ = context + .journal() + .load_account_delegated(inputs.bytecode_address)?; + + // Create subroutine checkpoint + let checkpoint = context.journal().checkpoint(); + + // Touch address. For "EIP-158 State Clear", this will erase empty accounts. + if let CallValue::Transfer(value) = inputs.value { + // Transfer value from caller to called account + // Target will get touched even if balance transferred is zero. + if let Some(i) = + context + .journal() + .transfer(&inputs.caller, &inputs.target_address, value)? + { + context.journal().checkpoint_revert(checkpoint); + return return_result(i.into()); + } + } + + if let Some(result) = precompile.run( + context, + &inputs.bytecode_address, + &inputs.input, + inputs.gas_limit, + )? { + if result.result.is_ok() { + context.journal().checkpoint_commit(); + } else { + context.journal().checkpoint_revert(checkpoint); + } + Ok(FrameOrResultGen::Result(FrameResult::Call(CallOutcome { + result, + memory_offset: inputs.return_memory_offset.clone(), + }))) + } else { + let account = context + .journal() + .load_account_code(inputs.bytecode_address)?; + + // TODO Request from foundry to get bytecode hash. + let _code_hash = account.info.code_hash(); + let mut bytecode = account.info.code.clone().unwrap_or_default(); + + // ExtDelegateCall is not allowed to call non-EOF contracts. + if inputs.scheme.is_ext_delegate_call() + && !bytecode.bytes_slice().starts_with(&EOF_MAGIC_BYTES) + { + return return_result(InstructionResult::InvalidExtDelegateCallTarget); + } + + if bytecode.is_empty() { + context.journal().checkpoint_commit(); + return return_result(InstructionResult::Stop); + } + + if let Bytecode::Eip7702(eip7702_bytecode) = bytecode { + bytecode = context + .journal() + .load_account_code(eip7702_bytecode.delegated_address)? + .info + .code + .clone() + .unwrap_or_default(); + } + + // Create interpreter and executes call and push new CallStackFrame. + let interpreter_input = InputsImpl { + target_address: inputs.target_address, + caller_address: inputs.caller, + input: inputs.input.clone(), + call_value: inputs.value.get(), + }; + + Ok(FrameOrResultGen::Frame(Self::new( + FrameData::Call(CallFrame { + return_memory_range: inputs.return_memory_offset.clone(), + }), + depth, + Interpreter::new( + memory.clone(), + bytecode, + interpreter_input, + inputs.is_static, + false, + context.cfg().spec().into(), + inputs.gas_limit, + ), + checkpoint, + precompile, + instructions, + memory, + ))) + } + } + + /// Make create frame. + #[inline] + pub fn make_create_frame( + context: &mut CTX, + depth: usize, + memory: Rc>, + inputs: &CreateInputs, + precompile: PRECOMPILE, + instructions: INSTRUCTION, + ) -> Result, ERROR> { + let spec = context.cfg().spec().into(); + let return_error = |e| { + Ok(FrameOrResultGen::Result(FrameResult::Create( + CreateOutcome { + result: InterpreterResult { + result: e, + gas: Gas::new(inputs.gas_limit), + output: Bytes::new(), + }, + address: None, + }, + ))) + }; + + // Check depth + if depth > CALL_STACK_LIMIT as usize { + return return_error(InstructionResult::CallTooDeep); + } + + // Prague EOF + if spec.is_enabled_in(PRAGUE_EOF) && inputs.init_code.starts_with(&EOF_MAGIC_BYTES) { + return return_error(InstructionResult::CreateInitCodeStartingEF00); + } + + // Fetch balance of caller. + let caller_balance = context + .journal() + .load_account(inputs.caller)? + .map(|a| a.info.balance); + + // Check if caller has enough balance to send to the created contract. + if caller_balance.data < inputs.value { + return return_error(InstructionResult::OutOfFunds); + } + + // Increase nonce of caller and check if it overflows + let old_nonce; + if let Some(nonce) = context.journal().inc_account_nonce(inputs.caller)? { + old_nonce = nonce - 1; + } else { + return return_error(InstructionResult::Return); + } + + // Create address + // TODO incorporating code hash inside interpreter. It was a request by foundry. + let mut _init_code_hash = B256::ZERO; + let created_address = match inputs.scheme { + CreateScheme::Create => inputs.caller.create(old_nonce), + CreateScheme::Create2 { salt } => { + _init_code_hash = keccak256(&inputs.init_code); + inputs.caller.create2(salt.to_be_bytes(), _init_code_hash) + } + }; + + // created address is not allowed to be a precompile. + // TODO add precompile check + if precompile.contains(&created_address) { + return return_error(InstructionResult::CreateCollision); + } + + // warm load account. + context.journal().load_account(created_address)?; + + // create account, transfer funds and make the journal checkpoint. + let checkpoint = match context.journal().create_account_checkpoint( + inputs.caller, + created_address, + inputs.value, + spec, + ) { + Ok(checkpoint) => checkpoint, + Err(e) => return return_error(e.into()), + }; + + let bytecode = Bytecode::new_legacy(inputs.init_code.clone()); + + let interpreter_input = InputsImpl { + target_address: created_address, + caller_address: inputs.caller, + input: Bytes::new(), + call_value: inputs.value, + }; + + Ok(FrameOrResultGen::Frame(Self::new( + FrameData::Create(CreateFrame { created_address }), + depth, + Interpreter::new( + memory.clone(), + bytecode, + interpreter_input, + false, + false, + spec, + inputs.gas_limit, + ), + checkpoint, + precompile, + instructions, + memory, + ))) + } + + /// Make create frame. + #[inline] + pub fn make_eofcreate_frame( + context: &mut CTX, + depth: usize, + memory: Rc>, + inputs: &EOFCreateInputs, + precompile: PRECOMPILE, + instructions: INSTRUCTION, + ) -> Result, ERROR> { + let spec = context.cfg().spec().into(); + let return_error = |e| { + Ok(FrameOrResultGen::Result(FrameResult::EOFCreate( + CreateOutcome { + result: InterpreterResult { + result: e, + gas: Gas::new(inputs.gas_limit), + output: Bytes::new(), + }, + address: None, + }, + ))) + }; + + let (input, initcode, created_address) = match &inputs.kind { + EOFCreateKind::Opcode { + initcode, + input, + created_address, + } => (input.clone(), initcode.clone(), Some(*created_address)), + EOFCreateKind::Tx { initdata } => { + // decode eof and init code. + // TODO handle inc_nonce handling more gracefully. + let Ok((eof, input)) = Eof::decode_dangling(initdata.clone()) else { + context.journal().inc_account_nonce(inputs.caller)?; + return return_error(InstructionResult::InvalidEOFInitCode); + }; + + if eof.validate().is_err() { + // TODO (EOF) new error type. + context.journal().inc_account_nonce(inputs.caller)?; + return return_error(InstructionResult::InvalidEOFInitCode); + } + + // Use nonce from tx to calculate address. + let tx = context.tx().common_fields(); + let create_address = tx.caller().create(tx.nonce()); + + (input, eof, Some(create_address)) + } + }; + + // Check depth + if depth > CALL_STACK_LIMIT as usize { + return return_error(InstructionResult::CallTooDeep); + } + + // Fetch balance of caller. + let caller_balance = context + .journal() + .load_account(inputs.caller)? + .map(|a| a.info.balance); + + // Check if caller has enough balance to send to the created contract. + if caller_balance.data < inputs.value { + return return_error(InstructionResult::OutOfFunds); + } + + // Increase nonce of caller and check if it overflows + let Some(nonce) = context.journal().inc_account_nonce(inputs.caller)? else { + // can't happen on mainnet. + return return_error(InstructionResult::Return); + }; + let old_nonce = nonce - 1; + + let created_address = created_address.unwrap_or_else(|| inputs.caller.create(old_nonce)); + + // created address is not allowed to be a precompile. + if precompile.contains(&created_address) { + return return_error(InstructionResult::CreateCollision); + } + + // Load account so it needs to be marked as warm for access list. + context.journal().load_account(created_address)?; + + // create account, transfer funds and make the journal checkpoint. + let checkpoint = match context.journal().create_account_checkpoint( + inputs.caller, + created_address, + inputs.value, + spec, + ) { + Ok(checkpoint) => checkpoint, + Err(e) => return return_error(e.into()), + }; + + let interpreter_input = InputsImpl { + target_address: created_address, + caller_address: inputs.caller, + input, + call_value: inputs.value, + }; + + Ok(FrameOrResultGen::Frame(Self::new( + FrameData::EOFCreate(EOFCreateFrame { created_address }), + depth, + Interpreter::new( + memory.clone(), + Bytecode::Eof(Arc::new(initcode)), + interpreter_input, + false, + true, + spec, + inputs.gas_limit, + ), + checkpoint, + precompile, + instructions, + memory, + ))) + } + + pub fn init_with_context( + depth: usize, + frame_init: FrameInput, + memory: Rc>, + precompile: PRECOMPILE, + instructions: INSTRUCTION, + context: &mut CTX, + ) -> Result, ERROR> { + match frame_init { + FrameInput::Call(inputs) => { + Self::make_call_frame(context, depth, memory, &inputs, precompile, instructions) + } + FrameInput::Create(inputs) => { + Self::make_create_frame(context, depth, memory, &inputs, precompile, instructions) + } + FrameInput::EOFCreate(inputs) => Self::make_eofcreate_frame( + context, + depth, + memory, + &inputs, + precompile, + instructions, + ), + } + } +} + +impl Frame + for EthFrame, PRECOMPILE, INSTRUCTION> +where + CTX: EthFrameContext, + ERROR: EthFrameError, + PRECOMPILE: PrecompileProvider, + INSTRUCTION: InstructionProvider, Host = CTX>, +{ + type Context = CTX; + type Error = ERROR; + type FrameInit = FrameInput; + type FrameResult = FrameResult; + + fn init_first( + context: &mut Self::Context, + frame_input: Self::FrameInit, + ) -> Result, Self::Error> { + let memory = Rc::new(RefCell::new(SharedMemory::new())); + let precompiles = PRECOMPILE::new(context); + let instructions = INSTRUCTION::new(context); + + // load precompiles addresses as warm. + for address in precompiles.warm_addresses() { + context.journal().warm_account(address); + } + + memory.borrow_mut().new_context(); + Self::init_with_context(0, frame_input, memory, precompiles, instructions, context) + } + + fn init( + &self, + context: &mut CTX, + frame_init: Self::FrameInit, + ) -> Result, Self::Error> { + self.memory.borrow_mut().new_context(); + Self::init_with_context( + self.depth + 1, + frame_init, + self.memory.clone(), + self.precompiles.clone(), + self.instructions.clone(), + context, + ) + } + + fn run( + &mut self, + context: &mut Self::Context, + ) -> Result, Self::Error> { + let spec = context.cfg().spec().into(); + + // run interpreter + let next_action = self.interpreter.run(self.instructions.table(), context); + + let mut interpreter_result = match next_action { + InterpreterAction::NewFrame(new_frame) => { + return Ok(FrameOrResultGen::Frame(new_frame)) + } + InterpreterAction::Return { result } => result, + InterpreterAction::None => unreachable!("InterpreterAction::None is not expected"), + }; + + // Handle return from frame + let result = match &self.data { + FrameData::Call(frame) => { + // return_call + // revert changes or not. + if interpreter_result.result.is_ok() { + context.journal().checkpoint_commit(); + } else { + context.journal().checkpoint_revert(self.checkpoint); + } + FrameOrResultGen::Result(FrameResult::Call(CallOutcome::new( + interpreter_result, + frame.return_memory_range.clone(), + ))) + } + FrameData::Create(frame) => { + let max_code_size = context.cfg().max_code_size(); + return_create( + context.journal(), + self.checkpoint, + &mut interpreter_result, + frame.created_address, + max_code_size, + spec, + ); + + FrameOrResultGen::Result(FrameResult::Create(CreateOutcome::new( + interpreter_result, + Some(frame.created_address), + ))) + } + FrameData::EOFCreate(frame) => { + let max_code_size = context.cfg().max_code_size(); + return_eofcreate( + context.journal(), + self.checkpoint, + &mut interpreter_result, + frame.created_address, + max_code_size, + ); + + FrameOrResultGen::Result(FrameResult::EOFCreate(CreateOutcome::new( + interpreter_result, + Some(frame.created_address), + ))) + } + }; + + Ok(result) + } + + fn return_result( + &mut self, + context: &mut Self::Context, + result: Self::FrameResult, + ) -> Result<(), Self::Error> { + self.memory.borrow_mut().free_context(); + context.take_error()?; + + // Insert result to the top frame. + match result { + FrameResult::Call(outcome) => { + let out_gas = outcome.gas(); + let ins_result = *outcome.instruction_result(); + let returned_len = outcome.result.output.len(); + + let interpreter = &mut self.interpreter; + let mem_length = outcome.memory_length(); + let mem_start = outcome.memory_start(); + *interpreter.return_data.buffer_mut() = outcome.result.output; + + let target_len = min(mem_length, returned_len); + + if ins_result == InstructionResult::FatalExternalError { + panic!("Fatal external error in insert_call_outcome"); + } + + let item = { + if interpreter.runtime_flag.is_eof() { + match ins_result { + return_ok!() => U256::ZERO, + return_revert!() => U256::from(1), + _ => U256::from(2), + } + } else if ins_result.is_ok() { + U256::from(1) + } else { + U256::ZERO + } + }; + // Safe to push without stack limit check + let _ = interpreter.stack.push(item); + + // return unspend gas. + if ins_result.is_ok_or_revert() { + interpreter.control.gas().erase_cost(out_gas.remaining()); + self.memory + .borrow_mut() + .set(mem_start, &interpreter.return_data.buffer()[..target_len]); + } + + if ins_result.is_ok() { + interpreter.control.gas().record_refund(out_gas.refunded()); + } + } + FrameResult::Create(outcome) => { + let instruction_result = *outcome.instruction_result(); + let interpreter = &mut self.interpreter; + + let buffer = interpreter.return_data.buffer_mut(); + if instruction_result == InstructionResult::Revert { + // Save data to return data buffer if the create reverted + *buffer = outcome.output().to_owned() + } else { + // Otherwise clear it. Note that RETURN opcode should abort. + buffer.clear(); + }; + + assert_ne!( + instruction_result, + InstructionResult::FatalExternalError, + "Fatal external error in insert_eofcreate_outcome" + ); + + let this_gas = interpreter.control.gas(); + if instruction_result.is_ok_or_revert() { + this_gas.erase_cost(outcome.gas().remaining()); + } + + let stack_item = if instruction_result.is_ok() { + this_gas.record_refund(outcome.gas().refunded()); + outcome.address.unwrap_or_default().into_word().into() + } else { + U256::ZERO + }; + + // Safe to push without stack limit check + let _ = interpreter.stack.push(stack_item); + } + FrameResult::EOFCreate(outcome) => { + let instruction_result = *outcome.instruction_result(); + let interpreter = &mut self.interpreter; + if instruction_result == InstructionResult::Revert { + // Save data to return data buffer if the create reverted + *interpreter.return_data.buffer_mut() = outcome.output().to_owned() + } else { + // Otherwise clear it. Note that RETURN opcode should abort. + interpreter.return_data.buffer_mut().clear(); + }; + + assert_ne!( + instruction_result, + InstructionResult::FatalExternalError, + "Fatal external error in insert_eofcreate_outcome" + ); + + let this_gas = interpreter.control.gas(); + if instruction_result.is_ok_or_revert() { + this_gas.erase_cost(outcome.gas().remaining()); + } + + let stack_item = if instruction_result.is_ok() { + this_gas.record_refund(outcome.gas().refunded()); + outcome.address.expect("EOF Address").into_word().into() + } else { + U256::ZERO + }; + + // Safe to push without stack limit check + let _ = interpreter.stack.push(stack_item); + } + } + + Ok(()) + } +} + +pub fn return_create( + journal: &mut Journal, + checkpoint: JournalCheckpoint, + interpreter_result: &mut InterpreterResult, + address: Address, + max_code_size: usize, + spec_id: SpecId, +) { + // if return is not ok revert and return. + if !interpreter_result.result.is_ok() { + journal.checkpoint_revert(checkpoint); + return; + } + // Host error if present on execution + // if ok, check contract creation limit and calculate gas deduction on output len. + // + // EIP-3541: Reject new contract code starting with the 0xEF byte + if spec_id.is_enabled_in(LONDON) && interpreter_result.output.first() == Some(&0xEF) { + journal.checkpoint_revert(checkpoint); + interpreter_result.result = InstructionResult::CreateContractStartingWithEF; + return; + } + + // EIP-170: Contract code size limit + // By default limit is 0x6000 (~25kb) + if spec_id.is_enabled_in(SPURIOUS_DRAGON) && interpreter_result.output.len() > max_code_size { + journal.checkpoint_revert(checkpoint); + interpreter_result.result = InstructionResult::CreateContractSizeLimit; + return; + } + let gas_for_code = interpreter_result.output.len() as u64 * gas::CODEDEPOSIT; + if !interpreter_result.gas.record_cost(gas_for_code) { + // record code deposit gas cost and check if we are out of gas. + // EIP-2 point 3: If contract creation does not have enough gas to pay for the + // final gas fee for adding the contract code to the state, the contract + // creation fails (i.e. goes out-of-gas) rather than leaving an empty contract. + if spec_id.is_enabled_in(HOMESTEAD) { + journal.checkpoint_revert(checkpoint); + interpreter_result.result = InstructionResult::OutOfGas; + return; + } else { + interpreter_result.output = Bytes::new(); + } + } + // if we have enough gas we can commit changes. + journal.checkpoint_commit(); + + // Do analysis of bytecode straight away. + let bytecode = Bytecode::new_legacy(interpreter_result.output.clone()); + + // set code + journal.set_code(address, bytecode); + + interpreter_result.result = InstructionResult::Return; +} + +pub fn return_eofcreate( + journal: &mut Journal, + checkpoint: JournalCheckpoint, + interpreter_result: &mut InterpreterResult, + address: Address, + max_code_size: usize, +) { + // Note we still execute RETURN opcode and return the bytes. + // In EOF those opcodes should abort execution. + // + // In RETURN gas is still protecting us from ddos and in oog, + // behaviour will be same as if it failed on return. + // + // Bytes of RETURN will drained in `insert_eofcreate_outcome`. + if interpreter_result.result != InstructionResult::ReturnContract { + journal.checkpoint_revert(checkpoint); + return; + } + + if interpreter_result.output.len() > max_code_size { + journal.checkpoint_revert(checkpoint); + interpreter_result.result = InstructionResult::CreateContractSizeLimit; + return; + } + + // deduct gas for code deployment. + let gas_for_code = interpreter_result.output.len() as u64 * gas::CODEDEPOSIT; + if !interpreter_result.gas.record_cost(gas_for_code) { + journal.checkpoint_revert(checkpoint); + interpreter_result.result = InstructionResult::OutOfGas; + return; + } + + journal.checkpoint_commit(); + + // decode bytecode has a performance hit, but it has reasonable restrains. + let bytecode = Eof::decode(interpreter_result.output.clone()).expect("Eof is already verified"); + + // eof bytecode is going to be hashed. + journal.set_code(address, Bytecode::Eof(Arc::new(bytecode))); +} + +pub trait EthFrameContext: + TransactionGetter + Host + ErrorGetter + BlockGetter + JournalStateGetter + CfgGetter +{ +} + +impl< + ERROR, + CTX: TransactionGetter + + ErrorGetter + + BlockGetter + + JournalStateGetter + + CfgGetter + + Host, + > EthFrameContext for CTX +{ +} + +pub trait EthFrameError: + From> + From +{ +} + +impl> + From> + EthFrameError for T +{ +} diff --git a/crates/handler/src/frame_data.rs b/crates/handler/src/frame_data.rs new file mode 100644 index 0000000000..1ad312e1ae --- /dev/null +++ b/crates/handler/src/frame_data.rs @@ -0,0 +1,145 @@ +use context_interface::result::Output; +use core::ops::Range; +use interpreter::{CallOutcome, CreateOutcome, Gas, InstructionResult, InterpreterResult}; +use primitives::Address; + +/// Call CallStackFrame. +//#[derive(Debug)] +//#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct CallFrame { + /// Call frame has return memory range where output will be stored. + pub return_memory_range: Range, +} + +//#[derive(Debug)] +//#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct CreateFrame { + /// Create frame has a created address. + pub created_address: Address, +} + +/// Eof Create Frame. +//#[derive(Debug)] +//#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct EOFCreateFrame { + pub created_address: Address, +} + +/// Call stack frame. +//#[derive(Debug)] +//#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum FrameData { + Call(CallFrame), + Create(CreateFrame), + EOFCreate(EOFCreateFrame), +} + +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Debug)] +pub enum FrameResult { + Call(CallOutcome), + Create(CreateOutcome), + EOFCreate(CreateOutcome), +} + +impl FrameResult { + /// Casts frame result to interpreter result. + #[inline] + pub fn into_interpreter_result(self) -> InterpreterResult { + match self { + FrameResult::Call(outcome) => outcome.result, + FrameResult::Create(outcome) => outcome.result, + FrameResult::EOFCreate(outcome) => outcome.result, + } + } + + /// Returns execution output. + #[inline] + pub fn output(&self) -> Output { + match self { + FrameResult::Call(outcome) => Output::Call(outcome.result.output.clone()), + FrameResult::Create(outcome) => { + Output::Create(outcome.result.output.clone(), outcome.address) + } + FrameResult::EOFCreate(outcome) => { + Output::Create(outcome.result.output.clone(), outcome.address) + } + } + } + + /// Returns reference to gas. + #[inline] + pub fn gas(&self) -> &Gas { + match self { + FrameResult::Call(outcome) => &outcome.result.gas, + FrameResult::Create(outcome) => &outcome.result.gas, + FrameResult::EOFCreate(outcome) => &outcome.result.gas, + } + } + + /// Returns mutable reference to interpreter result. + #[inline] + pub fn gas_mut(&mut self) -> &mut Gas { + match self { + FrameResult::Call(outcome) => &mut outcome.result.gas, + FrameResult::Create(outcome) => &mut outcome.result.gas, + FrameResult::EOFCreate(outcome) => &mut outcome.result.gas, + } + } + + /// Returns reference to interpreter result. + #[inline] + pub fn interpreter_result(&self) -> &InterpreterResult { + match self { + FrameResult::Call(outcome) => &outcome.result, + FrameResult::Create(outcome) => &outcome.result, + FrameResult::EOFCreate(outcome) => &outcome.result, + } + } + + /// Returns mutable reference to interpreter result. + #[inline] + pub fn interpreter_result_mut(&mut self) -> &InterpreterResult { + match self { + FrameResult::Call(outcome) => &mut outcome.result, + FrameResult::Create(outcome) => &mut outcome.result, + FrameResult::EOFCreate(outcome) => &mut outcome.result, + } + } + + /// Return Instruction result. + #[inline] + pub fn instruction_result(&self) -> InstructionResult { + self.interpreter_result().result + } +} + +impl FrameData { + pub fn new_create(created_address: Address) -> Self { + Self::Create(CreateFrame { created_address }) + } + + pub fn new_call(return_memory_range: Range) -> Self { + Self::Call(CallFrame { + return_memory_range, + }) + } + + /// Returns true if frame is call frame. + pub fn is_call(&self) -> bool { + matches!(self, Self::Call { .. }) + } + + /// Returns true if frame is create frame. + pub fn is_create(&self) -> bool { + matches!(self, Self::Create { .. }) + } + + /// Returns created address if frame is create otherwise returns None. + pub fn created_address(&self) -> Option
{ + match self { + Self::Create(create_frame) => Some(create_frame.created_address), + _ => None, + } + } +} diff --git a/crates/handler/src/lib.rs b/crates/handler/src/lib.rs new file mode 100644 index 0000000000..a1e8b1cf54 --- /dev/null +++ b/crates/handler/src/lib.rs @@ -0,0 +1,139 @@ +//! Optimism-specific constants, types, and helpers. +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(not(feature = "std"))] +extern crate alloc as std; + +// Mainnet related handlers. + +mod execution; +mod frame; +mod frame_data; +mod post_execution; +mod pre_execution; +mod precompile_provider; +mod validation; + +// Public exports + +pub use execution::{EthExecution, EthExecutionContext, EthExecutionError}; +pub use frame::{return_create, return_eofcreate, EthFrame, EthFrameContext, EthFrameError}; +pub use frame_data::{FrameData, FrameResult}; +pub use post_execution::{EthPostExecution, EthPostExecutionContext, EthPostExecutionError}; +pub use pre_execution::{ + apply_eip7702_auth_list, EthPreExecution, EthPreExecutionContext, EthPreExecutionError, +}; +use precompile::PrecompileErrors; +pub use precompile_provider::EthPrecompileProvider; +use primitives::Log; +use state::EvmState; +use std::vec::Vec; +pub use validation::{ + validate_eip4844_tx, validate_initial_tx_gas, validate_priority_fee_tx, + validate_tx_against_account, validate_tx_env, EthValidation, EthValidationContext, + EthValidationError, +}; + +// Imports + +use context_interface::{ + journaled_state::JournaledState, + result::{HaltReason, InvalidHeader, InvalidTransaction}, +}; +use context_interface::{ + BlockGetter, CfgGetter, ErrorGetter, JournalStateGetter, JournalStateGetterDBError, + TransactionGetter, +}; +use handler_interface::{ + ExecutionHandler, Handler, PostExecutionHandler, PreExecutionHandler, ValidationHandler, +}; +use interpreter::Host; + +#[derive(Default)] +pub struct EthHandler< + CTX, + ERROR, + VAL = EthValidation, + PREEXEC = EthPreExecution, + EXEC = EthExecution, + POSTEXEC = EthPostExecution, +> { + pub validation: VAL, + pub pre_execution: PREEXEC, + pub execution: EXEC, + pub post_execution: POSTEXEC, + _phantom: core::marker::PhantomData (CTX, ERROR)>, +} + +impl Default for EthHandler { + fn default() -> Self { + Self { + validation: EthValidation::new(), + pre_execution: EthPreExecution::new(), + execution: EthExecution::new(), + post_execution: EthPostExecution::new(), + _phantom: core::marker::PhantomData, + } + } +} + +impl + EthHandler +{ + pub fn new( + validation: VAL, + pre_execution: PREEXEC, + execution: EXEC, + post_execution: POSTEXEC, + ) -> Self { + Self { + validation, + pre_execution, + execution, + post_execution, + _phantom: core::marker::PhantomData, + } + } +} + +impl Handler + for EthHandler +where + CTX: TransactionGetter + + BlockGetter + + JournalStateGetter + + CfgGetter + + ErrorGetter + + JournalStateGetter)>> + + Host, + ERROR: From + + From + + From> + + From, + VAL: ValidationHandler, + PREEXEC: PreExecutionHandler, + EXEC: ExecutionHandler, + POSTEXEC: PostExecutionHandler, +{ + type Validation = VAL; + type PreExecution = PREEXEC; + type Execution = EXEC; + type PostExecution = POSTEXEC; + + fn validation(&mut self) -> &mut Self::Validation { + &mut self.validation + } + + fn pre_execution(&mut self) -> &mut Self::PreExecution { + &mut self.pre_execution + } + + fn execution(&mut self) -> &mut Self::Execution { + &mut self.execution + } + + fn post_execution(&mut self) -> &mut Self::PostExecution { + &mut self.post_execution + } +} diff --git a/crates/handler/src/post_execution.rs b/crates/handler/src/post_execution.rs new file mode 100644 index 0000000000..3fe4f445bd --- /dev/null +++ b/crates/handler/src/post_execution.rs @@ -0,0 +1,197 @@ +use context_interface::{ + journaled_state::JournaledState, + result::{ExecutionResult, HaltReasonTrait, ResultAndState}, + Block, BlockGetter, Cfg, CfgGetter, ErrorGetter, JournalStateGetter, JournalStateGetterDBError, + Transaction, TransactionGetter, +}; +use handler_interface::PostExecutionHandler; +use interpreter::SuccessOrHalt; +use primitives::{Log, U256}; +use specification::hardfork::SpecId; +use state::EvmState; +use std::{boxed::Box, vec::Vec}; + +use super::frame_data::FrameResult; + +#[derive(Default)] +pub struct EthPostExecution { + pub _phantom: core::marker::PhantomData<(CTX, ERROR, HALTREASON)>, +} + +impl EthPostExecution { + /// Create new instance of post execution handler. + pub fn new() -> Self { + Self { + _phantom: core::marker::PhantomData, + } + } + + /// Create new boxed instance of post execution handler. + /// + /// Boxed instance is useful to erase FORK type. + pub fn new_boxed() -> Box { + Box::new(Self::new()) + } +} + +impl PostExecutionHandler for EthPostExecution +where + CTX: EthPostExecutionContext, + ERROR: EthPostExecutionError, + HALTREASON: HaltReasonTrait, +{ + type Context = CTX; + type Error = ERROR; + type ExecResult = FrameResult; + type Output = ResultAndState; + + fn refund( + &self, + context: &mut Self::Context, + exec_result: &mut Self::ExecResult, + eip7702_refund: i64, + ) { + let gas = exec_result.gas_mut(); + gas.record_refund(eip7702_refund); + + // Calculate gas refund for transaction. + // If spec is set to london, it will decrease the maximum refund amount to 5th part of + // gas spend. (Before london it was 2th part of gas spend) + gas.set_final_refund(context.cfg().spec().into().is_enabled_in(SpecId::LONDON)); + } + + fn reimburse_caller( + &self, + context: &mut Self::Context, + exec_result: &mut Self::ExecResult, + ) -> Result<(), Self::Error> { + let basefee = *context.block().basefee(); + let caller = context.tx().common_fields().caller(); + let effective_gas_price = context.tx().effective_gas_price(basefee); + let gas = exec_result.gas(); + + // return balance of not spend gas. + let caller_account = context.journal().load_account(caller)?; + + let reimbursed = effective_gas_price * U256::from(gas.remaining() + gas.refunded() as u64); + caller_account.data.info.balance = + caller_account.data.info.balance.saturating_add(reimbursed); + + Ok(()) + } + + fn reward_beneficiary( + &self, + context: &mut Self::Context, + exec_result: &mut Self::ExecResult, + ) -> Result<(), Self::Error> { + let block = context.block(); + let tx = context.tx(); + let beneficiary = *block.beneficiary(); + let basefee = *block.basefee(); + let effective_gas_price = tx.effective_gas_price(basefee); + let gas = exec_result.gas(); + + // 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) { + effective_gas_price.saturating_sub(basefee) + } else { + effective_gas_price + }; + + let coinbase_account = context.journal().load_account(beneficiary)?; + + coinbase_account.data.mark_touch(); + coinbase_account.data.info.balance = + coinbase_account.data.info.balance.saturating_add( + coinbase_gas_price * U256::from(gas.spent() - gas.refunded() as u64), + ); + + Ok(()) + } + + fn output( + &self, + context: &mut Self::Context, + result: Self::ExecResult, + ) -> Result { + context.take_error()?; + + // used gas with refund calculated. + let gas_refunded = result.gas().refunded() as u64; + let final_gas_used = result.gas().spent() - gas_refunded; + let output = result.output(); + let instruction_result = result.into_interpreter_result(); + + // reset journal and return present state. + let (state, logs) = context.journal().finalize()?; + + let result = match SuccessOrHalt::::from(instruction_result.result) { + SuccessOrHalt::Success(reason) => ExecutionResult::Success { + reason, + gas_used: final_gas_used, + gas_refunded, + logs, + output, + }, + SuccessOrHalt::Revert => ExecutionResult::Revert { + gas_used: final_gas_used, + output: output.into_data(), + }, + SuccessOrHalt::Halt(reason) => ExecutionResult::Halt { + reason, + gas_used: final_gas_used, + }, + // Only two internal return flags. + flag @ (SuccessOrHalt::FatalExternalError | SuccessOrHalt::Internal(_)) => { + panic!( + "Encountered unexpected internal return flag: {:?} with instruction result: {:?}", + flag, instruction_result + ) + } + }; + + Ok(ResultAndState { result, state }) + } + + fn clear(&self, context: &mut Self::Context) { + // clear error and journaled state. + // TODO check effects of removal of take_error + // let _ = context.evm.take_error(); + context.journal().clear(); + } +} + +/// Trait for post execution context. +/// +/// TODO Generalize FinalOutput. +pub trait EthPostExecutionContext: + TransactionGetter + + ErrorGetter + + BlockGetter + + JournalStateGetter)>> + + CfgGetter +{ +} + +impl< + ERROR, + CTX: TransactionGetter + + ErrorGetter + + BlockGetter + + JournalStateGetter)>> + + CfgGetter, + > EthPostExecutionContext for CTX +{ +} + +pub trait EthPostExecutionError: + From> +{ +} + +impl>> + EthPostExecutionError for ERROR +{ +} diff --git a/crates/handler/src/pre_execution.rs b/crates/handler/src/pre_execution.rs new file mode 100644 index 0000000000..1235abdce1 --- /dev/null +++ b/crates/handler/src/pre_execution.rs @@ -0,0 +1,225 @@ +//! Handles related to the main function of the EVM. +//! +//! They handle initial setup of the EVM, call loop and the final return of the EVM + +use bytecode::Bytecode; +use context_interface::{ + journaled_state::JournaledState, + result::InvalidTransaction, + transaction::{ + eip7702::Authorization, AccessListTrait, Eip4844Tx, Eip7702Tx, Transaction, TransactionType, + }, + Block, BlockGetter, Cfg, CfgGetter, JournalStateGetter, JournalStateGetterDBError, + TransactionGetter, +}; +use handler_interface::PreExecutionHandler; +use primitives::{Address, BLOCKHASH_STORAGE_ADDRESS, U256}; +use specification::{eip7702, hardfork::SpecId}; +use std::{boxed::Box, vec::Vec}; + +#[derive(Default)] +pub struct EthPreExecution { + pub _phantom: core::marker::PhantomData<(CTX, ERROR)>, +} + +impl EthPreExecution { + pub fn new() -> Self { + Self { + _phantom: core::marker::PhantomData, + } + } + + pub fn new_boxed() -> Box { + Box::new(Self::new()) + } +} + +impl PreExecutionHandler for EthPreExecution +where + CTX: EthPreExecutionContext, + ERROR: EthPreExecutionError, +{ + type Context = CTX; + type Error = ERROR; + + fn load_accounts(&self, context: &mut Self::Context) -> Result<(), Self::Error> { + let spec = context.cfg().spec().into(); + // set journaling state flag. + context.journal().set_spec_id(spec); + + // load coinbase + // EIP-3651: Warm COINBASE. Starts the `COINBASE` address warm + if spec.is_enabled_in(SpecId::SHANGHAI) { + let coinbase = *context.block().beneficiary(); + context.journal().warm_account(coinbase); + } + + // Load blockhash storage address + // EIP-2935: Serve historical block hashes from state + if spec.is_enabled_in(SpecId::PRAGUE) { + context.journal().warm_account(BLOCKHASH_STORAGE_ADDRESS); + } + + // Load access list + if let Some(access_list) = context.tx().access_list().cloned() { + for access_list in access_list.iter() { + context.journal().warm_account_and_storage( + access_list.0, + access_list.1.map(|i| U256::from_be_bytes(i.0)), + )?; + } + }; + + Ok(()) + } + + fn apply_eip7702_auth_list(&self, context: &mut Self::Context) -> Result { + let spec = context.cfg().spec().into(); + if spec.is_enabled_in(SpecId::PRAGUE) { + apply_eip7702_auth_list::(context) + } else { + Ok(0) + } + } + + #[inline] + fn deduct_caller(&self, context: &mut Self::Context) -> Result<(), Self::Error> { + let basefee = *context.block().basefee(); + let blob_price = U256::from(context.block().blob_gasprice().unwrap_or_default()); + let effective_gas_price = context.tx().effective_gas_price(basefee); + // Subtract gas costs from the caller's account. + // We need to saturate the gas cost to prevent underflow in case that `disable_balance_check` is enabled. + let mut gas_cost = U256::from(context.tx().common_fields().gas_limit()) + .saturating_mul(effective_gas_price); + + // EIP-4844 + if context.tx().tx_type().into() == TransactionType::Eip4844 { + let blob_gas = U256::from(context.tx().eip4844().total_blob_gas()); + gas_cost = gas_cost.saturating_add(blob_price.saturating_mul(blob_gas)); + } + + let is_call = context.tx().kind().is_call(); + let caller = context.tx().common_fields().caller(); + + // load caller's account. + let caller_account = context.journal().load_account(caller)?.data; + // set new caller account balance. + caller_account.info.balance = caller_account.info.balance.saturating_sub(gas_cost); + + // bump the nonce for calls. Nonce for CREATE will be bumped in `handle_create`. + if is_call { + // Nonce is already checked + caller_account.info.nonce = caller_account.info.nonce.saturating_add(1); + } + + // touch account so we know it is changed. + caller_account.mark_touch(); + Ok(()) + } +} + +/// Apply EIP-7702 auth list and return number gas refund on already created accounts. +#[inline] +pub fn apply_eip7702_auth_list< + CTX: TransactionGetter + JournalStateGetter + CfgGetter, + ERROR: From + From>, +>( + context: &mut CTX, +) -> Result { + // return if there is no auth list. + let tx = context.tx(); + if tx.tx_type().into() != TransactionType::Eip7702 { + return Ok(0); + } + + struct Authorization { + authority: Option
, + address: Address, + nonce: u64, + chain_id: u64, + } + + let authorization_list = tx + .eip7702() + .authorization_list_iter() + .map(|a| Authorization { + authority: a.authority(), + address: a.address(), + nonce: a.nonce(), + chain_id: a.chain_id(), + }) + .collect::>(); + let chain_id = context.cfg().chain_id(); + + let mut refunded_accounts = 0; + for authorization in authorization_list { + // 1. recover authority and authorized addresses. + // authority = ecrecover(keccak(MAGIC || rlp([chain_id, address, nonce])), y_parity, r, s] + let Some(authority) = authorization.authority else { + continue; + }; + + // 2. Verify the chain id is either 0 or the chain's current ID. + if authorization.chain_id != 0 && authorization.chain_id != chain_id { + continue; + } + + // warm authority account and check nonce. + // 3. Add authority to accessed_addresses (as defined in EIP-2929.) + let mut authority_acc = context.journal().load_account_code(authority)?; + + // 4. Verify the code of authority is either empty or already delegated. + if let Some(bytecode) = &authority_acc.info.code { + // if it is not empty and it is not eip7702 + if !bytecode.is_empty() && !bytecode.is_eip7702() { + continue; + } + } + + // 5. Verify the nonce of authority is equal to nonce. + if authorization.nonce != authority_acc.info.nonce { + continue; + } + + // 6. Refund the sender PER_EMPTY_ACCOUNT_COST - PER_AUTH_BASE_COST gas if authority exists in the trie. + if !authority_acc.is_empty() { + refunded_accounts += 1; + } + + // 7. Set the code of authority to be 0xef0100 || address. This is a delegation designation. + let bytecode = Bytecode::new_eip7702(authorization.address); + authority_acc.info.code_hash = bytecode.hash_slow(); + authority_acc.info.code = Some(bytecode); + + // 8. Increase the nonce of authority by one. + authority_acc.info.nonce = authority_acc.info.nonce.saturating_add(1); + authority_acc.mark_touch(); + } + + let refunded_gas = + refunded_accounts * (eip7702::PER_EMPTY_ACCOUNT_COST - eip7702::PER_AUTH_BASE_COST); + + Ok(refunded_gas) +} + +pub trait EthPreExecutionContext: + TransactionGetter + BlockGetter + JournalStateGetter + CfgGetter +{ +} + +impl EthPreExecutionContext + for CTX +{ +} + +pub trait EthPreExecutionError: + From + From> +{ +} + +impl< + CTX: JournalStateGetter, + T: From + From>, + > EthPreExecutionError for T +{ +} diff --git a/crates/handler/src/precompile_provider.rs b/crates/handler/src/precompile_provider.rs new file mode 100644 index 0000000000..0382d1e33d --- /dev/null +++ b/crates/handler/src/precompile_provider.rs @@ -0,0 +1,81 @@ +use context_interface::{Cfg, CfgGetter}; +use handler_interface::PrecompileProvider; +use interpreter::{Gas, InstructionResult, InterpreterResult}; +use precompile::PrecompileErrors; +use precompile::{PrecompileSpecId, Precompiles}; +use primitives::{Address, Bytes}; + +pub struct EthPrecompileProvider { + pub precompiles: &'static Precompiles, + pub _phantom: core::marker::PhantomData<(CTX, ERROR)>, +} + +impl Clone for EthPrecompileProvider { + fn clone(&self) -> Self { + Self { + precompiles: self.precompiles, + _phantom: core::marker::PhantomData, + } + } +} + +impl PrecompileProvider for EthPrecompileProvider +where + CTX: CfgGetter, + ERROR: From, +{ + type Context = CTX; + type Error = ERROR; + + fn new(context: &mut Self::Context) -> Self { + let spec = context.cfg().spec().into(); + Self { + precompiles: Precompiles::new(PrecompileSpecId::from_spec_id(spec)), + _phantom: core::marker::PhantomData, + } + } + + fn run( + &mut self, + _context: &mut Self::Context, + address: &Address, + bytes: &Bytes, + gas_limit: u64, + ) -> Result, Self::Error> { + let Some(precompile) = self.precompiles.get(address) else { + return Ok(None); + }; + + let mut result = InterpreterResult { + result: InstructionResult::Return, + gas: Gas::new(gas_limit), + output: Bytes::new(), + }; + + match (*precompile)(bytes, gas_limit) { + Ok(output) => { + let underflow = result.gas.record_cost(output.gas_used); + assert!(underflow, "Gas underflow is not possible"); + result.result = InstructionResult::Return; + result.output = output.bytes; + } + Err(PrecompileErrors::Error(e)) => { + result.result = if e.is_oog() { + InstructionResult::PrecompileOOG + } else { + InstructionResult::PrecompileError + }; + } + Err(err @ PrecompileErrors::Fatal { .. }) => return Err(err.into()), + } + Ok(Some(result)) + } + + fn warm_addresses(&self) -> impl Iterator { + self.precompiles.addresses().cloned() + } + + fn contains(&self, address: &Address) -> bool { + self.precompiles.contains(address) + } +} diff --git a/crates/revm/src/handler/mainnet/validation.rs b/crates/handler/src/validation.rs similarity index 51% rename from crates/revm/src/handler/mainnet/validation.rs rename to crates/handler/src/validation.rs index 233a1cb5f0..476531175c 100644 --- a/crates/revm/src/handler/mainnet/validation.rs +++ b/crates/handler/src/validation.rs @@ -1,55 +1,83 @@ +use context_interface::{ + journaled_state::JournaledState, + result::{InvalidHeader, InvalidTransaction}, + transaction::{ + eip7702::Authorization, Eip1559CommonTxFields, Eip2930Tx, Eip4844Tx, Eip7702Tx, LegacyTx, + Transaction, TransactionType, + }, + Block, BlockGetter, Cfg, CfgGetter, JournalStateGetter, JournalStateGetterDBError, + TransactionGetter, +}; use core::cmp::{self, Ordering}; - -use crate::{Context, EvmWiring}; +use handler_interface::ValidationHandler; use interpreter::gas; use primitives::{B256, U256}; -use specification::{ - constants::MAX_INITCODE_SIZE, - eip4844, - hardfork::{Spec, SpecId}, -}; +use specification::{eip4844, hardfork::SpecId}; use state::Account; use std::boxed::Box; -use transaction::{ - eip7702::Authorization, Eip1559CommonTxFields, Eip2930Tx, Eip4844Tx, Eip7702Tx, LegacyTx, - Transaction, -}; -use wiring::{ - default::{CfgEnv, EnvWiring}, - result::{EVMError, EVMResultGeneric, InvalidHeader, InvalidTransaction}, - Block, TransactionType, -}; -/// Validate environment (block and transaction) for the mainnet. -pub fn validate_env( - env: &EnvWiring, -) -> EVMResultGeneric<(), EvmWiringT> +pub struct EthValidation { + pub _phantom: core::marker::PhantomData (CTX, ERROR)>, +} + +impl Default for EthValidation { + fn default() -> Self { + Self { + _phantom: core::marker::PhantomData, + } + } +} + +impl EthValidation { + pub fn new() -> Self { + Self { + _phantom: core::marker::PhantomData, + } + } + + pub fn new_boxed() -> Box { + Box::new(Self::new()) + } +} + +impl ValidationHandler for EthValidation where - ::TransactionError: From, + CTX: EthValidationContext, + ERROR: From + From + From>, { - // Important: validate block before tx as some field are used in transaction validation. - validate_block_env::(&env.block).map_err(EVMError::Header)?; + type Context = CTX; + type Error = ERROR; + + fn validate_env(&self, context: &Self::Context) -> Result<(), Self::Error> { + let spec = context.cfg().spec().into(); + // `prevrandao` is required for the merge + if spec.is_enabled_in(SpecId::MERGE) && context.block().prevrandao().is_none() { + return Err(InvalidHeader::PrevrandaoNotSet.into()); + } + // `excess_blob_gas` is required for Cancun + if spec.is_enabled_in(SpecId::CANCUN) + && context.block().blob_excess_gas_and_price().is_none() + { + return Err(InvalidHeader::ExcessBlobGasNotSet.into()); + } + validate_tx_env::<&Self::Context, InvalidTransaction>(context, spec).map_err(Into::into) + } - // validate transaction. - validate_tx_env::(&env.tx, &env.block, &env.cfg) - .map_err(|e| EVMError::Transaction(e.into()))?; - Ok(()) -} + fn validate_tx_against_state(&self, context: &mut Self::Context) -> Result<(), Self::Error> { + let tx_caller = context.tx().common_fields().caller(); -/// Validate the block environment. -#[inline] -pub fn validate_block_env( - block: &EvmWiringT::Block, -) -> Result<(), InvalidHeader> { - // `prevrandao` is required for the merge - if SPEC::enabled(SpecId::MERGE) && block.prevrandao().is_none() { - return Err(InvalidHeader::PrevrandaoNotSet); + // load acc + let account = &mut context.journal().load_account_code(tx_caller)?; + let account = account.data.clone(); + + validate_tx_against_account::(&account, context) } - // `excess_blob_gas` is required for Cancun - if SPEC::enabled(SpecId::CANCUN) && block.blob_excess_gas_and_price().is_none() { - return Err(InvalidHeader::ExcessBlobGasNotSet); + + fn validate_initial_tx_gas(&self, context: &Self::Context) -> Result { + let spec = context.cfg().spec().into(); + validate_initial_tx_gas::<&Self::Context, InvalidTransaction>(context, spec) + .map_err(Into::into) } - Ok(()) } /// Validate transaction that has EIP-1559 priority fee @@ -112,64 +140,66 @@ pub fn validate_eip4844_tx( } /// Validate transaction against block and configuration for mainnet. -pub fn validate_tx_env( - tx: &EvmWiringT::Transaction, - block: &EvmWiringT::Block, - cfg: &CfgEnv, -) -> Result<(), InvalidTransaction> { +pub fn validate_tx_env( + context: CTX, + spec_id: SpecId, +) -> Result<(), Error> +where + Error: From, +{ // Check if the transaction's chain id is correct - let common_field = tx.common_fields(); - let tx_type = tx.tx_type().into(); + let common_field = context.tx().common_fields(); + let tx_type = context.tx().tx_type().into(); - let base_fee = if cfg.is_base_fee_check_disabled() { + let base_fee = if context.cfg().is_base_fee_check_disabled() { None } else { - Some(*block.basefee()) + Some(*context.block().basefee()) }; match tx_type { TransactionType::Legacy => { - let tx = tx.legacy(); + let tx = context.tx().legacy(); // check chain_id only if it is present in the legacy transaction. // EIP-155: Simple replay attack protection if let Some(chain_id) = tx.chain_id() { - if chain_id != cfg.chain_id { - return Err(InvalidTransaction::InvalidChainId); + if chain_id != context.cfg().chain_id() { + return Err(InvalidTransaction::InvalidChainId.into()); } } // gas price must be at least the basefee. if let Some(base_fee) = base_fee { if U256::from(tx.gas_price()) < base_fee { - return Err(InvalidTransaction::GasPriceLessThanBasefee); + return Err(InvalidTransaction::GasPriceLessThanBasefee.into()); } } } TransactionType::Eip2930 => { // enabled in BERLIN hardfork - if !SPEC::enabled(SpecId::BERLIN) { - return Err(InvalidTransaction::Eip2930NotSupported); + if !spec_id.is_enabled_in(SpecId::BERLIN) { + return Err(InvalidTransaction::Eip2930NotSupported.into()); } - let tx = tx.eip2930(); + let tx = context.tx().eip2930(); - if cfg.chain_id != tx.chain_id() { - return Err(InvalidTransaction::InvalidChainId); + if context.cfg().chain_id() != tx.chain_id() { + return Err(InvalidTransaction::InvalidChainId.into()); } // gas price must be at least the basefee. if let Some(base_fee) = base_fee { if U256::from(tx.gas_price()) < base_fee { - return Err(InvalidTransaction::GasPriceLessThanBasefee); + return Err(InvalidTransaction::GasPriceLessThanBasefee.into()); } } } TransactionType::Eip1559 => { - if !SPEC::enabled(SpecId::LONDON) { - return Err(InvalidTransaction::Eip1559NotSupported); + if !spec_id.is_enabled_in(SpecId::LONDON) { + return Err(InvalidTransaction::Eip1559NotSupported.into()); } - let tx = tx.eip1559(); + let tx = context.tx().eip1559(); - if cfg.chain_id != tx.chain_id() { - return Err(InvalidTransaction::InvalidChainId); + if context.cfg().chain_id() != tx.chain_id() { + return Err(InvalidTransaction::InvalidChainId.into()); } validate_priority_fee_tx( @@ -179,13 +209,13 @@ pub fn validate_tx_env( )?; } TransactionType::Eip4844 => { - if !SPEC::enabled(SpecId::CANCUN) { - return Err(InvalidTransaction::Eip4844NotSupported); + if !spec_id.is_enabled_in(SpecId::CANCUN) { + return Err(InvalidTransaction::Eip4844NotSupported.into()); } - let tx = tx.eip4844(); + let tx = context.tx().eip4844(); - if cfg.chain_id != tx.chain_id() { - return Err(InvalidTransaction::InvalidChainId); + if context.cfg().chain_id() != tx.chain_id() { + return Err(InvalidTransaction::InvalidChainId.into()); } validate_priority_fee_tx( @@ -197,18 +227,18 @@ pub fn validate_tx_env( validate_eip4844_tx( tx.blob_versioned_hashes(), tx.max_fee_per_blob_gas(), - block.blob_gasprice().unwrap_or_default(), + context.block().blob_gasprice().unwrap_or_default(), )?; } TransactionType::Eip7702 => { // check if EIP-7702 transaction is enabled. - if !SPEC::enabled(SpecId::PRAGUE) { - return Err(InvalidTransaction::Eip7702NotSupported); + if !spec_id.is_enabled_in(SpecId::PRAGUE) { + return Err(InvalidTransaction::Eip7702NotSupported.into()); } - let tx = tx.eip7702(); + let tx = context.tx().eip7702(); - if cfg.chain_id != tx.chain_id() { - return Err(InvalidTransaction::InvalidChainId); + if context.cfg().chain_id() != tx.chain_id() { + return Err(InvalidTransaction::InvalidChainId.into()); } validate_priority_fee_tx( @@ -220,13 +250,13 @@ pub fn validate_tx_env( let auth_list_len = tx.authorization_list_len(); // The transaction is considered invalid if the length of authorization_list is zero. if auth_list_len == 0 { - return Err(InvalidTransaction::EmptyAuthorizationList); + return Err(InvalidTransaction::EmptyAuthorizationList.into()); } // TODO temporary here as newest EIP have removed this check. for auth in tx.authorization_list_iter() { if auth.is_invalid() { - return Err(InvalidTransaction::Eip7702NotSupported); + return Err(InvalidTransaction::Eip7702NotSupported.into()); } } } @@ -236,20 +266,17 @@ pub fn validate_tx_env( }; // Check if gas_limit is more than block_gas_limit - if !cfg.is_block_gas_limit_disabled() - && U256::from(common_field.gas_limit()) > *block.gas_limit() + if !context.cfg().is_block_gas_limit_disabled() + && U256::from(common_field.gas_limit()) > *context.block().gas_limit() { - return Err(InvalidTransaction::CallerGasLimitMoreThanBlock); + return Err(InvalidTransaction::CallerGasLimitMoreThanBlock.into()); } // EIP-3860: Limit and meter initcode - if SPEC::enabled(SpecId::SHANGHAI) && tx.kind().is_create() { - let max_initcode_size = cfg - .limit_contract_code_size - .map(|limit| limit.saturating_mul(2)) - .unwrap_or(MAX_INITCODE_SIZE); - if tx.common_fields().input().len() > max_initcode_size { - return Err(InvalidTransaction::CreateInitCodeSizeLimit); + if spec_id.is_enabled_in(SpecId::SHANGHAI) && context.tx().kind().is_create() { + let max_initcode_size = context.cfg().max_code_size().saturating_mul(2); + if context.tx().common_fields().input().len() > max_initcode_size { + return Err(InvalidTransaction::CreateInitCodeSizeLimit.into()); } } @@ -257,123 +284,92 @@ pub fn validate_tx_env( } /// Validate account against the transaction. -pub fn validate_tx_against_account( - account: &mut Account, - tx: &EvmWiringT::Transaction, - cfg: &CfgEnv, -) -> Result<(), InvalidTransaction> +#[inline] +pub fn validate_tx_against_account( + account: &Account, + context: &CTX, +) -> Result<(), ERROR> where - ::TransactionError: From, + ERROR: From, { - let tx_type = tx.tx_type().into(); + let tx_type = context.tx().tx_type().into(); // EIP-3607: Reject transactions from senders with deployed code // This EIP is introduced after london but there was no collision in past // so we can leave it enabled always - if !cfg.is_eip3607_disabled() { + if !context.cfg().is_eip3607_disabled() { let bytecode = &account.info.code.as_ref().unwrap(); // allow EOAs whose code is a valid delegation designation, // i.e. 0xef0100 || address, to continue to originate transactions. if !bytecode.is_empty() && !bytecode.is_eip7702() { - return Err(InvalidTransaction::RejectCallerWithCode); + return Err(InvalidTransaction::RejectCallerWithCode.into()); } } // Check that the transaction's nonce is correct - if !cfg.is_nonce_check_disabled() { - let tx = tx.common_fields().nonce(); + if !context.cfg().is_nonce_check_disabled() { + let tx = context.tx().common_fields().nonce(); let state = account.info.nonce; match tx.cmp(&state) { Ordering::Greater => { - return Err(InvalidTransaction::NonceTooHigh { tx, state }); + return Err(InvalidTransaction::NonceTooHigh { tx, state }.into()); } Ordering::Less => { - return Err(InvalidTransaction::NonceTooLow { tx, state }); + return Err(InvalidTransaction::NonceTooLow { tx, state }.into()); } _ => {} } } // gas_limit * max_fee + value - let mut balance_check = U256::from(tx.common_fields().gas_limit()) - .checked_mul(U256::from(tx.max_fee())) - .and_then(|gas_cost| gas_cost.checked_add(tx.common_fields().value())) + let mut balance_check = U256::from(context.tx().common_fields().gas_limit()) + .checked_mul(U256::from(context.tx().max_fee())) + .and_then(|gas_cost| gas_cost.checked_add(context.tx().common_fields().value())) .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?; if tx_type == TransactionType::Eip4844 { - let tx = tx.eip4844(); - // if the tx is not a blob tx, this will be None, so we add zero + let tx = context.tx().eip4844(); let data_fee = tx.calc_max_data_fee(); balance_check = balance_check - .checked_add(U256::from(data_fee)) + .checked_add(data_fee) .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?; } // Check if account has enough balance for `gas_limit * max_fee`` and value transfer. // Transfer will be done inside `*_inner` functions. - if balance_check > account.info.balance { - if cfg.is_balance_check_disabled() { - // Add transaction cost to balance to ensure execution doesn't fail. - account.info.balance = account.info.balance.saturating_add(balance_check); - } else { - return Err(InvalidTransaction::LackOfFundForMaxFee { - fee: Box::new(balance_check), - balance: Box::new(account.info.balance), - }); + if balance_check > account.info.balance && !context.cfg().is_balance_check_disabled() { + return Err(InvalidTransaction::LackOfFundForMaxFee { + fee: Box::new(balance_check), + balance: Box::new(account.info.balance), } + .into()); } Ok(()) } -/// Validates transaction against the state. -pub fn validate_tx_against_state( - context: &mut Context, -) -> EVMResultGeneric<(), EvmWiringT> -where - ::TransactionError: From, -{ - let tx_caller = context.evm.env.tx.common_fields().caller(); - // load acc - - let inner = &mut context.evm.inner; - - let caller_account = inner - .journaled_state - .load_code(tx_caller, &mut inner.db) - .map_err(EVMError::Database)?; - - validate_tx_against_account::( - caller_account.data, - &inner.env.tx, - &inner.env.cfg, - ) - .map_err(|e| EVMError::Transaction(e.into()))?; - - Ok(()) -} - /// Validate initial transaction gas. -pub fn validate_initial_tx_gas( - env: &EnvWiring, -) -> EVMResultGeneric +pub fn validate_initial_tx_gas( + env: TxGetter, + spec_id: SpecId, +) -> Result where - ::TransactionError: From, + Error: From, { - let tx_type = env.tx.tx_type().into(); + let tx_type = env.tx().tx_type().into(); let authorization_list_num = if tx_type == TransactionType::Eip7702 { - env.tx.eip7702().authorization_list_len() as u64 + env.tx().eip7702().authorization_list_len() as u64 } else { 0 }; - let common_fields = env.tx.common_fields(); - let is_create = env.tx.kind().is_create(); + let common_fields = env.tx().common_fields(); + let is_create = env.tx().kind().is_create(); let input = common_fields.input(); - let access_list = env.tx.access_list(); + let access_list = env.tx().access_list(); let initial_gas_spend = gas::validate_initial_tx_gas( - SPEC::SPEC_ID, + spec_id, input, is_create, access_list, @@ -382,9 +378,31 @@ where // Additional check to see if limit is big enough to cover initial gas. if initial_gas_spend > common_fields.gas_limit() { - return Err(EVMError::Transaction( - InvalidTransaction::CallGasCostMoreThanGasLimit.into(), - )); + return Err(InvalidTransaction::CallGasCostMoreThanGasLimit.into()); } Ok(initial_gas_spend) } + +/// Helper trait that summarizes ValidationHandler requirements from Context. +pub trait EthValidationContext: + TransactionGetter + BlockGetter + JournalStateGetter + CfgGetter +{ +} + +impl EthValidationContext + for T +{ +} + +/// Helper trait that summarizes all possible requirements by EthValidation. +pub trait EthValidationError: + From + From + From> +{ +} + +impl< + CTX: JournalStateGetter, + T: From + From + From>, + > EthValidationError for T +{ +} diff --git a/crates/inspector/Cargo.toml b/crates/inspector/Cargo.toml index 393b30659f..e5f7ea55f6 100644 --- a/crates/inspector/Cargo.toml +++ b/crates/inspector/Cargo.toml @@ -26,8 +26,8 @@ all = "warn" revm.workspace = true # mics -auto_impl = { version = "1.2", default-features = false } -derive-where = { version = "1.2.7", default-features = false } +auto_impl.workspace = true +derive-where.workspace = true # Optional serde = { version = "1.0", default-features = false, features = [ diff --git a/crates/inspector/src/customprinter.rs b/crates/inspector/src/customprinter.rs deleted file mode 100644 index d87096a42b..0000000000 --- a/crates/inspector/src/customprinter.rs +++ /dev/null @@ -1,162 +0,0 @@ -//! Custom print inspector, it has step level information of execution. -//! It is a great tool if some debugging is needed. - -use crate::{inspectors::GasInspector, Inspector}; -use revm::{ - bytecode::opcode::OpCode, - interpreter::{CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter}, - primitives::{Address, U256}, - EvmContext, EvmWiring, -}; - -/// Custom print [Inspector], it has step level information of execution. -/// -/// It is a great tool if some debugging is needed. -#[derive(Clone, Debug, Default)] -pub struct CustomPrintTracer { - gas_inspector: GasInspector, -} - -impl Inspector for CustomPrintTracer { - fn initialize_interp( - &mut self, - interp: &mut Interpreter, - context: &mut EvmContext, - ) { - self.gas_inspector.initialize_interp(interp, context); - } - - // get opcode by calling `interp.contract.opcode(interp.program_counter())`. - // all other information can be obtained from interp. - fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { - let opcode = interp.current_opcode(); - let name = OpCode::name_by_op(opcode); - - let gas_remaining = self.gas_inspector.gas_remaining(); - - let memory_size = interp.shared_memory.len(); - - println!( - "depth:{}, PC:{}, gas:{:#x}({}), OPCODE: {:?}({:?}) refund:{:#x}({}) Stack:{:?}, Data size:{}", - context.journaled_state.depth(), - interp.program_counter(), - gas_remaining, - gas_remaining, - name, - opcode, - interp.gas.refunded(), - interp.gas.refunded(), - interp.stack.data(), - memory_size, - ); - - self.gas_inspector.step(interp, context); - } - - fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { - self.gas_inspector.step_end(interp, context); - } - - fn call_end( - &mut self, - context: &mut EvmContext, - inputs: &CallInputs, - outcome: CallOutcome, - ) -> CallOutcome { - self.gas_inspector.call_end(context, inputs, outcome) - } - - fn create_end( - &mut self, - context: &mut EvmContext, - inputs: &CreateInputs, - outcome: CreateOutcome, - ) -> CreateOutcome { - self.gas_inspector.create_end(context, inputs, outcome) - } - - fn call( - &mut self, - _context: &mut EvmContext, - inputs: &mut CallInputs, - ) -> Option { - println!( - "SM Address: {:?}, caller:{:?},target:{:?} is_static:{:?}, transfer:{:?}, input_size:{:?}", - inputs.bytecode_address, - inputs.caller, - inputs.target_address, - inputs.is_static, - inputs.value, - inputs.input.len(), - ); - None - } - - fn create( - &mut self, - _context: &mut EvmContext, - inputs: &mut CreateInputs, - ) -> Option { - println!( - "CREATE CALL: caller:{:?}, scheme:{:?}, value:{:?}, init_code:{:?}, gas:{:?}", - inputs.caller, inputs.scheme, inputs.value, inputs.init_code, inputs.gas_limit - ); - None - } - - fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) { - println!( - "SELFDESTRUCT: contract: {:?}, refund target: {:?}, value {:?}", - contract, target, value - ); - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::inspector_handle_register; - - use database::InMemoryDB; - use revm::{ - bytecode::Bytecode, - primitives::{address, bytes, keccak256, Bytes, TxKind, U256}, - specification::hardfork::SpecId, - state::AccountInfo, - wiring::EthereumWiring, - Evm, - }; - - #[test] - fn gas_calculation_underflow() { - let callee = address!("5fdcca53617f4d2b9134b29090c87d01058e27e9"); - - // https://github.com/bluealloy/revm/issues/277 - // checks this use case - let mut evm = Evm::>::builder() - .with_default_db() - .with_default_ext_ctx() - .modify_db(|db| { - let code = bytes!("5b597fb075978b6c412c64d169d56d839a8fe01b3f4607ed603b2c78917ce8be1430fe6101e8527ffe64706ecad72a2f5c97a95e006e279dc57081902029ce96af7edae5de116fec610208527f9fc1ef09d4dd80683858ae3ea18869fe789ddc365d8d9d800e26c9872bac5e5b6102285260276102485360d461024953601661024a53600e61024b53607d61024c53600961024d53600b61024e5360b761024f5360596102505360796102515360a061025253607261025353603a6102545360fb61025553601261025653602861025753600761025853606f61025953601761025a53606161025b53606061025c5360a661025d53602b61025e53608961025f53607a61026053606461026153608c6102625360806102635360d56102645360826102655360ae61026653607f6101e8610146610220677a814b184591c555735fdcca53617f4d2b9134b29090c87d01058e27e962047654f259595947443b1b816b65cdb6277f4b59c10a36f4e7b8658f5a5e6f5561"); - let info = AccountInfo { - balance: "0x100c5d668240db8e00".parse().unwrap(), - code_hash: keccak256(&code), - code: Some(Bytecode::new_raw(code.clone())), - nonce: 1, - }; - db.insert_account_info(callee, info); - }) - .modify_tx_env(|tx| { - tx.caller = address!("5fdcca53617f4d2b9134b29090c87d01058e27e0"); - tx.transact_to = TxKind::Call(callee); - tx.data = Bytes::new(); - tx.value = U256::ZERO; - tx.gas_limit = 100_000; - }) - .with_spec_id(SpecId::BERLIN) - .append_handler_register(inspector_handle_register) - .build(); - - evm.transact().expect("Transaction to work"); - } -} diff --git a/crates/inspector/src/eip3155.rs b/crates/inspector/src/eip3155.rs index 8fd1c22323..fe5ad50968 100644 --- a/crates/inspector/src/eip3155.rs +++ b/crates/inspector/src/eip3155.rs @@ -2,26 +2,31 @@ use crate::{inspectors::GasInspector, Inspector}; use derive_where::derive_where; use revm::{ bytecode::opcode::OpCode, + context::Cfg, + context_interface::{CfgGetter, JournalStateGetter, Transaction, TransactionGetter}, interpreter::{ - CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter, InterpreterResult, + interpreter_types::{Jumps, LoopControl, MemoryTrait, StackTrait}, + CallInputs, CallOutcome, CreateInputs, CreateOutcome, EOFCreateInputs, Interpreter, + InterpreterResult, InterpreterTypes, Stack, }, primitives::{hex, HashMap, B256, U256}, - wiring::Transaction, - EvmContext, EvmWiring, }; use serde::Serialize; use std::io::Write; /// [EIP-3155](https://eips.ethereum.org/EIPS/eip-3155) tracer [Inspector]. -#[derive_where(Debug)] -pub struct TracerEip3155 { +#[derive_where(Debug; CTX, INTR)] +pub struct TracerEip3155 { #[derive_where(skip)] output: Box, - gas_inspector: GasInspector, + gas_inspector: GasInspector, /// Print summary of the execution. print_summary: bool, + /// depth + depth: usize, + stack: Vec, pc: usize, opcode: u8, @@ -99,7 +104,11 @@ struct Summary { fork: Option, } -impl TracerEip3155 { +impl TracerEip3155 +where + CTX: CfgGetter + TransactionGetter, + INTR:, +{ /// Sets the writer to use for the output. pub fn set_writer(&mut self, writer: Box) { self.output = writer; @@ -119,7 +128,7 @@ impl TracerEip3155 { skip, .. } = self; - *gas_inspector = GasInspector::default(); + *gas_inspector = GasInspector::new(); stack.clear(); *pc = 0; *opcode = 0; @@ -128,15 +137,14 @@ impl TracerEip3155 { *mem_size = 0; *skip = false; } -} -impl TracerEip3155 { pub fn new(output: Box) -> Self { Self { output, - gas_inspector: GasInspector::default(), + gas_inspector: GasInspector::new(), print_summary: true, include_memory: false, + depth: 0, stack: Default::default(), memory: Default::default(), pc: 0, @@ -166,54 +174,63 @@ impl TracerEip3155 { self.output.flush() } - fn print_summary( - &mut self, - result: &InterpreterResult, - context: &mut EvmContext, - ) { + fn print_summary(&mut self, result: &InterpreterResult, context: &mut CTX) { if self.print_summary { - let spec_name: &str = context.spec_id().into(); + let spec = context.cfg().spec().into(); + let gas_limit = context.tx().common_fields().gas_limit(); let value = Summary { state_root: B256::ZERO.to_string(), output: result.output.to_string(), - gas_used: hex_number( - context.inner.env().tx.common_fields().gas_limit() - - self.gas_inspector.gas_remaining(), - ), + gas_used: hex_number(gas_limit - self.gas_inspector.gas_remaining()), pass: result.is_ok(), time: None, - fork: Some(spec_name.to_string()), + fork: Some(spec.to_string()), }; let _ = self.write_value(&value); } } } -impl Inspector for TracerEip3155 { - fn initialize_interp( - &mut self, - interp: &mut Interpreter, - context: &mut EvmContext, - ) { +pub trait CloneStack { + fn clone_from(&self) -> Vec; +} + +impl CloneStack for Stack { + fn clone_from(&self) -> Vec { + self.data().to_vec() + } +} + +impl Inspector for TracerEip3155 +where + CTX: CfgGetter + TransactionGetter + JournalStateGetter, + INTR: InterpreterTypes, +{ + type Context = CTX; + type InterpreterTypes = INTR; + + fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut CTX) { self.gas_inspector.initialize_interp(interp, context); } - fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { + fn step(&mut self, interp: &mut Interpreter, context: &mut CTX) { self.gas_inspector.step(interp, context); - self.stack.clone_from(interp.stack.data()); + self.stack = interp.stack.clone_from(); self.memory = if self.include_memory { - Some(hex::encode_prefixed(interp.shared_memory.context_memory())) + Some(hex::encode_prefixed( + interp.memory.slice(0..usize::MAX).as_ref(), + )) } else { None }; - self.pc = interp.program_counter(); - self.opcode = interp.current_opcode(); - self.mem_size = interp.shared_memory.len(); - self.gas = interp.gas.remaining(); - self.refunded = interp.gas.refunded(); + self.pc = interp.bytecode.pc(); + self.opcode = interp.bytecode.opcode(); + self.mem_size = interp.memory.size(); + self.gas = interp.control.gas().remaining(); + self.refunded = interp.control.gas().refunded(); } - fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { + fn step_end(&mut self, interp: &mut Interpreter, context: &mut CTX) { self.gas_inspector.step_end(interp, context); if self.skip { self.skip = false; @@ -226,14 +243,14 @@ impl Inspector for TracerEip3155 { gas: hex_number(self.gas), gas_cost: hex_number(self.gas_inspector.last_gas_cost()), stack: self.stack.iter().map(hex_number_u256).collect(), - depth: context.journaled_state.depth(), + depth: self.depth as u64, return_data: "0x".to_string(), refund: hex_number(self.refunded as u64), mem_size: self.mem_size.to_string(), op_name: OpCode::new(self.opcode).map(|i| i.as_str()), - error: if !interp.instruction_result.is_ok() { - Some(format!("{:?}", interp.instruction_result)) + error: if !interp.control.instruction_result().is_ok() { + Some(format!("{:?}", interp.control.instruction_result())) } else { None }, @@ -244,39 +261,55 @@ impl Inspector for TracerEip3155 { let _ = self.write_value(&value); } - fn call_end( + fn call(&mut self, _: &mut Self::Context, _: &mut CallInputs) -> Option { + self.depth += 1; + None + } + + fn create(&mut self, _: &mut Self::Context, _: &mut CreateInputs) -> Option { + self.depth += 1; + None + } + + fn eofcreate( &mut self, - context: &mut EvmContext, - inputs: &CallInputs, - outcome: CallOutcome, - ) -> CallOutcome { - let outcome = self.gas_inspector.call_end(context, inputs, outcome); + _: &mut Self::Context, + _: &mut EOFCreateInputs, + ) -> Option { + self.depth += 1; + None + } + + fn call_end(&mut self, context: &mut CTX, inputs: &CallInputs, outcome: &mut CallOutcome) { + self.gas_inspector.call_end(context, inputs, outcome); + self.depth -= 1; - if context.journaled_state.depth() == 0 { + if self.depth == 0 { self.print_summary(&outcome.result, context); // clear the state if we are at the top level self.clear(); } - - outcome } fn create_end( &mut self, - context: &mut EvmContext, + context: &mut CTX, inputs: &CreateInputs, - outcome: CreateOutcome, - ) -> CreateOutcome { - let outcome = self.gas_inspector.create_end(context, inputs, outcome); + outcome: &mut CreateOutcome, + ) { + self.gas_inspector.create_end(context, inputs, outcome); + self.depth -= 1; - if context.journaled_state.depth() == 0 { + if self.depth == 0 { self.print_summary(&outcome.result, context); // clear the state if we are at the top level self.clear(); } + } - outcome + fn eofcreate_end(&mut self, _: &mut Self::Context, _: &EOFCreateInputs, _: &mut CreateOutcome) { + self.depth -= 1; } } diff --git a/crates/inspector/src/gas.rs b/crates/inspector/src/gas.rs index 31d887e31d..697d27892f 100644 --- a/crates/inspector/src/gas.rs +++ b/crates/inspector/src/gas.rs @@ -1,20 +1,27 @@ //! GasIspector. Helper Inspector to calculate gas for others. use crate::Inspector; -use revm::{ - interpreter::{CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter}, - EvmContext, EvmWiring, +use revm::interpreter::{ + interpreter_types::LoopControl, CallInputs, CallOutcome, CreateInputs, CreateOutcome, + Interpreter, InterpreterTypes, }; /// Helper [Inspector] that keeps track of gas. #[allow(dead_code)] -#[derive(Clone, Copy, Debug, Default)] -pub struct GasInspector { +#[derive(Clone, Copy, Debug)] +pub struct GasInspector { gas_remaining: u64, last_gas_cost: u64, + _phantom: core::marker::PhantomData<(CTX, INTR)>, } -impl GasInspector { +impl Default for GasInspector { + fn default() -> Self { + Self::new() + } +} + +impl GasInspector { pub fn gas_remaining(&self) -> u64 { self.gas_remaining } @@ -22,193 +29,200 @@ impl GasInspector { pub fn last_gas_cost(&self) -> u64 { self.last_gas_cost } + + pub fn new() -> Self { + Self { + gas_remaining: 0, + last_gas_cost: 0, + _phantom: core::marker::PhantomData, + } + } } -impl Inspector for GasInspector { +impl Inspector for GasInspector { + type Context = CTX; + type InterpreterTypes = INTR; + + #[inline] fn initialize_interp( &mut self, - interp: &mut Interpreter, - _context: &mut EvmContext, + interp: &mut Interpreter, + _: &mut Self::Context, ) { - self.gas_remaining = interp.gas.limit(); + self.gas_remaining = interp.control.gas().limit(); } - fn step(&mut self, interp: &mut Interpreter, _context: &mut EvmContext) { - self.gas_remaining = interp.gas.remaining(); + #[inline] + fn step(&mut self, interp: &mut Interpreter, _: &mut Self::Context) { + self.gas_remaining = interp.control.gas().remaining(); } - - fn step_end(&mut self, interp: &mut Interpreter, _context: &mut EvmContext) { - let remaining = interp.gas.remaining(); + #[inline] + fn step_end( + &mut self, + interp: &mut Interpreter, + _: &mut Self::Context, + ) { + let remaining = interp.control.gas().remaining(); self.last_gas_cost = self.gas_remaining.saturating_sub(remaining); self.gas_remaining = remaining; } - fn call_end( - &mut self, - _context: &mut EvmContext, - _inputs: &CallInputs, - mut outcome: CallOutcome, - ) -> CallOutcome { + #[inline] + fn call_end(&mut self, _: &mut Self::Context, _: &CallInputs, outcome: &mut CallOutcome) { if outcome.result.result.is_error() { outcome.result.gas.spend_all(); self.gas_remaining = 0; } - outcome } - fn create_end( - &mut self, - _context: &mut EvmContext, - _inputs: &CreateInputs, - mut outcome: CreateOutcome, - ) -> CreateOutcome { + #[inline] + fn create_end(&mut self, _: &mut Self::Context, _: &CreateInputs, outcome: &mut CreateOutcome) { if outcome.result.result.is_error() { outcome.result.gas.spend_all(); self.gas_remaining = 0; } - outcome } } -#[cfg(test)] -mod tests { - use super::*; - use crate::inspector_handle_register; - use database::BenchmarkDB; - use revm::{ - bytecode::{opcode, Bytecode}, - interpreter::Interpreter, - primitives::{address, Bytes, Log, TxKind}, - wiring::EvmWiring as PrimitiveEvmWiring, - wiring::{DefaultEthereumWiring, EthereumWiring}, - Evm, EvmWiring, - }; - - type TestEvmWiring = DefaultEthereumWiring; - - #[derive(Default, Debug)] - struct StackInspector { - pc: usize, - gas_inspector: GasInspector, - gas_remaining_steps: Vec<(usize, u64)>, - } - - impl Inspector for StackInspector { - fn initialize_interp( - &mut self, - interp: &mut Interpreter, - context: &mut EvmContext, - ) { - self.gas_inspector.initialize_interp(interp, context); - } - - fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { - self.pc = interp.program_counter(); - self.gas_inspector.step(interp, context); - } - - fn log( - &mut self, - interp: &mut Interpreter, - context: &mut EvmContext, - log: &Log, - ) { - self.gas_inspector.log(interp, context, log); - } - - fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { - self.gas_inspector.step_end(interp, context); - self.gas_remaining_steps - .push((self.pc, self.gas_inspector.gas_remaining())); - } - - fn call( - &mut self, - context: &mut EvmContext, - call: &mut CallInputs, - ) -> Option { - self.gas_inspector.call(context, call) - } - - fn call_end( - &mut self, - context: &mut EvmContext, - inputs: &CallInputs, - outcome: CallOutcome, - ) -> CallOutcome { - self.gas_inspector.call_end(context, inputs, outcome) - } - - fn create( - &mut self, - context: &mut EvmContext, - call: &mut CreateInputs, - ) -> Option { - self.gas_inspector.create(context, call); - None - } - - fn create_end( - &mut self, - context: &mut EvmContext, - inputs: &CreateInputs, - outcome: CreateOutcome, - ) -> CreateOutcome { - self.gas_inspector.create_end(context, inputs, outcome) - } - } - - #[test] - fn test_gas_inspector() { - let contract_data: Bytes = Bytes::from(vec![ - opcode::PUSH1, - 0x1, - opcode::PUSH1, - 0xb, - opcode::JUMPI, - opcode::PUSH1, - 0x1, - opcode::PUSH1, - 0x1, - opcode::PUSH1, - 0x1, - opcode::JUMPDEST, - opcode::STOP, - ]); - let bytecode = Bytecode::new_raw(contract_data); - - let mut evm = Evm::>::builder() - .with_db(BenchmarkDB::new_bytecode(bytecode.clone())) - .with_default_ext_ctx() - .modify_tx_env(|tx| { - *tx = ::Transaction::default(); - - tx.caller = address!("1000000000000000000000000000000000000000"); - tx.transact_to = TxKind::Call(address!("0000000000000000000000000000000000000000")); - tx.gas_limit = 21100; - }) - .append_handler_register(inspector_handle_register) - .build(); - - // run evm. - evm.transact().unwrap(); - - let inspector = evm.into_context().external; - - // starting from 100gas - let steps = vec![ - // push1 -3 - (0, 97), - // push1 -3 - (2, 94), - // jumpi -10 - (4, 84), - // jumpdest 1 - (11, 83), - // stop 0 - (12, 83), - ]; - - assert_eq!(inspector.gas_remaining_steps, steps); - } -} +// #[cfg(test)] +// mod tests { +// use super::*; +// use crate::inspector_handle_register; +// use database::BenchmarkDB; +// use revm::{ +// bytecode::{opcode, Bytecode}, +// context_interface::EvmWiring as PrimitiveEvmWiring, +// context_interface::{DefaultEthereumWiring, EthereumWiring}, +// interpreter::Interpreter, +// primitives::{address, Bytes, Log, TxKind}, +// Evm, EvmWiring, +// }; + +// type TestEvmWiring = DefaultEthereumWiring; + +// #[derive(Default, Debug)] +// struct StackInspector { +// pc: usize, +// gas_inspector: GasInspector, +// gas_remaining_steps: Vec<(usize, u64)>, +// } + +// impl Inspector for StackInspector { +// fn initialize_interp( +// &mut self, +// interp: &mut Interpreter, +// context: &mut EvmContext, +// ) { +// self.gas_inspector.initialize_interp(interp, context); +// } + +// fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { +// self.pc = interp.program_counter(); +// self.gas_inspector.step(interp, context); +// } + +// fn log( +// &mut self, +// interp: &mut Interpreter, +// context: &mut EvmContext, +// log: &Log, +// ) { +// self.gas_inspector.log(interp, context, log); +// } + +// fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { +// self.gas_inspector.step_end(interp, context); +// self.gas_remaining_steps +// .push((self.pc, self.gas_inspector.gas_remaining())); +// } + +// fn call( +// &mut self, +// context: &mut EvmContext, +// call: &mut CallInputs, +// ) -> Option { +// self.gas_inspector.call(context, call) +// } + +// fn call_end( +// &mut self, +// context: &mut EvmContext, +// inputs: &CallInputs, +// outcome: CallOutcome, +// ) -> CallOutcome { +// self.gas_inspector.call_end(context, inputs, outcome) +// } + +// fn create( +// &mut self, +// context: &mut EvmContext, +// call: &mut CreateInputs, +// ) -> Option { +// self.gas_inspector.create(context, call); +// None +// } + +// fn create_end( +// &mut self, +// context: &mut EvmContext, +// inputs: &CreateInputs, +// outcome: CreateOutcome, +// ) -> CreateOutcome { +// self.gas_inspector.create_end(context, inputs, outcome) +// } +// } + +// #[test] +// fn test_gas_inspector() { +// let contract_data: Bytes = Bytes::from(vec![ +// opcode::PUSH1, +// 0x1, +// opcode::PUSH1, +// 0xb, +// opcode::JUMPI, +// opcode::PUSH1, +// 0x1, +// opcode::PUSH1, +// 0x1, +// opcode::PUSH1, +// 0x1, +// opcode::JUMPDEST, +// opcode::STOP, +// ]); +// let bytecode = Bytecode::new_raw(contract_data); + +// let mut evm = Evm::>::builder() +// .with_db(BenchmarkDB::new_bytecode(bytecode.clone())) +// .with_default_ext_context() +// .modify_tx_env(|tx| { +// *tx = ::Transaction::default(); + +// tx.caller = address!("1000000000000000000000000000000000000000"); +// tx.transact_to = TxKind::Call(address!("0000000000000000000000000000000000000000")); +// tx.gas_limit = 21100; +// }) +// .append_handler_register(inspector_handle_register) +// .build(); + +// // run evm. +// evm.transact().unwrap(); + +// let inspector = evm.into_context().external; + +// // starting from 100gas +// let steps = vec![ +// // push1 -3 +// (0, 97), +// // push1 -3 +// (2, 94), +// // jumpi -10 +// (4, 84), +// // jumpdest 1 +// (11, 83), +// // stop 0 +// (12, 83), +// ]; + +// assert_eq!(inspector.gas_remaining_steps, steps); +// } +// } diff --git a/crates/inspector/src/handler_register.rs b/crates/inspector/src/handler_register.rs deleted file mode 100644 index 88e13f0e79..0000000000 --- a/crates/inspector/src/handler_register.rs +++ /dev/null @@ -1,408 +0,0 @@ -use crate::Inspector; -use core::cell::RefCell; -use revm::{ - bytecode::opcode, - handler::register::EvmHandler, - interpreter::{table::DynInstruction, InstructionResult, Interpreter}, - wiring::result::EVMResultGeneric, - Context, EvmWiring, FrameOrResult, FrameResult, JournalEntry, -}; -use std::{rc::Rc, sync::Arc, vec::Vec}; - -/// Provides access to an `Inspector` instance. -pub trait GetInspector { - /// Returns the associated `Inspector`. - fn get_inspector(&mut self) -> &mut impl Inspector; -} - -impl> GetInspector for INSP { - #[inline] - fn get_inspector(&mut self) -> &mut impl Inspector { - self - } -} - -/// Register Inspector handles that interact with Inspector instance. -/// -/// -/// # Note -/// -/// Inspector handle register does not override any existing handlers, and it -/// calls them before (or after) calling Inspector. This means that it is safe -/// to use this register with any other register. -/// -/// A few instructions handlers are wrapped twice once for `step` and `step_end` -/// and in case of Logs and Selfdestruct wrapper is wrapped again for the -/// `log` and `selfdestruct` calls. -pub fn inspector_handle_register< - EvmWiringT: EvmWiring>, ->( - handler: &mut EvmHandler<'_, EvmWiringT>, -) { - let table = &mut handler.instruction_table; - - // Update all instructions to call inspector step and step_end. - table.update_all(inspector_instruction); - - // Register inspector LOG* instructions. - for opcode in opcode::LOG0..=opcode::LOG4 { - table.update_boxed(opcode, move |prev, interpreter, host| { - let prev_log_len = host.evm.journaled_state.logs.len(); - prev(interpreter, host); - // check if log was added. It is possible that revert happened - // cause of gas or stack underflow. - if host.evm.journaled_state.logs.len() == prev_log_len + 1 { - // clone log. - // TODO decide if we should remove this and leave the comment - // that log can be found as journaled_state. - let last_log = host.evm.journaled_state.logs.last().unwrap().clone(); - // call Inspector - host.external - .get_inspector() - .log(interpreter, &mut host.evm, &last_log); - } - }); - } - - // Register selfdestruct function. - table.update_boxed(opcode::SELFDESTRUCT, |prev, interpreter, host| { - // execute selfdestruct - prev(interpreter, host); - // check if selfdestruct was successful and if journal entry is made. - match host.evm.journaled_state.journal.last().unwrap().last() { - Some(JournalEntry::AccountDestroyed { - address, - target, - had_balance, - .. - }) => { - host.external - .get_inspector() - .selfdestruct(*address, *target, *had_balance); - } - Some(JournalEntry::BalanceTransfer { - from, to, balance, .. - }) => { - host.external - .get_inspector() - .selfdestruct(*from, *to, *balance); - } - _ => {} - } - }); - - // call and create input stack shared between handlers. They are used to share - // inputs in *_end Inspector calls. - let call_input_stack = Rc::>>::default(); - let create_input_stack = Rc::>>::default(); - let eofcreate_input_stack = Rc::>>::default(); - - // Create handler - let create_input_stack_inner = create_input_stack.clone(); - let prev_handle = handler.execution.create.clone(); - handler.execution.create = Arc::new( - move |ctx, mut inputs| -> EVMResultGeneric { - let inspector = ctx.external.get_inspector(); - // call inspector create to change input or return outcome. - if let Some(outcome) = inspector.create(&mut ctx.evm, &mut inputs) { - create_input_stack_inner.borrow_mut().push(inputs.clone()); - return Ok(FrameOrResult::Result(FrameResult::Create(outcome))); - } - create_input_stack_inner.borrow_mut().push(inputs.clone()); - - let mut frame_or_result = prev_handle(ctx, inputs); - if let Ok(FrameOrResult::Frame(frame)) = &mut frame_or_result { - ctx.external - .get_inspector() - .initialize_interp(frame.interpreter_mut(), &mut ctx.evm) - } - frame_or_result - }, - ); - - // Call handler - let call_input_stack_inner = call_input_stack.clone(); - let prev_handle = handler.execution.call.clone(); - handler.execution.call = Arc::new(move |ctx, mut inputs| { - // Call inspector to change input or return outcome. - let outcome = ctx.external.get_inspector().call(&mut ctx.evm, &mut inputs); - call_input_stack_inner.borrow_mut().push(inputs.clone()); - if let Some(outcome) = outcome { - return Ok(FrameOrResult::Result(FrameResult::Call(outcome))); - } - - let mut frame_or_result = prev_handle(ctx, inputs); - if let Ok(FrameOrResult::Frame(frame)) = &mut frame_or_result { - ctx.external - .get_inspector() - .initialize_interp(frame.interpreter_mut(), &mut ctx.evm) - } - frame_or_result - }); - - // Calls inspector `eofcreate` and `initialize_interp` functions. Queues the inputs for the `eofcreate_end`` function. - // Calls the old handler, and in case of inspector returning outcome, - // returns the outcome without executing eofcreate. - let eofcreate_input_stack_inner = eofcreate_input_stack.clone(); - let prev_handle = handler.execution.eofcreate.clone(); - handler.execution.eofcreate = Arc::new(move |ctx, mut inputs| { - // Call inspector to change input or return outcome. - let outcome = ctx - .external - .get_inspector() - .eofcreate(&mut ctx.evm, &mut inputs); - eofcreate_input_stack_inner - .borrow_mut() - .push(inputs.clone()); - if let Some(outcome) = outcome { - return Ok(FrameOrResult::Result(FrameResult::EOFCreate(outcome))); - } - - let mut frame_or_result = prev_handle(ctx, inputs); - if let Ok(FrameOrResult::Frame(frame)) = &mut frame_or_result { - ctx.external - .get_inspector() - .initialize_interp(frame.interpreter_mut(), &mut ctx.evm) - } - frame_or_result - }); - - // Pops eofcreate input from the stack and calls inspector `eofcreate_end` function. - // preserve the old handler and calls it with the outcome. - let eofcreate_input_stack_inner = eofcreate_input_stack.clone(); - let prev_handle = handler.execution.insert_eofcreate_outcome.clone(); - handler.execution.insert_eofcreate_outcome = Arc::new(move |ctx, frame, mut outcome| { - let create_inputs = eofcreate_input_stack_inner.borrow_mut().pop().unwrap(); - outcome = ctx - .external - .get_inspector() - .eofcreate_end(&mut ctx.evm, &create_inputs, outcome); - prev_handle(ctx, frame, outcome) - }); - - // call outcome - let call_input_stack_inner = call_input_stack.clone(); - let prev_handle = handler.execution.insert_call_outcome.clone(); - handler.execution.insert_call_outcome = - Arc::new(move |ctx, frame, shared_memory, mut outcome| { - let call_inputs = call_input_stack_inner.borrow_mut().pop().unwrap(); - outcome = ctx - .external - .get_inspector() - .call_end(&mut ctx.evm, &call_inputs, outcome); - prev_handle(ctx, frame, shared_memory, outcome) - }); - - // create outcome - let create_input_stack_inner = create_input_stack.clone(); - let prev_handle = handler.execution.insert_create_outcome.clone(); - handler.execution.insert_create_outcome = Arc::new(move |ctx, frame, mut outcome| { - let create_inputs = create_input_stack_inner.borrow_mut().pop().unwrap(); - outcome = ctx - .external - .get_inspector() - .create_end(&mut ctx.evm, &create_inputs, outcome); - prev_handle(ctx, frame, outcome) - }); - - // last frame outcome - let prev_handle = handler.execution.last_frame_return.clone(); - handler.execution.last_frame_return = Arc::new(move |ctx, frame_result| { - let inspector = ctx.external.get_inspector(); - match frame_result { - FrameResult::Call(outcome) => { - let call_inputs = call_input_stack.borrow_mut().pop().unwrap(); - *outcome = inspector.call_end(&mut ctx.evm, &call_inputs, outcome.clone()); - } - FrameResult::Create(outcome) => { - let create_inputs = create_input_stack.borrow_mut().pop().unwrap(); - *outcome = inspector.create_end(&mut ctx.evm, &create_inputs, outcome.clone()); - } - FrameResult::EOFCreate(outcome) => { - let eofcreate_inputs = eofcreate_input_stack.borrow_mut().pop().unwrap(); - *outcome = - inspector.eofcreate_end(&mut ctx.evm, &eofcreate_inputs, outcome.clone()); - } - } - prev_handle(ctx, frame_result) - }); -} - -fn inspector_instruction( - prev: &DynInstruction<'_, Context>, - interpreter: &mut Interpreter, - host: &mut Context, -) where - EvmWiringT: EvmWiring, - EvmWiringT::ExternalContext: GetInspector, -{ - // SAFETY: as the PC was already incremented we need to subtract 1 to preserve the - // old Inspector behavior. - interpreter.instruction_pointer = unsafe { interpreter.instruction_pointer.sub(1) }; - - // Call step. - host.external - .get_inspector() - .step(interpreter, &mut host.evm); - if interpreter.instruction_result != InstructionResult::Continue { - return; - } - - // Reset PC to previous value. - interpreter.instruction_pointer = unsafe { interpreter.instruction_pointer.add(1) }; - - // Execute instruction. - prev(interpreter, host); - - // Call step_end. - host.external - .get_inspector() - .step_end(interpreter, &mut host.evm); -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{inspector_handle_register, inspectors::NoOpInspector}; - use database::BenchmarkDB; - use revm::{ - bytecode::{opcode, Bytecode}, - database_interface::EmptyDB, - interpreter::{CallInputs, CallOutcome, CreateInputs, CreateOutcome}, - primitives::{address, Bytes, TxKind}, - wiring::{DefaultEthereumWiring, EthereumWiring, EvmWiring as PrimitiveEvmWiring}, - Evm, EvmContext, EvmWiring, - }; - - type TestEvmWiring = DefaultEthereumWiring; - - #[derive(Default, Debug)] - struct StackInspector { - initialize_interp_called: bool, - step: u32, - step_end: u32, - call: bool, - call_end: bool, - } - - impl Inspector for StackInspector { - fn initialize_interp( - &mut self, - _interp: &mut Interpreter, - _context: &mut EvmContext, - ) { - if self.initialize_interp_called { - unreachable!("initialize_interp should not be called twice") - } - self.initialize_interp_called = true; - } - - fn step(&mut self, _interp: &mut Interpreter, _context: &mut EvmContext) { - self.step += 1; - } - - fn step_end(&mut self, _interp: &mut Interpreter, _context: &mut EvmContext) { - self.step_end += 1; - } - - fn call( - &mut self, - context: &mut EvmContext, - _call: &mut CallInputs, - ) -> Option { - if self.call { - unreachable!("call should not be called twice") - } - self.call = true; - assert_eq!(context.journaled_state.depth(), 0); - None - } - - fn call_end( - &mut self, - context: &mut EvmContext, - _inputs: &CallInputs, - outcome: CallOutcome, - ) -> CallOutcome { - if self.call_end { - unreachable!("call_end should not be called twice") - } - assert_eq!(context.journaled_state.depth(), 0); - self.call_end = true; - outcome - } - - fn create( - &mut self, - context: &mut EvmContext, - _call: &mut CreateInputs, - ) -> Option { - assert_eq!(context.journaled_state.depth(), 0); - None - } - - fn create_end( - &mut self, - context: &mut EvmContext, - _inputs: &CreateInputs, - outcome: CreateOutcome, - ) -> CreateOutcome { - assert_eq!(context.journaled_state.depth(), 0); - outcome - } - } - - #[test] - fn test_inspector_handlers() { - let contract_data: Bytes = Bytes::from(vec![ - opcode::PUSH1, - 0x1, - opcode::PUSH1, - 0xb, - opcode::PUSH1, - 0x1, - opcode::PUSH1, - 0x1, - opcode::PUSH1, - 0x1, - opcode::CREATE, - opcode::STOP, - ]); - let bytecode = Bytecode::new_raw(contract_data); - - let mut evm = Evm::>::builder() - .with_default_ext_ctx() - .with_db(BenchmarkDB::new_bytecode(bytecode.clone())) - .with_external_context(StackInspector::default()) - .modify_tx_env(|tx| { - *tx = ::Transaction::default(); - - tx.caller = address!("1000000000000000000000000000000000000000"); - tx.transact_to = TxKind::Call(address!("0000000000000000000000000000000000000000")); - tx.gas_limit = 21100; - }) - .append_handler_register(inspector_handle_register) - .build(); - - // run evm. - evm.transact().unwrap(); - - let inspector = evm.into_context().external; - - assert_eq!(inspector.step, 6); - assert_eq!(inspector.step_end, 6); - assert!(inspector.initialize_interp_called); - assert!(inspector.call); - assert!(inspector.call_end); - } - - #[test] - fn test_inspector_reg() { - let mut noop = NoOpInspector; - let _evm: Evm<'_, EthereumWiring> = Evm::builder() - .with_default_db() - .with_external_context(&mut noop) - .append_handler_register(inspector_handle_register) - .build(); - } -} diff --git a/crates/inspector/src/inspector.rs b/crates/inspector/src/inspector.rs index 923f91c8ef..4660ade64e 100644 --- a/crates/inspector/src/inspector.rs +++ b/crates/inspector/src/inspector.rs @@ -1,15 +1,45 @@ use auto_impl::auto_impl; +use core::mem::MaybeUninit; +use derive_where::derive_where; use revm::{ + bytecode::opcode::OpCode, + context::{block::BlockEnv, tx::TxEnv, Cfg}, + context_interface::{ + block::BlockSetter, + journaled_state::{AccountLoad, Eip7702CodeLoad}, + result::EVMError, + transaction::TransactionSetter, + Block, BlockGetter, CfgEnv, CfgGetter, DatabaseGetter, ErrorGetter, JournalStateGetter, + JournalStateGetterDBError, Transaction, TransactionGetter, + }, + database_interface::{Database, EmptyDB}, + handler::{ + EthExecution, EthFrame, EthHandler, EthPostExecution, EthPreExecution, + EthPrecompileProvider, EthValidation, FrameResult, + }, + handler_interface::{Frame, FrameOrResultGen, PrecompileProvider}, interpreter::{ - CallInputs, CallOutcome, CreateInputs, CreateOutcome, EOFCreateInputs, Interpreter, + instructions::host::{log, selfdestruct}, + interpreter::{EthInterpreter, InstructionProvider}, + interpreter_types::{Jumps, LoopControl}, + table::{self, CustomInstruction}, + CallInputs, CallOutcome, CreateInputs, CreateOutcome, EOFCreateInputs, FrameInput, Host, + Instruction, InstructionResult, Interpreter, InterpreterTypes, SStoreResult, + SelfDestructResult, StateLoad, }, - primitives::{Address, Log, U256}, - EvmContext, EvmWiring, + precompile::PrecompileErrors, + primitives::{Address, Bytes, Log, B256, U256}, + specification::hardfork::SpecId, + Context, Error, Evm, JournalEntry, JournaledState, }; +use std::{rc::Rc, vec::Vec}; /// EVM [Interpreter] callbacks. #[auto_impl(&mut, Box)] -pub trait Inspector { +pub trait Inspector { + type Context; + type InterpreterTypes: InterpreterTypes; + /// Called before the interpreter is initialized. /// /// If `interp.instruction_result` is set to anything other than [revm::interpreter::InstructionResult::Continue] then the execution of the interpreter @@ -17,8 +47,8 @@ pub trait Inspector { #[inline] fn initialize_interp( &mut self, - interp: &mut Interpreter, - context: &mut EvmContext, + interp: &mut Interpreter, + context: &mut Self::Context, ) { let _ = interp; let _ = context; @@ -33,7 +63,11 @@ pub trait Inspector { /// /// To get the current opcode, use `interp.current_opcode()`. #[inline] - fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { + fn step( + &mut self, + interp: &mut Interpreter, + context: &mut Self::Context, + ) { let _ = interp; let _ = context; } @@ -43,14 +77,23 @@ pub trait Inspector { /// Setting `interp.instruction_result` to anything other than [revm::interpreter::InstructionResult::Continue] alters the execution /// of the interpreter. #[inline] - fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext) { + fn step_end( + &mut self, + interp: &mut Interpreter, + context: &mut Self::Context, + ) { let _ = interp; let _ = context; } /// Called when a log is emitted. #[inline] - fn log(&mut self, interp: &mut Interpreter, context: &mut EvmContext, log: &Log) { + fn log( + &mut self, + interp: &mut Interpreter, + context: &mut Self::Context, + log: &Log, + ) { let _ = interp; let _ = context; let _ = log; @@ -62,7 +105,7 @@ pub trait Inspector { #[inline] fn call( &mut self, - context: &mut EvmContext, + context: &mut Self::Context, inputs: &mut CallInputs, ) -> Option { let _ = context; @@ -78,13 +121,13 @@ pub trait Inspector { #[inline] fn call_end( &mut self, - context: &mut EvmContext, + context: &mut Self::Context, inputs: &CallInputs, - outcome: CallOutcome, - ) -> CallOutcome { + outcome: &mut CallOutcome, + ) { let _ = context; let _ = inputs; - outcome + let _ = outcome; } /// Called when a contract is about to be created. @@ -95,7 +138,7 @@ pub trait Inspector { #[inline] fn create( &mut self, - context: &mut EvmContext, + context: &mut Self::Context, inputs: &mut CreateInputs, ) -> Option { let _ = context; @@ -110,13 +153,13 @@ pub trait Inspector { #[inline] fn create_end( &mut self, - context: &mut EvmContext, + context: &mut Self::Context, inputs: &CreateInputs, - outcome: CreateOutcome, - ) -> CreateOutcome { + outcome: &mut CreateOutcome, + ) { let _ = context; let _ = inputs; - outcome + let _ = outcome; } /// Called when EOF creating is called. @@ -124,7 +167,7 @@ pub trait Inspector { /// This can happen from create TX or from EOFCREATE opcode. fn eofcreate( &mut self, - context: &mut EvmContext, + context: &mut Self::Context, inputs: &mut EOFCreateInputs, ) -> Option { let _ = context; @@ -135,13 +178,13 @@ pub trait Inspector { /// Called when eof creating has ended. fn eofcreate_end( &mut self, - context: &mut EvmContext, + context: &mut Self::Context, inputs: &EOFCreateInputs, - outcome: CreateOutcome, - ) -> CreateOutcome { + outcome: &mut CreateOutcome, + ) { let _ = context; let _ = inputs; - outcome + let _ = outcome; } /// Called when a contract has been self-destructed with funds transferred to target. @@ -152,3 +195,620 @@ pub trait Inspector { let _ = value; } } + +/// Provides access to an `Inspector` instance. +pub trait GetInspector { + type Inspector: Inspector; + /// Returns the associated `Inspector`. + fn get_inspector(&mut self) -> &mut Self::Inspector; +} + +pub trait InspectorCtx { + type IT: InterpreterTypes; + + fn step(&mut self, interp: &mut Interpreter); + fn step_end(&mut self, interp: &mut Interpreter); + fn initialize_interp(&mut self, interp: &mut Interpreter); + fn frame_start(&mut self, frame_input: &mut FrameInput) -> Option; + fn frame_end(&mut self, frame_output: &mut FrameResult); + fn inspector_selfdestruct(&mut self, contract: Address, target: Address, value: U256); + fn inspector_log(&mut self, interp: &mut Interpreter, log: &Log); +} + +impl GetInspector for INSP { + type Inspector = INSP; + #[inline] + fn get_inspector(&mut self) -> &mut Self::Inspector { + self + } +} + +/// EVM context contains data that EVM needs for execution. +#[derive_where(Clone, Debug; INSP, BLOCK, SPEC, CHAIN, TX, DB, ::Error)] +pub struct InspectorContext< + INSP, + BLOCK = BlockEnv, + TX = TxEnv, + SPEC = SpecId, + DB: Database = EmptyDB, + CHAIN = (), +> { + pub inner: Context, + pub inspector: INSP, + pub frame_input_stack: Vec, +} + +impl + InspectorContext +{ + pub fn new(inner: Context, inspector: INSP) -> Self { + Self { + inner, + inspector, + frame_input_stack: Vec::new(), + } + } +} + +impl Host + for InspectorContext +{ + type BLOCK = BLOCK; + type TX = TX; + type CFG = CFG; + + fn tx(&self) -> &Self::TX { + &self.inner.tx + } + + fn block(&self) -> &Self::BLOCK { + &self.inner.block + } + + fn cfg(&self) -> &Self::CFG { + &self.inner.cfg + } + + fn block_hash(&mut self, requested_number: u64) -> Option { + self.inner.block_hash(requested_number) + } + + fn load_account_delegated(&mut self, address: Address) -> Option { + self.inner.load_account_delegated(address) + } + + fn balance(&mut self, address: Address) -> Option> { + self.inner.balance(address) + } + + fn code(&mut self, address: Address) -> Option> { + // TODO remove duplicated function name. + as Host>::code(&mut self.inner, address) + } + + fn code_hash(&mut self, address: Address) -> Option> { + as Host>::code_hash(&mut self.inner, address) + } + + fn sload(&mut self, address: Address, index: U256) -> Option> { + self.inner.sload(address, index) + } + + fn sstore( + &mut self, + address: Address, + index: U256, + value: U256, + ) -> Option> { + self.inner.sstore(address, index, value) + } + + fn tload(&mut self, address: Address, index: U256) -> U256 { + self.inner.tload(address, index) + } + + fn tstore(&mut self, address: Address, index: U256, value: U256) { + self.inner.tstore(address, index, value) + } + + fn log(&mut self, log: Log) { + self.inner.log(log); + } + + fn selfdestruct( + &mut self, + address: Address, + target: Address, + ) -> Option> { + self.inner.selfdestruct(address, target) + } +} + +impl InspectorCtx + for InspectorContext +where + INSP: GetInspector< + Inspector: Inspector< + Context = Context, + InterpreterTypes = EthInterpreter, + >, + >, +{ + type IT = EthInterpreter<()>; + + fn step(&mut self, interp: &mut Interpreter) { + self.inspector.get_inspector().step(interp, &mut self.inner); + } + + fn step_end(&mut self, interp: &mut Interpreter) { + self.inspector + .get_inspector() + .step_end(interp, &mut self.inner); + } + + fn initialize_interp(&mut self, interp: &mut Interpreter) { + self.inspector + .get_inspector() + .initialize_interp(interp, &mut self.inner); + } + fn inspector_log(&mut self, interp: &mut Interpreter, log: &Log) { + self.inspector + .get_inspector() + .log(interp, &mut self.inner, log); + } + + fn frame_start(&mut self, frame_input: &mut FrameInput) -> Option { + let insp = self.inspector.get_inspector(); + let context = &mut self.inner; + match frame_input { + FrameInput::Call(i) => { + if let Some(output) = insp.call(context, i) { + return Some(FrameResult::Call(output)); + } + } + FrameInput::Create(i) => { + if let Some(output) = insp.create(context, i) { + return Some(FrameResult::Create(output)); + } + } + FrameInput::EOFCreate(i) => { + if let Some(output) = insp.eofcreate(context, i) { + return Some(FrameResult::EOFCreate(output)); + } + } + } + self.frame_input_stack.push(frame_input.clone()); + None + } + + fn frame_end(&mut self, frame_output: &mut FrameResult) { + let insp = self.inspector.get_inspector(); + let context = &mut self.inner; + let frame_input = self.frame_input_stack.pop().expect("Frame pushed"); + match frame_output { + FrameResult::Call(outcome) => { + let FrameInput::Call(i) = frame_input else { + panic!("FrameInput::Call expected"); + }; + insp.call_end(context, &i, outcome); + } + FrameResult::Create(outcome) => { + let FrameInput::Create(i) = frame_input else { + panic!("FrameInput::Create expected"); + }; + insp.create_end(context, &i, outcome); + } + FrameResult::EOFCreate(outcome) => { + let FrameInput::EOFCreate(i) = frame_input else { + panic!("FrameInput::EofCreate expected"); + }; + insp.eofcreate_end(context, &i, outcome); + } + } + } + + fn inspector_selfdestruct(&mut self, contract: Address, target: Address, value: U256) { + self.inspector + .get_inspector() + .selfdestruct(contract, target, value) + } +} + +impl CfgGetter + for InspectorContext +{ + type Cfg = CFG; + + fn cfg(&self) -> &Self::Cfg { + &self.inner.cfg + } +} + +impl JournalStateGetter + for InspectorContext +{ + type Journal = JournaledState; + + fn journal(&mut self) -> &mut Self::Journal { + &mut self.inner.journaled_state + } +} + +impl DatabaseGetter + for InspectorContext +{ + type Database = DB; + + fn db(&mut self) -> &mut Self::Database { + &mut self.inner.journaled_state.database + } +} + +impl ErrorGetter + for InspectorContext +{ + type Error = EVMError; + + fn take_error(&mut self) -> Result<(), Self::Error> { + core::mem::replace(&mut self.inner.error, Ok(())).map_err(EVMError::Database) + } +} + +impl TransactionGetter + for InspectorContext +{ + type Transaction = TX; + + fn tx(&self) -> &Self::Transaction { + &self.inner.tx + } +} + +impl TransactionSetter + for InspectorContext +{ + fn set_tx(&mut self, tx: ::Transaction) { + self.inner.tx = tx; + } +} + +impl BlockGetter + for InspectorContext +{ + type Block = BLOCK; + + fn block(&self) -> &Self::Block { + &self.inner.block + } +} + +impl BlockSetter + for InspectorContext +{ + fn set_block(&mut self, block: ::Block) { + self.inner.block = block; + } +} + +impl JournalExtGetter + for InspectorContext +{ + type JournalExt = JournaledState; + + fn journal_ext(&self) -> &Self::JournalExt { + &self.inner.journaled_state + } +} + +#[derive(Clone)] +pub struct InspectorInstruction { + pub instruction: fn(&mut Interpreter, &mut HOST), +} + +impl CustomInstruction for InspectorInstruction +where + HOST: InspectorCtx, +{ + type Wire = IT; + type Host = HOST; + + fn exec(&self, interpreter: &mut Interpreter, host: &mut Self::Host) { + // SAFETY: as the PC was already incremented we need to subtract 1 to preserve the + // old Inspector behavior. + interpreter.bytecode.relative_jump(-1); + + // Call step. + host.step(interpreter); + if interpreter.control.instruction_result() != InstructionResult::Continue { + return; + } + + // Reset PC to previous value. + interpreter.bytecode.relative_jump(1); + + // Execute instruction. + (self.instruction)(interpreter, host); + + // Call step_end. + host.step_end(interpreter); + } + + fn from_base(instruction: Instruction) -> Self { + Self { instruction } + } +} + +pub struct InspectorInstructionProvider { + instruction_table: Rc<[InspectorInstruction; 256]>, +} + +impl Clone for InspectorInstructionProvider +where + WIRE: InterpreterTypes, +{ + fn clone(&self) -> Self { + Self { + instruction_table: self.instruction_table.clone(), + } + } +} + +pub trait JournalExt { + fn logs(&self) -> &[Log]; + + fn last_journal(&self) -> &[JournalEntry]; +} + +impl JournalExt for JournaledState { + fn logs(&self) -> &[Log] { + &self.logs + } + + fn last_journal(&self) -> &[JournalEntry] { + self.journal.last().expect("Journal is never empty") + } +} + +#[auto_impl(&, &mut, Box, Arc)] +pub trait JournalExtGetter { + type JournalExt: JournalExt; + + fn journal_ext(&self) -> &Self::JournalExt; +} + +impl InstructionProvider for InspectorInstructionProvider +where + WIRE: InterpreterTypes, + HOST: Host + JournalExtGetter + JournalStateGetter + InspectorCtx, +{ + type WIRE = WIRE; + type Host = HOST; + + fn new(_context: &mut Self::Host) -> Self { + let main_table = table::make_instruction_table::(); + let mut table: [MaybeUninit>; 256] = + unsafe { MaybeUninit::uninit().assume_init() }; + + for (i, element) in table.iter_mut().enumerate() { + let function = InspectorInstruction { + instruction: main_table[i], + }; + *element = MaybeUninit::new(function); + } + + let mut table = unsafe { + core::mem::transmute::< + [MaybeUninit>; 256], + [InspectorInstruction; 256], + >(table) + }; + + // inspector log wrapper + + fn inspector_log( + interpreter: &mut Interpreter<::IT>, + context: &mut CTX, + prev: Instruction<::IT, CTX>, + ) { + prev(interpreter, context); + + if interpreter.control.instruction_result() == InstructionResult::Continue { + let last_log = context.journal_ext().logs().last().unwrap().clone(); + context.inspector_log(interpreter, &last_log); + } + } + + /* LOG and Selfdestruct instructions */ + table[OpCode::LOG0.as_usize()] = InspectorInstruction { + instruction: |interp, context| { + inspector_log(interp, context, log::<0, HOST>); + }, + }; + table[OpCode::LOG1.as_usize()] = InspectorInstruction { + instruction: |interp, context| { + inspector_log(interp, context, log::<1, HOST>); + }, + }; + table[OpCode::LOG2.as_usize()] = InspectorInstruction { + instruction: |interp, context| { + inspector_log(interp, context, log::<2, HOST>); + }, + }; + table[OpCode::LOG3.as_usize()] = InspectorInstruction { + instruction: |interp, context| { + inspector_log(interp, context, log::<3, HOST>); + }, + }; + table[OpCode::LOG4.as_usize()] = InspectorInstruction { + instruction: |interp, context| { + inspector_log(interp, context, log::<4, HOST>); + }, + }; + + table[OpCode::SELFDESTRUCT.as_usize()] = InspectorInstruction { + instruction: |interp, context| { + selfdestruct::(interp, context); + if interp.control.instruction_result() == InstructionResult::SelfDestruct { + match context.journal_ext().last_journal().last() { + Some(JournalEntry::AccountDestroyed { + address, + target, + had_balance, + .. + }) => { + context.inspector_selfdestruct(*address, *target, *had_balance); + } + Some(JournalEntry::BalanceTransfer { + from, to, balance, .. + }) => { + context.inspector_selfdestruct(*from, *to, *balance); + } + _ => {} + } + } + }, + }; + + Self { + instruction_table: Rc::new(table), + } + } + + fn table(&mut self) -> &[impl CustomInstruction; 256] { + self.instruction_table.as_ref() + } +} + +pub struct InspectorEthFrame +where + CTX: Host, +{ + /// TODO for now hardcode the InstructionProvider. But in future this should be configurable + /// as generic parameter. + pub eth_frame: EthFrame< + CTX, + ERROR, + EthInterpreter<()>, + PRECOMPILE, + InspectorInstructionProvider, CTX>, + >, +} + +impl Frame for InspectorEthFrame +where + CTX: TransactionGetter + + ErrorGetter + + BlockGetter + + JournalStateGetter + + CfgGetter + + JournalExtGetter + + Host + + InspectorCtx, + ERROR: From> + From, + PRECOMPILE: PrecompileProvider, +{ + type Context = CTX; + type Error = ERROR; + type FrameInit = FrameInput; + type FrameResult = FrameResult; + + fn init_first( + context: &mut Self::Context, + mut frame_input: Self::FrameInit, + ) -> Result, Self::Error> { + if let Some(output) = context.frame_start(&mut frame_input) { + return Ok(FrameOrResultGen::Result(output)); + } + let mut ret = EthFrame::init_first(context, frame_input) + .map(|frame| frame.map_frame(|eth_frame| Self { eth_frame })); + + match &mut ret { + Ok(FrameOrResultGen::Result(res)) => { + context.frame_end(res); + } + Ok(FrameOrResultGen::Frame(frame)) => { + context.initialize_interp(&mut frame.eth_frame.interpreter); + } + _ => (), + } + + ret + } + + fn init( + &self, + context: &mut Self::Context, + mut frame_input: Self::FrameInit, + ) -> Result, Self::Error> { + if let Some(output) = context.frame_start(&mut frame_input) { + return Ok(FrameOrResultGen::Result(output)); + } + let mut ret = self + .eth_frame + .init(context, frame_input) + .map(|frame| frame.map_frame(|eth_frame| Self { eth_frame })); + + if let Ok(FrameOrResultGen::Frame(frame)) = &mut ret { + context.initialize_interp(&mut frame.eth_frame.interpreter); + } + + // TODO handle last frame_end. MAKE a separate function for `last_return_result`. + + ret + } + + fn run( + &mut self, + context: &mut Self::Context, + ) -> Result, Self::Error> { + self.eth_frame.run(context) + } + + fn return_result( + &mut self, + context: &mut Self::Context, + mut result: Self::FrameResult, + ) -> Result<(), Self::Error> { + context.frame_end(&mut result); + self.eth_frame.return_result(context, result) + } +} + +pub type InspCtxType = + InspectorContext; + +pub type InspectorMainEvm = Evm< + Error, + InspCtxType, + EthHandler< + InspCtxType, + Error, + EthValidation, Error>, + EthPreExecution, Error>, + InspectorEthExecution, Error>, + >, +>; + +/// Function to create Inspector Handler. +pub fn inspector_handler() -> InspectorHandler +{ + EthHandler::new( + EthValidation::new(), + EthPreExecution::new(), + EthExecution::<_, _, InspectorEthFrame<_, _, PRECOMPILE>>::new(), + EthPostExecution::new(), + ) +} + +/// Composed type for Inspector Execution handler. +pub type InspectorEthExecution> = + EthExecution>; + +/// Composed type for Inspector Handler. +pub type InspectorHandler = EthHandler< + CTX, + ERROR, + EthValidation, + EthPreExecution, + InspectorEthExecution, +>; diff --git a/crates/inspector/src/lib.rs b/crates/inspector/src/lib.rs index 59c784b2b4..66e6f7084a 100644 --- a/crates/inspector/src/lib.rs +++ b/crates/inspector/src/lib.rs @@ -5,22 +5,16 @@ #[cfg(not(feature = "std"))] extern crate alloc as std; -#[cfg(feature = "std")] -mod customprinter; #[cfg(all(feature = "std", feature = "serde-json"))] mod eip3155; mod gas; -mod handler_register; mod inspector; mod noop; -pub use handler_register::{inspector_handle_register, GetInspector}; -pub use inspector::Inspector; +pub use inspector::*; /// [Inspector] implementations. pub mod inspectors { - #[cfg(feature = "std")] - pub use super::customprinter::CustomPrintTracer; #[cfg(all(feature = "std", feature = "serde-json"))] pub use super::eip3155::TracerEip3155; pub use super::gas::GasInspector; diff --git a/crates/inspector/src/noop.rs b/crates/inspector/src/noop.rs index b95a21554b..f79cff76a1 100644 --- a/crates/inspector/src/noop.rs +++ b/crates/inspector/src/noop.rs @@ -1,8 +1,14 @@ +use revm::interpreter::InterpreterTypes; + use crate::Inspector; -use revm::EvmWiring; /// Dummy [Inspector], helpful as standalone replacement. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct NoOpInspector; +pub struct NoOpInspector { + _phantom: core::marker::PhantomData<(CTX, INTR)>, +} -impl Inspector for NoOpInspector {} +impl Inspector for NoOpInspector { + type Context = CTX; + type InterpreterTypes = INTR; +} diff --git a/crates/interface/LICENSE b/crates/interface/LICENSE deleted file mode 100644 index ad98ff22cc..0000000000 --- a/crates/interface/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2021-2024 draganrakita - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/crates/interface/src/lib.rs b/crates/interface/src/lib.rs deleted file mode 100644 index b3ceb57058..0000000000 --- a/crates/interface/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -//! Optimism-specific constants, types, and helpers. -#![cfg_attr(not(test), warn(unused_crate_dependencies))] -#![cfg_attr(not(feature = "std"), no_std)] - -#[cfg(not(feature = "std"))] -extern crate alloc as std; - - diff --git a/crates/interpreter/Cargo.toml b/crates/interpreter/Cargo.toml index e2d99ad3fc..231d892843 100644 --- a/crates/interpreter/Cargo.toml +++ b/crates/interpreter/Cargo.toml @@ -26,11 +26,7 @@ all = "warn" bytecode.workspace = true primitives.workspace = true specification.workspace = true -wiring.workspace = true -transaction.workspace = true - -# mics -derive-where = { version = "1.2.7", default-features = false } +context-interface.workspace = true # optional serde = { version = "1.0", default-features = false, features = [ @@ -46,8 +42,14 @@ bincode = "1.3" [features] default = ["std"] -std = ["serde?/std", "primitives/std", "wiring/std"] +std = ["serde?/std", "primitives/std", "context-interface/std"] hashbrown = ["primitives/hashbrown"] -serde = ["dep:serde", "primitives/serde", "bytecode/serde", "wiring/serde"] +serde = [ + "dep:serde", + "primitives/serde", + "bytecode/serde", + "context-interface/serde", +] arbitrary = ["std", "primitives/arbitrary"] -memory_limit = ["wiring/memory_limit"] +# TODO Should be set from Context or from crate that consumes this PR. +memory_limit = [] diff --git a/crates/interpreter/src/gas.rs b/crates/interpreter/src/gas.rs index 1407ae7bc6..308e917425 100644 --- a/crates/interpreter/src/gas.rs +++ b/crates/interpreter/src/gas.rs @@ -16,6 +16,8 @@ pub struct Gas { remaining: u64, /// Refunded gas. This is used only at the end of execution. refunded: i64, + /// Memoisation of values for memory expansion cost. + memory: MemoryGas, } impl Gas { @@ -26,6 +28,7 @@ impl Gas { limit, remaining: limit, refunded: 0, + memory: MemoryGas::new(), } } @@ -36,6 +39,7 @@ impl Gas { limit, remaining: 0, refunded: 0, + memory: MemoryGas::new(), } } @@ -128,4 +132,63 @@ impl Gas { } success } + + /// Record memory expansion + #[inline] + #[must_use = "internally uses record_cost that flags out of gas error"] + pub fn record_memory_expansion(&mut self, new_len: usize) -> MemoryExtensionResult { + let Some(additional_cost) = self.memory.record_new_len(new_len) else { + return MemoryExtensionResult::Same; + }; + + if !self.record_cost(additional_cost) { + return MemoryExtensionResult::OutOfGas; + } + + MemoryExtensionResult::Extended + } +} + +pub enum MemoryExtensionResult { + /// Memory was extended. + Extended, + /// Memory size stayed the same. + Same, + /// Not enough gas to extend memory.s + OutOfGas, +} + +/// Utility struct that speeds up calculation of memory expansion +/// It contains the current memory length and its memory expansion cost. +/// +/// It allows us to split gas accounting from memory structure. +#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct MemoryGas { + /// Current memory length + pub words_num: usize, + /// Current memory expansion cost + pub expansion_cost: u64, +} + +impl MemoryGas { + pub const fn new() -> Self { + Self { + words_num: 0, + expansion_cost: 0, + } + } + + #[inline] + pub fn record_new_len(&mut self, new_num: usize) -> Option { + if new_num <= self.words_num { + return None; + } + self.words_num = new_num; + let mut cost = crate::gas::calc::memory_gas(new_num); + core::mem::swap(&mut self.expansion_cost, &mut cost); + // safe to subtract because we know that new_len > length + // notice the swap above. + Some(self.expansion_cost - cost) + } } diff --git a/crates/interpreter/src/gas/calc.rs b/crates/interpreter/src/gas/calc.rs index b5fc98fa45..eccbff365f 100644 --- a/crates/interpreter/src/gas/calc.rs +++ b/crates/interpreter/src/gas/calc.rs @@ -1,18 +1,11 @@ use super::constants::*; -use crate::{num_words, AccountLoad, Eip7702CodeLoad, SStoreResult, SelfDestructResult, StateLoad}; +use crate::{num_words, tri, SStoreResult, SelfDestructResult, StateLoad}; +use context_interface::{ + journaled_state::{AccountLoad, Eip7702CodeLoad}, + transaction::AccessListTrait, +}; use primitives::U256; use specification::{eip7702, hardfork::SpecId}; -use transaction::AccessListTrait; - -/// `const` Option `?`. -macro_rules! tri { - ($e:expr) => { - match $e { - Some(v) => v, - None => return None, - } - }; -} /// `SSTORE` opcode refund calculation. #[allow(clippy::collapsible_else_if)] @@ -68,7 +61,7 @@ pub fn sstore_refund(spec_id: SpecId, vals: &SStoreResult) -> i64 { /// `CREATE2` opcode cost calculation. #[inline] -pub const fn create2_cost(len: u64) -> Option { +pub const fn create2_cost(len: usize) -> Option { CREATE.checked_add(tri!(cost_per_word(len, KECCAK256WORD))) } @@ -116,13 +109,17 @@ pub fn exp_cost(spec_id: SpecId, power: U256) -> Option { /// `*COPY` opcodes cost calculation. #[inline] -pub const fn copy_cost_verylow(len: u64) -> Option { +pub const fn copy_cost_verylow(len: usize) -> Option { copy_cost(VERYLOW, len) } /// `EXTCODECOPY` opcode cost calculation. #[inline] -pub const fn extcodecopy_cost(spec_id: SpecId, len: u64, load: Eip7702CodeLoad<()>) -> Option { +pub const fn extcodecopy_cost( + spec_id: SpecId, + len: usize, + load: Eip7702CodeLoad<()>, +) -> Option { let base_gas = if spec_id.is_enabled_in(SpecId::BERLIN) { warm_cold_cost_with_delegation(load) } else if spec_id.is_enabled_in(SpecId::TANGERINE) { @@ -134,7 +131,7 @@ pub const fn extcodecopy_cost(spec_id: SpecId, len: u64, load: Eip7702CodeLoad<( } #[inline] -pub const fn copy_cost(base_cost: u64, len: u64) -> Option { +pub const fn copy_cost(base_cost: u64, len: usize) -> Option { base_cost.checked_add(tri!(cost_per_word(len, COPY))) } @@ -146,14 +143,14 @@ pub const fn log_cost(n: u8, len: u64) -> Option { /// `KECCAK256` opcode cost calculation. #[inline] -pub const fn keccak256_cost(len: u64) -> Option { +pub const fn keccak256_cost(len: usize) -> Option { KECCAK256.checked_add(tri!(cost_per_word(len, KECCAK256WORD))) } /// Calculate the cost of buffer per word. #[inline] -pub const fn cost_per_word(len: u64, multiple: u64) -> Option { - multiple.checked_mul(num_words(len)) +pub const fn cost_per_word(len: usize, multiple: u64) -> Option { + multiple.checked_mul(num_words(len) as u64) } /// EIP-3860: Limit and meter initcode @@ -162,7 +159,7 @@ pub const fn cost_per_word(len: u64, multiple: u64) -> Option { /// /// This cannot overflow as the initcode length is assumed to be checked. #[inline] -pub const fn initcode_cost(len: u64) -> u64 { +pub const fn initcode_cost(len: usize) -> u64 { let Some(cost) = cost_per_word(len, INITCODE_WORD_COST) else { panic!("initcode cost overflow") }; @@ -342,15 +339,10 @@ pub const fn warm_cold_cost_with_delegation(load: Eip7702CodeLoad<()>) -> u64 { gas } -/// Memory expansion cost calculation for a given memory length. -#[inline] -pub const fn memory_gas_for_len(len: usize) -> u64 { - memory_gas(crate::interpreter::num_words(len as u64)) -} - /// Memory expansion cost calculation for a given number of words. #[inline] -pub const fn memory_gas(num_words: u64) -> u64 { +pub const fn memory_gas(num_words: usize) -> u64 { + let num_words = num_words as u64; MEMORY .saturating_mul(num_words) .saturating_add(num_words.saturating_mul(num_words) / 512) @@ -401,7 +393,7 @@ pub fn validate_initial_tx_gas( // EIP-3860: Limit and meter initcode // Init code stipend for bytecode analysis if spec_id.is_enabled_in(SpecId::SHANGHAI) && is_create { - initial_gas += initcode_cost(input.len() as u64) + initial_gas += initcode_cost(input.len()) } // EIP-7702 diff --git a/crates/interpreter/src/host.rs b/crates/interpreter/src/host.rs index de1a3cc115..5d12002bd1 100644 --- a/crates/interpreter/src/host.rs +++ b/crates/interpreter/src/host.rs @@ -1,20 +1,30 @@ -use core::ops::{Deref, DerefMut}; -use primitives::{Address, Bytes, Log, B256, U256}; - mod dummy; pub use dummy::DummyHost; -use wiring::{default::EnvWiring, EvmWiring}; + +use context_interface::{ + journaled_state::{AccountLoad, Eip7702CodeLoad}, + Block, Cfg, Transaction, +}; +use primitives::{Address, Bytes, Log, B256, U256}; + +pub use context_interface::journaled_state::StateLoad; /// EVM context host. +/// TODO move to context-interface pub trait Host { /// Chain specification. - type EvmWiringT: EvmWiring; + type BLOCK: Block; + type TX: Transaction; + type CFG: Cfg; /// Returns a reference to the environment. - fn env(&self) -> &EnvWiring; + fn tx(&self) -> &Self::TX; /// Returns a mutable reference to the environment. - fn env_mut(&mut self) -> &mut EnvWiring; + fn block(&self) -> &Self::BLOCK; + + /// TODO make it generic in future + fn cfg(&self) -> &Self::CFG; /// Load an account code. fn load_account_delegated(&mut self, address: Address) -> Option; @@ -111,141 +121,6 @@ impl SStoreResult { } } -/// 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 AccountLoad { - /// Is account and delegate code are loaded - pub load: Eip7702CodeLoad<()>, - /// Is account empty, if true account is not created. - pub is_empty: bool, -} - -impl Deref for AccountLoad { - type Target = Eip7702CodeLoad<()>; - - fn deref(&self) -> &Self::Target { - &self.load - } -} - -impl DerefMut for AccountLoad { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.load - } -} - -/// State load information that contains the data and if the account or storage is cold loaded. -#[derive(Clone, Debug, Default, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct StateLoad { - /// returned data - pub data: T, - /// True if account is cold loaded. - pub is_cold: bool, -} - -impl Deref for StateLoad { - type Target = T; - - fn deref(&self) -> &Self::Target { - &self.data - } -} - -impl DerefMut for StateLoad { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.data - } -} - -impl StateLoad { - /// Returns a new [`StateLoad`] with the given data and cold load status. - pub fn new(data: T, is_cold: bool) -> Self { - Self { data, is_cold } - } - - /// Maps the data of the [`StateLoad`] to a new value. - /// - /// Useful for transforming the data of the [`StateLoad`] without changing the cold load status. - pub fn map(self, f: F) -> StateLoad - where - F: FnOnce(T) -> B, - { - StateLoad::new(f(self.data), self.is_cold) - } -} - -/// EIP-7702 code load result that contains optional delegation is_cold information. -/// -/// [`Self::is_delegate_account_cold`] will be [`Some`] if account has delegation. -#[derive(Clone, Debug, Default, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct Eip7702CodeLoad { - /// returned data - pub state_load: StateLoad, - /// True if account has delegate code and delegated account is cold loaded. - pub is_delegate_account_cold: Option, -} - -impl Deref for Eip7702CodeLoad { - type Target = StateLoad; - - fn deref(&self) -> &Self::Target { - &self.state_load - } -} - -impl DerefMut for Eip7702CodeLoad { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.state_load - } -} - -impl Eip7702CodeLoad { - /// Returns a new [`Eip7702CodeLoad`] with the given data and without delegation. - pub fn new_state_load(state_load: StateLoad) -> Self { - Self { - state_load, - is_delegate_account_cold: None, - } - } - - /// Returns a new [`Eip7702CodeLoad`] with the given data and without delegation. - pub fn new_not_delegated(data: T, is_cold: bool) -> Self { - Self { - state_load: StateLoad::new(data, is_cold), - is_delegate_account_cold: None, - } - } - - /// Deconstructs the [`Eip7702CodeLoad`] by extracting data and - /// returning a new [`Eip7702CodeLoad`] with empty data. - pub fn into_components(self) -> (T, Eip7702CodeLoad<()>) { - let is_cold = self.is_cold; - ( - self.state_load.data, - Eip7702CodeLoad { - state_load: StateLoad::new((), is_cold), - is_delegate_account_cold: self.is_delegate_account_cold, - }, - ) - } - - /// Sets the delegation cold load status. - pub fn set_delegate_load(&mut self, is_delegate_account_cold: bool) { - self.is_delegate_account_cold = Some(is_delegate_account_cold); - } - - /// Returns a new [`Eip7702CodeLoad`] with the given data and delegation cold load status. - pub fn new(state_load: StateLoad, is_delegate_account_cold: bool) -> Self { - Self { - state_load, - is_delegate_account_cold: Some(is_delegate_account_cold), - } - } -} - /// Result of a selfdestruct action. /// /// Value returned are needed to calculate the gas spent. @@ -257,18 +132,19 @@ pub struct SelfDestructResult { pub previously_destroyed: bool, } -#[cfg(test)] -mod tests { - use database_interface::EmptyDB; - use wiring::EthereumWiring; +// TODO TEST +// #[cfg(test)] +// mod tests { +// use database_interface::EmptyDB; +// use context_interface::EthereumWiring; - use super::*; +// use super::*; - fn assert_host() {} +// fn assert_host() {} - #[test] - fn object_safety() { - assert_host::>>(); - assert_host::>>(); - } -} +// #[test] +// fn object_safety() { +// assert_host::>>(); +// assert_host::>>(); +// } +// } diff --git a/crates/interpreter/src/host/dummy.rs b/crates/interpreter/src/host/dummy.rs index c181210306..3b04283ada 100644 --- a/crates/interpreter/src/host/dummy.rs +++ b/crates/interpreter/src/host/dummy.rs @@ -1,35 +1,38 @@ use crate::{Host, SStoreResult, SelfDestructResult}; -use derive_where::derive_where; +use context_interface::{Block, Cfg, CfgEnv, Transaction}; use primitives::{hash_map::Entry, Address, Bytes, HashMap, Log, B256, KECCAK_EMPTY, U256}; use std::vec::Vec; -use wiring::{ - default::{Env, EnvWiring}, - EvmWiring, -}; use super::{AccountLoad, Eip7702CodeLoad, StateLoad}; /// A dummy [Host] implementation. -#[derive_where(Clone, Debug, Default; EvmWiringT::Block, EvmWiringT::Transaction)] -pub struct DummyHost +#[derive(Clone, Debug, Default)] +pub struct DummyHost where - EvmWiringT: EvmWiring, + BLOCK: Block, + TX: Transaction, + CFG: Cfg, { - pub env: Env, + pub tx: TX, + pub block: BLOCK, + pub cfg: CFG, pub storage: HashMap, pub transient_storage: HashMap, pub log: Vec, } -impl DummyHost +impl DummyHost where - EvmWiringT: EvmWiring, + BLOCK: Block, + TX: Transaction, { - /// Create a new dummy host with the given [`Env`]. + /// Create a new dummy host with the given [`Transaction`] and [`Block`]. #[inline] - pub fn new(env: EnvWiring) -> Self { + pub fn new(tx: TX, block: BLOCK) -> Self { Self { - env, + tx, + block, + cfg: CfgEnv::default(), storage: HashMap::default(), transient_storage: HashMap::default(), log: Vec::new(), @@ -44,20 +47,24 @@ where } } -impl Host for DummyHost -where - EvmWiringT: EvmWiring, -{ - type EvmWiringT = EvmWiringT; +impl Host for DummyHost { + type TX = TX; + type BLOCK = BLOCK; + type CFG = CFG; + + #[inline] + fn tx(&self) -> &Self::TX { + &self.tx + } #[inline] - fn env(&self) -> &EnvWiring { - &self.env + fn block(&self) -> &Self::BLOCK { + &self.block } #[inline] - fn env_mut(&mut self) -> &mut EnvWiring { - &mut self.env + fn cfg(&self) -> &Self::CFG { + &self.cfg } #[inline] diff --git a/crates/interpreter/src/instruction_result.rs b/crates/interpreter/src/instruction_result.rs index 2783300395..ab04bc26eb 100644 --- a/crates/interpreter/src/instruction_result.rs +++ b/crates/interpreter/src/instruction_result.rs @@ -1,8 +1,8 @@ -use core::fmt::Debug; -use wiring::{ - result::{HaltReason, OutOfGasError, SuccessReason}, - HaltReasonTrait, +use context_interface::{ + journaled_state::TransferError, + result::{HaltReason, HaltReasonTrait, OutOfGasError, SuccessReason}, }; +use core::fmt::Debug; #[repr(u8)] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] @@ -91,7 +91,7 @@ pub enum InstructionResult { /// Legacy contract is calling opcode that is enabled only in EOF. EOFOpcodeDisabledInLegacy, /// Stack overflow in EOF subroutine function calls. - EOFFunctionStackOverflow, + SubRoutineStackOverflow, /// Aux data overflow, new aux data is larger than `u16` max size. EofAuxDataOverflow, /// Aux data is smaller then already present data size. @@ -100,6 +100,18 @@ pub enum InstructionResult { InvalidEXTCALLTarget, } +impl From for InstructionResult { + fn from(e: TransferError) -> Self { + match e { + TransferError::OutOfFunds => InstructionResult::OutOfFunds, + TransferError::OverflowPayment => InstructionResult::OverflowPayment, + TransferError::CreateCollision => InstructionResult::CreateCollision, + } + } +} + +impl InstructionResult {} + impl From for InstructionResult { fn from(value: SuccessReason) -> Self { match value { @@ -142,7 +154,7 @@ impl From for InstructionResult { HaltReason::CallTooDeep => Self::CallTooDeep, HaltReason::EofAuxDataOverflow => Self::EofAuxDataOverflow, HaltReason::EofAuxDataTooSmall => Self::EofAuxDataTooSmall, - HaltReason::EOFFunctionStackOverflow => Self::EOFFunctionStackOverflow, + HaltReason::SubRoutineStackOverflow => Self::SubRoutineStackOverflow, HaltReason::InvalidEXTCALLTarget => Self::InvalidEXTCALLTarget, } } @@ -199,7 +211,7 @@ macro_rules! return_error { | $crate::InstructionResult::FatalExternalError | $crate::InstructionResult::ReturnContractInNotInitEOF | $crate::InstructionResult::EOFOpcodeDisabledInLegacy - | $crate::InstructionResult::EOFFunctionStackOverflow + | $crate::InstructionResult::SubRoutineStackOverflow | $crate::InstructionResult::EofAuxDataTooSmall | $crate::InstructionResult::EofAuxDataOverflow | $crate::InstructionResult::InvalidEXTCALLTarget @@ -213,6 +225,16 @@ impl InstructionResult { matches!(self, crate::return_ok!()) } + #[inline] + pub const fn is_ok_or_revert(self) -> bool { + matches!(self, crate::return_ok!() | crate::return_revert!()) + } + + #[inline] + pub const fn is_continue(self) -> bool { + matches!(self, InstructionResult::Continue) + } + /// Returns whether the result is a revert. #[inline] pub const fn is_revert(self) -> bool { @@ -286,6 +308,12 @@ impl SuccessOrHalt { } } +impl From for SuccessOrHalt { + fn from(reason: HaltReason) -> Self { + SuccessOrHalt::Halt(reason.into()) + } +} + impl From for SuccessOrHalt { fn from(result: InstructionResult) -> Self { match result { @@ -348,8 +376,8 @@ impl From for SuccessOrHalt { Self::Halt(HaltReason::OpcodeNotFound.into()) } - InstructionResult::EOFFunctionStackOverflow => { - Self::Halt(HaltReason::EOFFunctionStackOverflow.into()) + InstructionResult::SubRoutineStackOverflow => { + Self::Halt(HaltReason::SubRoutineStackOverflow.into()) } InstructionResult::ReturnContract => Self::Success(SuccessReason::EofReturnContract), InstructionResult::EofAuxDataOverflow => { diff --git a/crates/interpreter/src/instructions.rs b/crates/interpreter/src/instructions.rs index ae411d35de..2b7250b150 100644 --- a/crates/interpreter/src/instructions.rs +++ b/crates/interpreter/src/instructions.rs @@ -16,19 +16,20 @@ pub mod system; pub mod tx_info; pub mod utility; -use crate::Host; -use specification::hardfork::Spec; +use crate::{interpreter_types::InterpreterTypes, Host}; /// Returns the instruction function for the given opcode and spec. -pub const fn instruction(opcode: u8) -> crate::table::Instruction { - let table = instruction_table::(); +pub const fn instruction( + opcode: u8, +) -> crate::table::Instruction { + let table = instruction_table::(); table[opcode as usize] } -pub const fn instruction_table() -> [crate::table::Instruction; 256] -{ +pub const fn instruction_table( +) -> [crate::table::Instruction; 256] { use bytecode::opcode::*; - let mut table = [control::unknown as crate::table::Instruction; 256]; + let mut table = [control::unknown as crate::table::Instruction; 256]; table[STOP as usize] = control::stop; table[ADD as usize] = arithmetic::add; @@ -43,7 +44,7 @@ pub const fn instruction_table() -> [crate::table: table[SMOD as usize] = arithmetic::smod; table[ADDMOD as usize] = arithmetic::addmod; table[MULMOD as usize] = arithmetic::mulmod; - table[EXP as usize] = arithmetic::exp::; + table[EXP as usize] = arithmetic::exp; table[SIGNEXTEND as usize] = arithmetic::signextend; table[LT as usize] = bitwise::lt; @@ -57,14 +58,14 @@ pub const fn instruction_table() -> [crate::table: table[XOR as usize] = bitwise::bitxor; table[NOT as usize] = bitwise::not; table[BYTE as usize] = bitwise::byte; - table[SHL as usize] = bitwise::shl::; - table[SHR as usize] = bitwise::shr::; - table[SAR as usize] = bitwise::sar::; + table[SHL as usize] = bitwise::shl; + table[SHR as usize] = bitwise::shr; + table[SAR as usize] = bitwise::sar; table[KECCAK256 as usize] = system::keccak256; table[ADDRESS as usize] = system::address; - table[BALANCE as usize] = host::balance::; + table[BALANCE as usize] = host::balance; table[ORIGIN as usize] = tx_info::origin; table[CALLER as usize] = system::caller; table[CALLVALUE as usize] = system::callvalue; @@ -75,112 +76,112 @@ pub const fn instruction_table() -> [crate::table: table[CODECOPY as usize] = system::codecopy; table[GASPRICE as usize] = tx_info::gasprice; - table[EXTCODESIZE as usize] = host::extcodesize::; - table[EXTCODECOPY as usize] = host::extcodecopy::; - table[RETURNDATASIZE as usize] = system::returndatasize::; - table[RETURNDATACOPY as usize] = system::returndatacopy::; - table[EXTCODEHASH as usize] = host::extcodehash::; - table[BLOCKHASH as usize] = host::blockhash::; + table[EXTCODESIZE as usize] = host::extcodesize; + table[EXTCODECOPY as usize] = host::extcodecopy; + table[RETURNDATASIZE as usize] = system::returndatasize; + table[RETURNDATACOPY as usize] = system::returndatacopy; + table[EXTCODEHASH as usize] = host::extcodehash; + table[BLOCKHASH as usize] = host::blockhash; table[COINBASE as usize] = block_info::coinbase; table[TIMESTAMP as usize] = block_info::timestamp; table[NUMBER as usize] = block_info::block_number; - table[DIFFICULTY as usize] = block_info::difficulty::; + table[DIFFICULTY as usize] = block_info::difficulty; table[GASLIMIT as usize] = block_info::gaslimit; - table[CHAINID as usize] = block_info::chainid::; - table[SELFBALANCE as usize] = host::selfbalance::; - table[BASEFEE as usize] = block_info::basefee::; - table[BLOBHASH as usize] = tx_info::blob_hash::; - table[BLOBBASEFEE as usize] = block_info::blob_basefee::; + table[CHAINID as usize] = block_info::chainid; + table[SELFBALANCE as usize] = host::selfbalance; + table[BASEFEE as usize] = block_info::basefee; + table[BLOBHASH as usize] = tx_info::blob_hash; + table[BLOBBASEFEE as usize] = block_info::blob_basefee; table[POP as usize] = stack::pop; table[MLOAD as usize] = memory::mload; table[MSTORE as usize] = memory::mstore; table[MSTORE8 as usize] = memory::mstore8; - table[SLOAD as usize] = host::sload::; - table[SSTORE as usize] = host::sstore::; + table[SLOAD as usize] = host::sload; + table[SSTORE as usize] = host::sstore; table[JUMP as usize] = control::jump; table[JUMPI as usize] = control::jumpi; table[PC as usize] = control::pc; table[MSIZE as usize] = memory::msize; table[GAS as usize] = system::gas; table[JUMPDEST as usize] = control::jumpdest_or_nop; - table[TLOAD as usize] = host::tload::; - table[TSTORE as usize] = host::tstore::; - table[MCOPY as usize] = memory::mcopy::; - - table[PUSH0 as usize] = stack::push0::; - table[PUSH1 as usize] = stack::push::<1, H>; - table[PUSH2 as usize] = stack::push::<2, H>; - table[PUSH3 as usize] = stack::push::<3, H>; - table[PUSH4 as usize] = stack::push::<4, H>; - table[PUSH5 as usize] = stack::push::<5, H>; - table[PUSH6 as usize] = stack::push::<6, H>; - table[PUSH7 as usize] = stack::push::<7, H>; - table[PUSH8 as usize] = stack::push::<8, H>; - table[PUSH9 as usize] = stack::push::<9, H>; - table[PUSH10 as usize] = stack::push::<10, H>; - table[PUSH11 as usize] = stack::push::<11, H>; - table[PUSH12 as usize] = stack::push::<12, H>; - table[PUSH13 as usize] = stack::push::<13, H>; - table[PUSH14 as usize] = stack::push::<14, H>; - table[PUSH15 as usize] = stack::push::<15, H>; - table[PUSH16 as usize] = stack::push::<16, H>; - table[PUSH17 as usize] = stack::push::<17, H>; - table[PUSH18 as usize] = stack::push::<18, H>; - table[PUSH19 as usize] = stack::push::<19, H>; - table[PUSH20 as usize] = stack::push::<20, H>; - table[PUSH21 as usize] = stack::push::<21, H>; - table[PUSH22 as usize] = stack::push::<22, H>; - table[PUSH23 as usize] = stack::push::<23, H>; - table[PUSH24 as usize] = stack::push::<24, H>; - table[PUSH25 as usize] = stack::push::<25, H>; - table[PUSH26 as usize] = stack::push::<26, H>; - table[PUSH27 as usize] = stack::push::<27, H>; - table[PUSH28 as usize] = stack::push::<28, H>; - table[PUSH29 as usize] = stack::push::<29, H>; - table[PUSH30 as usize] = stack::push::<30, H>; - table[PUSH31 as usize] = stack::push::<31, H>; - table[PUSH32 as usize] = stack::push::<32, H>; - - table[DUP1 as usize] = stack::dup::<1, H>; - table[DUP2 as usize] = stack::dup::<2, H>; - table[DUP3 as usize] = stack::dup::<3, H>; - table[DUP4 as usize] = stack::dup::<4, H>; - table[DUP5 as usize] = stack::dup::<5, H>; - table[DUP6 as usize] = stack::dup::<6, H>; - table[DUP7 as usize] = stack::dup::<7, H>; - table[DUP8 as usize] = stack::dup::<8, H>; - table[DUP9 as usize] = stack::dup::<9, H>; - table[DUP10 as usize] = stack::dup::<10, H>; - table[DUP11 as usize] = stack::dup::<11, H>; - table[DUP12 as usize] = stack::dup::<12, H>; - table[DUP13 as usize] = stack::dup::<13, H>; - table[DUP14 as usize] = stack::dup::<14, H>; - table[DUP15 as usize] = stack::dup::<15, H>; - table[DUP16 as usize] = stack::dup::<16, H>; - - table[SWAP1 as usize] = stack::swap::<1, H>; - table[SWAP2 as usize] = stack::swap::<2, H>; - table[SWAP3 as usize] = stack::swap::<3, H>; - table[SWAP4 as usize] = stack::swap::<4, H>; - table[SWAP5 as usize] = stack::swap::<5, H>; - table[SWAP6 as usize] = stack::swap::<6, H>; - table[SWAP7 as usize] = stack::swap::<7, H>; - table[SWAP8 as usize] = stack::swap::<8, H>; - table[SWAP9 as usize] = stack::swap::<9, H>; - table[SWAP10 as usize] = stack::swap::<10, H>; - table[SWAP11 as usize] = stack::swap::<11, H>; - table[SWAP12 as usize] = stack::swap::<12, H>; - table[SWAP13 as usize] = stack::swap::<13, H>; - table[SWAP14 as usize] = stack::swap::<14, H>; - table[SWAP15 as usize] = stack::swap::<15, H>; - table[SWAP16 as usize] = stack::swap::<16, H>; - - table[LOG0 as usize] = host::log::<0, H>; - table[LOG1 as usize] = host::log::<1, H>; - table[LOG2 as usize] = host::log::<2, H>; - table[LOG3 as usize] = host::log::<3, H>; - table[LOG4 as usize] = host::log::<4, H>; + table[TLOAD as usize] = host::tload; + table[TSTORE as usize] = host::tstore; + table[MCOPY as usize] = memory::mcopy; + + table[PUSH0 as usize] = stack::push0; + table[PUSH1 as usize] = stack::push::<1, _, _>; + table[PUSH2 as usize] = stack::push::<2, _, _>; + table[PUSH3 as usize] = stack::push::<3, _, _>; + table[PUSH4 as usize] = stack::push::<4, _, _>; + table[PUSH5 as usize] = stack::push::<5, _, _>; + table[PUSH6 as usize] = stack::push::<6, _, _>; + table[PUSH7 as usize] = stack::push::<7, _, _>; + table[PUSH8 as usize] = stack::push::<8, _, _>; + table[PUSH9 as usize] = stack::push::<9, _, _>; + table[PUSH10 as usize] = stack::push::<10, _, _>; + table[PUSH11 as usize] = stack::push::<11, _, _>; + table[PUSH12 as usize] = stack::push::<12, _, _>; + table[PUSH13 as usize] = stack::push::<13, _, _>; + table[PUSH14 as usize] = stack::push::<14, _, _>; + table[PUSH15 as usize] = stack::push::<15, _, _>; + table[PUSH16 as usize] = stack::push::<16, _, _>; + table[PUSH17 as usize] = stack::push::<17, _, _>; + table[PUSH18 as usize] = stack::push::<18, _, _>; + table[PUSH19 as usize] = stack::push::<19, _, _>; + table[PUSH20 as usize] = stack::push::<20, _, _>; + table[PUSH21 as usize] = stack::push::<21, _, _>; + table[PUSH22 as usize] = stack::push::<22, _, _>; + table[PUSH23 as usize] = stack::push::<23, _, _>; + table[PUSH24 as usize] = stack::push::<24, _, _>; + table[PUSH25 as usize] = stack::push::<25, _, _>; + table[PUSH26 as usize] = stack::push::<26, _, _>; + table[PUSH27 as usize] = stack::push::<27, _, _>; + table[PUSH28 as usize] = stack::push::<28, _, _>; + table[PUSH29 as usize] = stack::push::<29, _, _>; + table[PUSH30 as usize] = stack::push::<30, _, _>; + table[PUSH31 as usize] = stack::push::<31, _, _>; + table[PUSH32 as usize] = stack::push::<32, _, _>; + + table[DUP1 as usize] = stack::dup::<1, _, _>; + table[DUP2 as usize] = stack::dup::<2, _, _>; + table[DUP3 as usize] = stack::dup::<3, _, _>; + table[DUP4 as usize] = stack::dup::<4, _, _>; + table[DUP5 as usize] = stack::dup::<5, _, _>; + table[DUP6 as usize] = stack::dup::<6, _, _>; + table[DUP7 as usize] = stack::dup::<7, _, _>; + table[DUP8 as usize] = stack::dup::<8, _, _>; + table[DUP9 as usize] = stack::dup::<9, _, _>; + table[DUP10 as usize] = stack::dup::<10, _, _>; + table[DUP11 as usize] = stack::dup::<11, _, _>; + table[DUP12 as usize] = stack::dup::<12, _, _>; + table[DUP13 as usize] = stack::dup::<13, _, _>; + table[DUP14 as usize] = stack::dup::<14, _, _>; + table[DUP15 as usize] = stack::dup::<15, _, _>; + table[DUP16 as usize] = stack::dup::<16, _, _>; + + table[SWAP1 as usize] = stack::swap::<1, _, _>; + table[SWAP2 as usize] = stack::swap::<2, _, _>; + table[SWAP3 as usize] = stack::swap::<3, _, _>; + table[SWAP4 as usize] = stack::swap::<4, _, _>; + table[SWAP5 as usize] = stack::swap::<5, _, _>; + table[SWAP6 as usize] = stack::swap::<6, _, _>; + table[SWAP7 as usize] = stack::swap::<7, _, _>; + table[SWAP8 as usize] = stack::swap::<8, _, _>; + table[SWAP9 as usize] = stack::swap::<9, _, _>; + table[SWAP10 as usize] = stack::swap::<10, _, _>; + table[SWAP11 as usize] = stack::swap::<11, _, _>; + table[SWAP12 as usize] = stack::swap::<12, _, _>; + table[SWAP13 as usize] = stack::swap::<13, _, _>; + table[SWAP14 as usize] = stack::swap::<14, _, _>; + table[SWAP15 as usize] = stack::swap::<15, _, _>; + table[SWAP16 as usize] = stack::swap::<16, _, _>; + + table[LOG0 as usize] = host::log::<0, _>; + table[LOG1 as usize] = host::log::<1, _>; + table[LOG2 as usize] = host::log::<2, _>; + table[LOG3 as usize] = host::log::<3, _>; + table[LOG4 as usize] = host::log::<4, _>; table[DATALOAD as usize] = data::data_load; table[DATALOADN as usize] = data::data_loadn; @@ -201,47 +202,46 @@ pub const fn instruction_table() -> [crate::table: table[RETURNCONTRACT as usize] = contract::return_contract; - table[CREATE as usize] = contract::create::; - table[CALL as usize] = contract::call::; - table[CALLCODE as usize] = contract::call_code::; + table[CREATE as usize] = contract::create::<_, false, _>; + table[CALL as usize] = contract::call; + table[CALLCODE as usize] = contract::call_code; table[RETURN as usize] = control::ret; - table[DELEGATECALL as usize] = contract::delegate_call::; - table[CREATE2 as usize] = contract::create::; + table[DELEGATECALL as usize] = contract::delegate_call; + table[CREATE2 as usize] = contract::create::<_, true, _>; table[RETURNDATALOAD as usize] = system::returndataload; - table[EXTCALL as usize] = contract::extcall::; - table[EXTDELEGATECALL as usize] = contract::extdelegatecall::; - table[STATICCALL as usize] = contract::static_call::; + table[EXTCALL as usize] = contract::extcall; + table[EXTDELEGATECALL as usize] = contract::extdelegatecall; + table[STATICCALL as usize] = contract::static_call; table[EXTSTATICCALL as usize] = contract::extstaticcall; - table[REVERT as usize] = control::revert::; + table[REVERT as usize] = control::revert; table[INVALID as usize] = control::invalid; - table[SELFDESTRUCT as usize] = host::selfdestruct::; + table[SELFDESTRUCT as usize] = host::selfdestruct; table } #[cfg(test)] mod tests { - use super::*; - use crate::DummyHost; - use bytecode::opcode::*; - use specification::hardfork::LatestSpec; - use wiring::DefaultEthereumWiring; - - #[test] - fn all_instructions_and_opcodes_used() { - // known unknown instruction we compare it with other instructions from table. - let unknown_instruction = 0x0C_usize; - let instr_table = instruction_table::, LatestSpec>(); - - let unknown_istr = instr_table[unknown_instruction]; - for (i, instr) in instr_table.iter().enumerate() { - let is_opcode_unknown = OpCode::new(i as u8).is_none(); - let is_instr_unknown = *instr == unknown_istr; - assert_eq!( - is_instr_unknown, is_opcode_unknown, - "Opcode 0x{:X?} is not handled", - i - ); - } - } + // use super::*; + // use crate::DummyHost; + // use bytecode::opcode::*; + + // TODO define EthEthereumWire + // #[test] + // fn all_instructions_and_opcodes_used() { + // // known unknown instruction we compare it with other instructions from table. + // let unknown_instruction = 0x0C_usize; + // let instr_table = instruction_table::>(); + + // let unknown_istr = instr_table[unknown_instruction]; + // for (i, instr) in instr_table.iter().enumerate() { + // let is_opcode_unknown = OpCode::new(i as u8).is_none(); + // let is_instr_unknown = *instr == unknown_istr; + // assert_eq!( + // is_instr_unknown, is_opcode_unknown, + // "Opcode 0x{:X?} is not handled", + // i + // ); + // } + // } } diff --git a/crates/interpreter/src/instructions/arithmetic.rs b/crates/interpreter/src/instructions/arithmetic.rs index 0f3753bb3b..e599920d3a 100644 --- a/crates/interpreter/src/instructions/arithmetic.rs +++ b/crates/interpreter/src/instructions/arithmetic.rs @@ -1,69 +1,104 @@ use super::i256::{i256_div, i256_mod}; -use crate::{gas, Host, Interpreter}; +use crate::{ + gas, + interpreter::Interpreter, + interpreter_types::{InterpreterTypes, LoopControl, RuntimeFlag, StackTrait}, + Host, +}; use primitives::U256; -use specification::hardfork::Spec; -pub fn add(interpreter: &mut Interpreter, _host: &mut H) { +pub fn add( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); + popn_top!([op1], op2, interpreter); *op2 = op1.wrapping_add(*op2); } -pub fn mul(interpreter: &mut Interpreter, _host: &mut H) { +pub fn mul( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::LOW); - pop_top!(interpreter, op1, op2); + popn_top!([op1], op2, interpreter); *op2 = op1.wrapping_mul(*op2); } -pub fn sub(interpreter: &mut Interpreter, _host: &mut H) { +pub fn sub( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); + popn_top!([op1], op2, interpreter); *op2 = op1.wrapping_sub(*op2); } -pub fn div(interpreter: &mut Interpreter, _host: &mut H) { +pub fn div( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::LOW); - pop_top!(interpreter, op1, op2); + popn_top!([op1], op2, interpreter); if !op2.is_zero() { *op2 = op1.wrapping_div(*op2); } } -pub fn sdiv(interpreter: &mut Interpreter, _host: &mut H) { +pub fn sdiv( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::LOW); - pop_top!(interpreter, op1, op2); + popn_top!([op1], op2, interpreter); *op2 = i256_div(op1, *op2); } -pub fn rem(interpreter: &mut Interpreter, _host: &mut H) { +pub fn rem( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::LOW); - pop_top!(interpreter, op1, op2); + popn_top!([op1], op2, interpreter); if !op2.is_zero() { *op2 = op1.wrapping_rem(*op2); } } -pub fn smod(interpreter: &mut Interpreter, _host: &mut H) { +pub fn smod( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::LOW); - pop_top!(interpreter, op1, op2); + popn_top!([op1], op2, interpreter); *op2 = i256_mod(op1, *op2) } -pub fn addmod(interpreter: &mut Interpreter, _host: &mut H) { +pub fn addmod( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::MID); - pop_top!(interpreter, op1, op2, op3); + popn_top!([op1, op2], op3, interpreter); *op3 = op1.add_mod(op2, *op3) } -pub fn mulmod(interpreter: &mut Interpreter, _host: &mut H) { +pub fn mulmod( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::MID); - pop_top!(interpreter, op1, op2, op3); + popn_top!([op1, op2], op3, interpreter); *op3 = op1.mul_mod(op2, *op3) } -pub fn exp(interpreter: &mut Interpreter, _host: &mut H) { - pop_top!(interpreter, op1, op2); - gas_or_fail!(interpreter, gas::exp_cost(SPEC::SPEC_ID, *op2)); +pub fn exp( + interpreter: &mut Interpreter, + _host: &mut H, +) { + let spec_id = interpreter.runtime_flag.spec_id(); + popn_top!([op1], op2, interpreter); + gas_or_fail!(interpreter, gas::exp_cost(spec_id, *op2)); *op2 = op1.pow(*op2); } @@ -84,9 +119,12 @@ pub fn exp(interpreter: &mut Interpreter, _host: & /// `y | !mask` where `|` is the bitwise `OR` and `!` is bitwise negation. Similarly, if /// `b == 0` then the yellow paper says the output should start with all zeros, then end with /// bits from `b`; this is equal to `y & mask` where `&` is bitwise `AND`. -pub fn signextend(interpreter: &mut Interpreter, _host: &mut H) { +pub fn signextend( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::LOW); - pop_top!(interpreter, ext, x); + popn_top!([ext], x, interpreter); // For 31 we also don't need to do anything. if ext < U256::from(31) { let ext = ext.as_limbs()[0]; diff --git a/crates/interpreter/src/instructions/bitwise.rs b/crates/interpreter/src/instructions/bitwise.rs index 0d9ec0d59e..2e6dc68a30 100644 --- a/crates/interpreter/src/instructions/bitwise.rs +++ b/crates/interpreter/src/instructions/bitwise.rs @@ -1,72 +1,116 @@ use super::i256::i256_cmp; -use crate::{gas, Host, Interpreter}; +use crate::{ + gas, + interpreter::Interpreter, + interpreter_types::{InterpreterTypes, LoopControl, RuntimeFlag, StackTrait}, + Host, +}; use core::cmp::Ordering; use primitives::U256; -use specification::hardfork::Spec; -pub fn lt(interpreter: &mut Interpreter, _host: &mut H) { +pub fn lt( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); + popn_top!([op1], op2, interpreter); *op2 = U256::from(op1 < *op2); } -pub fn gt(interpreter: &mut Interpreter, _host: &mut H) { +pub fn gt( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); + popn_top!([op1], op2, interpreter); + *op2 = U256::from(op1 > *op2); } -pub fn slt(interpreter: &mut Interpreter, _host: &mut H) { +pub fn slt( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); + popn_top!([op1], op2, interpreter); + *op2 = U256::from(i256_cmp(&op1, op2) == Ordering::Less); } -pub fn sgt(interpreter: &mut Interpreter, _host: &mut H) { +pub fn sgt( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); + popn_top!([op1], op2, interpreter); + *op2 = U256::from(i256_cmp(&op1, op2) == Ordering::Greater); } -pub fn eq(interpreter: &mut Interpreter, _host: &mut H) { +pub fn eq( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); + popn_top!([op1], op2, interpreter); + *op2 = U256::from(op1 == *op2); } -pub fn iszero(interpreter: &mut Interpreter, _host: &mut H) { +pub fn iszero( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1); + popn_top!([], op1, interpreter); *op1 = U256::from(op1.is_zero()); } -pub fn bitand(interpreter: &mut Interpreter, _host: &mut H) { +pub fn bitand( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); + popn_top!([op1], op2, interpreter); *op2 = op1 & *op2; } -pub fn bitor(interpreter: &mut Interpreter, _host: &mut H) { +pub fn bitor( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); + popn_top!([op1], op2, interpreter); + *op2 = op1 | *op2; } -pub fn bitxor(interpreter: &mut Interpreter, _host: &mut H) { +pub fn bitxor( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); + popn_top!([op1], op2, interpreter); + *op2 = op1 ^ *op2; } -pub fn not(interpreter: &mut Interpreter, _host: &mut H) { +pub fn not( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1); + popn_top!([], op1, interpreter); + *op1 = !*op1; } -pub fn byte(interpreter: &mut Interpreter, _host: &mut H) { +pub fn byte( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); + popn_top!([op1], op2, interpreter); let o1 = as_usize_saturated!(op1); *op2 = if o1 < 32 { @@ -78,10 +122,14 @@ pub fn byte(interpreter: &mut Interpreter, _host: &mut H) { } /// EIP-145: Bitwise shifting instructions in EVM -pub fn shl(interpreter: &mut Interpreter, _host: &mut H) { +pub fn shl( + interpreter: &mut Interpreter, + _host: &mut H, +) { check!(interpreter, CONSTANTINOPLE); gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); + popn_top!([op1], op2, interpreter); + let shift = as_usize_saturated!(op1); *op2 = if shift < 256 { *op2 << shift @@ -91,10 +139,14 @@ pub fn shl(interpreter: &mut Interpreter, _host: & } /// EIP-145: Bitwise shifting instructions in EVM -pub fn shr(interpreter: &mut Interpreter, _host: &mut H) { +pub fn shr( + interpreter: &mut Interpreter, + _host: &mut H, +) { check!(interpreter, CONSTANTINOPLE); gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); + popn_top!([op1], op2, interpreter); + let shift = as_usize_saturated!(op1); *op2 = if shift < 256 { *op2 >> shift @@ -104,10 +156,13 @@ pub fn shr(interpreter: &mut Interpreter, _host: & } /// EIP-145: Bitwise shifting instructions in EVM -pub fn sar(interpreter: &mut Interpreter, _host: &mut H) { +pub fn sar( + interpreter: &mut Interpreter, + _host: &mut H, +) { check!(interpreter, CONSTANTINOPLE); gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); + popn_top!([op1], op2, interpreter); let shift = as_usize_saturated!(op1); *op2 = if shift < 256 { @@ -119,13 +174,17 @@ pub fn sar(interpreter: &mut Interpreter, _host: & }; } +/* +TODO TESTS + #[cfg(test)] mod tests { use crate::instructions::bitwise::{byte, sar, shl, shr}; + use crate::interpreter_wiring::StackTrait; use crate::{Contract, DummyHost, Interpreter}; use primitives::{uint, U256}; use specification::hardfork::LatestSpec; - use wiring::{default::Env, DefaultEthereumWiring}; + use context_interface::{default::Env, DefaultEthereumWiring}; #[test] fn test_shift_left() { @@ -202,8 +261,8 @@ mod tests { host.clear(); push!(interpreter, test.value); push!(interpreter, test.shift); - shl::, LatestSpec>(&mut interpreter, &mut host); - pop!(interpreter, res); + shl::>(&mut interpreter, &mut host); + let res = interpreter.stack.pop().unwrap(); assert_eq!(res, test.expected); } } @@ -283,8 +342,8 @@ mod tests { host.clear(); push!(interpreter, test.value); push!(interpreter, test.shift); - shr::, LatestSpec>(&mut interpreter, &mut host); - pop!(interpreter, res); + shr::>(&mut interpreter, &mut host); + let res = interpreter.stack.pop().unwrap(); assert_eq!(res, test.expected); } } @@ -389,8 +448,8 @@ mod tests { host.clear(); push!(interpreter, test.value); push!(interpreter, test.shift); - sar::, LatestSpec>(&mut interpreter, &mut host); - pop!(interpreter, res); + sar::>(&mut interpreter, &mut host); + let res = interpreter.stack.pop().unwrap(); assert_eq!(res, test.expected); } } @@ -425,8 +484,9 @@ mod tests { push!(interpreter, test.input); push!(interpreter, U256::from(test.index)); byte(&mut interpreter, &mut host); - pop!(interpreter, res); + let res = interpreter.stack.pop().unwrap(); assert_eq!(res, test.expected, "Failed at index: {}", test.index); } } } +*/ diff --git a/crates/interpreter/src/instructions/block_info.rs b/crates/interpreter/src/instructions/block_info.rs index 9cb656fbc5..cbbececaef 100644 --- a/crates/interpreter/src/instructions/block_info.rs +++ b/crates/interpreter/src/instructions/block_info.rs @@ -1,57 +1,91 @@ -use crate::{gas, Host, Interpreter}; +use crate::{ + gas, + instructions::utility::IntoU256, + interpreter::Interpreter, + interpreter_types::{InterpreterTypes, LoopControl, RuntimeFlag, StackTrait}, + Host, +}; +use context_interface::{Block, Cfg}; use primitives::U256; -use specification::hardfork::{Spec, SpecId::*}; -use wiring::Block; +use specification::hardfork::SpecId::*; /// EIP-1344: ChainID opcode -pub fn chainid(interpreter: &mut Interpreter, host: &mut H) { +pub fn chainid( + interpreter: &mut Interpreter, + host: &mut H, +) { check!(interpreter, ISTANBUL); gas!(interpreter, gas::BASE); - push!(interpreter, U256::from(host.env().cfg.chain_id)); + push!(interpreter, U256::from(host.cfg().chain_id())); } -pub fn coinbase(interpreter: &mut Interpreter, host: &mut H) { +pub fn coinbase( + interpreter: &mut Interpreter, + host: &mut H, +) { gas!(interpreter, gas::BASE); - push_b256!(interpreter, host.env().block.coinbase().into_word()); + push!(interpreter, host.block().beneficiary().into_word().into()); } -pub fn timestamp(interpreter: &mut Interpreter, host: &mut H) { +pub fn timestamp( + interpreter: &mut Interpreter, + host: &mut H, +) { gas!(interpreter, gas::BASE); - push!(interpreter, *host.env().block.timestamp()); + push!(interpreter, *host.block().timestamp()); } -pub fn block_number(interpreter: &mut Interpreter, host: &mut H) { +pub fn block_number( + interpreter: &mut Interpreter, + host: &mut H, +) { gas!(interpreter, gas::BASE); - push!(interpreter, *host.env().block.number()); + push!(interpreter, *host.block().number()); } -pub fn difficulty(interpreter: &mut Interpreter, host: &mut H) { +pub fn difficulty( + interpreter: &mut Interpreter, + host: &mut H, +) { gas!(interpreter, gas::BASE); - if SPEC::enabled(MERGE) { - push_b256!(interpreter, *host.env().block.prevrandao().unwrap()); + if interpreter.runtime_flag.spec_id().is_enabled_in(MERGE) { + // Unwrap is safe as this fields is checked in validation handler. + push!( + interpreter, + (*host.block().prevrandao().unwrap()).into_u256() + ); } else { - push!(interpreter, *host.env().block.difficulty()); + push!(interpreter, *host.block().difficulty()); } } -pub fn gaslimit(interpreter: &mut Interpreter, host: &mut H) { +pub fn gaslimit( + interpreter: &mut Interpreter, + host: &mut H, +) { gas!(interpreter, gas::BASE); - push!(interpreter, *host.env().block.gas_limit()); + push!(interpreter, *host.block().gas_limit()); } /// EIP-3198: BASEFEE opcode -pub fn basefee(interpreter: &mut Interpreter, host: &mut H) { +pub fn basefee( + interpreter: &mut Interpreter, + host: &mut H, +) { check!(interpreter, LONDON); gas!(interpreter, gas::BASE); - push!(interpreter, *host.env().block.basefee()); + push!(interpreter, *host.block().basefee()); } /// EIP-7516: BLOBBASEFEE opcode -pub fn blob_basefee(interpreter: &mut Interpreter, host: &mut H) { +pub fn blob_basefee( + interpreter: &mut Interpreter, + host: &mut H, +) { check!(interpreter, CANCUN); gas!(interpreter, gas::BASE); push!( interpreter, - U256::from(host.env().block.blob_gasprice().unwrap_or_default()) + U256::from(host.block().blob_gasprice().unwrap_or_default()) ); } diff --git a/crates/interpreter/src/instructions/contract.rs b/crates/interpreter/src/instructions/contract.rs index 17c3f64631..ee29298680 100644 --- a/crates/interpreter/src/instructions/contract.rs +++ b/crates/interpreter/src/instructions/contract.rs @@ -4,34 +4,40 @@ pub use call_helpers::{calc_call_gas, get_memory_input_and_out_ranges, resize_me use crate::{ gas::{self, cost_per_word, EOF_CREATE_GAS, KECCAK256WORD, MIN_CALLEE_GAS}, + instructions::utility::IntoAddress, interpreter::Interpreter, - interpreter_action::NewFrameAction, + interpreter_action::FrameInput, + interpreter_types::{ + EofContainer, Immediates, InputsTrait, InterpreterTypes, Jumps, LoopControl, MemoryTrait, + ReturnData, RuntimeFlag, StackTrait, + }, CallInputs, CallScheme, CallValue, CreateInputs, EOFCreateInputs, Host, InstructionResult, - InterpreterAction, InterpreterResult, MAX_INITCODE_SIZE, + InterpreterAction, InterpreterResult, }; use bytecode::eof::{Eof, EofHeader}; +use context_interface::{Cfg, CreateScheme}; use core::cmp::max; use primitives::{keccak256, Address, Bytes, B256, U256}; -use specification::hardfork::{BerlinSpec, Spec, SpecId::*}; +use specification::hardfork::SpecId; use std::boxed::Box; -use wiring::default::CreateScheme; /// EOF Create instruction -pub fn eofcreate(interpreter: &mut Interpreter, _host: &mut H) { +pub fn eofcreate( + interpreter: &mut Interpreter, + _host: &mut H, +) { require_eof!(interpreter); require_non_staticcall!(interpreter); gas!(interpreter, EOF_CREATE_GAS); - let initcontainer_index = unsafe { *interpreter.instruction_pointer }; - pop!(interpreter, value, salt, data_offset, data_size); - - let sub_container = interpreter - .eof() - .expect("EOF is set") - .body - .container_section - .get(initcontainer_index as usize) - .cloned() - .expect("EOF is checked"); + let initcontainer_index = interpreter.bytecode.read_u8(); + + popn!([value, salt, data_offset, data_size], interpreter); + + let container = interpreter + .bytecode + .eof_container(initcontainer_index as usize) + .expect("valid container") + .clone(); // resize memory and get return range. let Some(input_range) = resize_memory(interpreter, data_offset, data_size) else { @@ -39,16 +45,12 @@ pub fn eofcreate(interpreter: &mut Interpreter, _host: &mut H) }; let input = if !input_range.is_empty() { - interpreter - .shared_memory - .slice_range(input_range) - .to_vec() - .into() + interpreter.memory.slice(input_range).to_vec().into() } else { Bytes::new() }; - let eof = Eof::decode(sub_container.clone()).expect("Subcontainer is verified"); + let eof = Eof::decode(container.clone()).expect("Subcontainer is verified"); if !eof.body.is_data_filled { // should be always false as it is verified by eof verification. @@ -56,99 +58,108 @@ pub fn eofcreate(interpreter: &mut Interpreter, _host: &mut H) } // deduct gas for hash that is needed to calculate address. - gas_or_fail!( - interpreter, - cost_per_word(sub_container.len() as u64, KECCAK256WORD) - ); + gas_or_fail!(interpreter, cost_per_word(container.len(), KECCAK256WORD)); let created_address = interpreter - .contract - .target_address - .create2(salt.to_be_bytes(), keccak256(sub_container)); + .input + .target_address() + .create2(salt.to_be_bytes(), keccak256(container)); - let gas_limit = interpreter.gas().remaining_63_of_64_parts(); + let gas_limit = interpreter.control.gas().remaining_63_of_64_parts(); gas!(interpreter, gas_limit); // Send container for execution container is preverified. - interpreter.instruction_result = InstructionResult::CallOrCreate; - interpreter.next_action = InterpreterAction::NewFrame(NewFrameAction::EOFCreate(Box::new( - EOFCreateInputs::new_opcode( - interpreter.contract.target_address, - created_address, - value, - eof, - gas_limit, - input, - ), - ))); + interpreter.control.set_next_action( + InterpreterAction::NewFrame(FrameInput::EOFCreate(Box::new( + EOFCreateInputs::new_opcode( + interpreter.input.target_address(), + created_address, + value, + eof, + gas_limit, + input, + ), + ))), + InstructionResult::CallOrCreate, + ); - interpreter.instruction_pointer = unsafe { interpreter.instruction_pointer.offset(1) }; + interpreter.bytecode.relative_jump(1); } -pub fn return_contract(interpreter: &mut Interpreter, _host: &mut H) { - require_init_eof!(interpreter); - let deploy_container_index = unsafe { *interpreter.instruction_pointer }; - pop!(interpreter, aux_data_offset, aux_data_size); +pub fn return_contract( + interpreter: &mut Interpreter, + _host: &mut H, +) { + if !interpreter.runtime_flag.is_eof_init() { + interpreter + .control + .set_instruction_result(InstructionResult::ReturnContractInNotInitEOF); + return; + } + let deploy_container_index = interpreter.bytecode.read_u8(); + popn!([aux_data_offset, aux_data_size], interpreter); let aux_data_size = as_usize_or_fail!(interpreter, aux_data_size); - // important: offset must be ignored if len is zeros let container = interpreter - .eof() - .expect("EOF is set") - .body - .container_section - .get(deploy_container_index as usize) - .expect("EOF is checked") + .bytecode + .eof_container(deploy_container_index as usize) + .expect("valid container") .clone(); // convert to EOF so we can check data section size. let (eof_header, _) = EofHeader::decode(&container).expect("valid EOF header"); - let aux_slice = if aux_data_size != 0 { + let static_aux_size = eof_header.eof_size() - container.len(); + + // important: offset must be ignored if len is zeros + let mut output = if aux_data_size != 0 { let aux_data_offset = as_usize_or_fail!(interpreter, aux_data_offset); resize_memory!(interpreter, aux_data_offset, aux_data_size); - interpreter - .shared_memory - .slice(aux_data_offset, aux_data_size) + let aux_slice = interpreter.memory.slice_len(aux_data_offset, aux_data_size); + + [&container, aux_slice.as_ref()].concat() } else { - &[] + container.to_vec() }; - let static_aux_size = eof_header.eof_size() - container.len(); - // data_size - static_aux_size give us current data `container` size. // and with aux_slice len we can calculate new data size. - let new_data_size = eof_header.data_size as usize - static_aux_size + aux_slice.len(); + let new_data_size = eof_header.data_size as usize - static_aux_size + aux_data_size; if new_data_size > 0xFFFF { // aux data is too big - interpreter.instruction_result = InstructionResult::EofAuxDataOverflow; + interpreter + .control + .set_instruction_result(InstructionResult::EofAuxDataOverflow); return; } if new_data_size < eof_header.data_size as usize { // aux data is too small - interpreter.instruction_result = InstructionResult::EofAuxDataTooSmall; + interpreter + .control + .set_instruction_result(InstructionResult::EofAuxDataTooSmall); return; } let new_data_size = (new_data_size as u16).to_be_bytes(); - let mut output = [&container, aux_slice].concat(); // set new data size in eof bytes as we know exact index. output[eof_header.data_size_raw_i()..][..2].clone_from_slice(&new_data_size); let output: Bytes = output.into(); let result = InstructionResult::ReturnContract; - interpreter.instruction_result = result; - interpreter.next_action = crate::InterpreterAction::Return { - result: InterpreterResult { - output, - gas: interpreter.gas, - result, + let gas = *interpreter.control.gas(); + interpreter.control.set_next_action( + crate::InterpreterAction::Return { + result: InterpreterResult { + output, + gas, + result, + }, }, - }; + result, + ); } -pub fn extcall_input(interpreter: &mut Interpreter) -> Option { - pop_ret!(interpreter, input_offset, input_size, None); - +pub fn extcall_input(interpreter: &mut Interpreter) -> Option { + popn!([input_offset, input_size], interpreter, None); let return_memory_offset = resize_memory(interpreter, input_offset, input_size)?; if return_memory_offset.is_empty() { @@ -157,29 +168,41 @@ pub fn extcall_input(interpreter: &mut Interpreter) -> Option { Some(Bytes::copy_from_slice( interpreter - .shared_memory - .slice_range(return_memory_offset.clone()), + .memory + .slice(return_memory_offset.clone()) + .as_ref(), )) } -pub fn extcall_gas_calc( - interpreter: &mut Interpreter, +pub fn extcall_gas_calc( + interpreter: &mut Interpreter, host: &mut H, target: Address, transfers_value: bool, ) -> Option { let Some(account_load) = host.load_account_delegated(target) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; + interpreter + .control + .set_instruction_result(InstructionResult::FatalExternalError); return None; }; - // account_load.is_empty will be accounted if there is transfer value. - let call_cost = gas::call_cost(BerlinSpec::SPEC_ID, transfers_value, account_load); + // account_load.is_empty will be accounted if there is transfer value + // Berlin can be hardcoded as extcall came after berlin. + let call_cost = gas::call_cost( + interpreter.runtime_flag.spec_id(), + transfers_value, + account_load, + ); gas!(interpreter, call_cost, None); // 7. Calculate the gas available to callee as caller’s // remaining gas reduced by max(ceil(gas/64), MIN_RETAINED_GAS) (MIN_RETAINED_GAS is 5000). - let gas_reduce = max(interpreter.gas.remaining() / 64, 5000); - let gas_limit = interpreter.gas().remaining().saturating_sub(gas_reduce); + let gas_reduce = max(interpreter.control.gas().remaining() / 64, 5000); + let gas_limit = interpreter + .control + .gas() + .remaining() + .saturating_sub(gas_reduce); // The MIN_CALLEE_GAS rule is a replacement for stipend: // it simplifies the reasoning about the gas costs and is @@ -189,8 +212,8 @@ pub fn extcall_gas_calc( if gas_limit < MIN_CALLEE_GAS { // Push 1 to stack to indicate that call light failed. // It is safe to ignore stack overflow error as we already popped multiple values from stack. - let _ = interpreter.stack_mut().push(U256::from(1)); - interpreter.return_data_buffer.clear(); + let _ = interpreter.stack.push(U256::from(1)); + interpreter.return_data.buffer_mut().clear(); // Return none to continue execution. return None; } @@ -203,19 +226,26 @@ pub fn extcall_gas_calc( /// /// Valid address has first 12 bytes as zeroes. #[inline] -pub fn pop_extcall_target_address(interpreter: &mut Interpreter) -> Option
{ - pop_ret!(interpreter, target_address, None); +pub fn pop_extcall_target_address( + interpreter: &mut Interpreter, +) -> Option
{ + popn!([target_address], interpreter, None); let target_address = B256::from(target_address); // Check if target is left padded with zeroes. if target_address[..12].iter().any(|i| *i != 0) { - interpreter.instruction_result = InstructionResult::InvalidEXTCALLTarget; + interpreter + .control + .set_instruction_result(InstructionResult::InvalidEXTCALLTarget); return None; } // discard first 12 bytes. Some(Address::from_word(target_address)) } -pub fn extcall(interpreter: &mut Interpreter, host: &mut H) { +pub fn extcall( + interpreter: &mut Interpreter, + host: &mut H, +) { require_eof!(interpreter); // pop target address @@ -228,10 +258,12 @@ pub fn extcall(interpreter: &mut Interpreter, host return; }; - pop!(interpreter, value); + popn!([value], interpreter); let has_transfer = !value.is_zero(); - if interpreter.is_static && has_transfer { - interpreter.instruction_result = InstructionResult::CallNotAllowedInsideStatic; + if interpreter.runtime_flag.is_static() && has_transfer { + interpreter + .control + .set_instruction_result(InstructionResult::CallNotAllowedInsideStatic); return; } @@ -240,23 +272,27 @@ pub fn extcall(interpreter: &mut Interpreter, host }; // Call host to interact with target contract - interpreter.next_action = - InterpreterAction::NewFrame(NewFrameAction::Call(Box::new(CallInputs { + interpreter.control.set_next_action( + InterpreterAction::NewFrame(FrameInput::Call(Box::new(CallInputs { input, gas_limit, target_address, - caller: interpreter.contract.target_address, + caller: interpreter.input.target_address(), bytecode_address: target_address, value: CallValue::Transfer(value), scheme: CallScheme::ExtCall, - is_static: interpreter.is_static, + is_static: interpreter.runtime_flag.is_static(), is_eof: true, return_memory_offset: 0..0, - }))); - interpreter.instruction_result = InstructionResult::CallOrCreate; + }))), + InstructionResult::CallOrCreate, + ); } -pub fn extdelegatecall(interpreter: &mut Interpreter, host: &mut H) { +pub fn extdelegatecall( + interpreter: &mut Interpreter, + host: &mut H, +) { require_eof!(interpreter); // pop target address @@ -274,23 +310,27 @@ pub fn extdelegatecall(interpreter: &mut Interpret }; // Call host to interact with target contract - interpreter.next_action = - InterpreterAction::NewFrame(NewFrameAction::Call(Box::new(CallInputs { + interpreter.control.set_next_action( + InterpreterAction::NewFrame(FrameInput::Call(Box::new(CallInputs { input, gas_limit, - target_address: interpreter.contract.target_address, - caller: interpreter.contract.caller, + target_address: interpreter.input.target_address(), + caller: interpreter.input.caller_address(), bytecode_address: target_address, - value: CallValue::Apparent(interpreter.contract.call_value), + value: CallValue::Apparent(interpreter.input.call_value()), scheme: CallScheme::ExtDelegateCall, - is_static: interpreter.is_static, + is_static: interpreter.runtime_flag.is_static(), is_eof: true, return_memory_offset: 0..0, - }))); - interpreter.instruction_result = InstructionResult::CallOrCreate; + }))), + InstructionResult::CallOrCreate, + ); } -pub fn extstaticcall(interpreter: &mut Interpreter, host: &mut H) { +pub fn extstaticcall( + interpreter: &mut Interpreter, + host: &mut H, +) { require_eof!(interpreter); // pop target address @@ -308,24 +348,25 @@ pub fn extstaticcall(interpreter: &mut Interpreter, host: &mut }; // Call host to interact with target contract - interpreter.next_action = - InterpreterAction::NewFrame(NewFrameAction::Call(Box::new(CallInputs { + interpreter.control.set_next_action( + InterpreterAction::NewFrame(FrameInput::Call(Box::new(CallInputs { input, gas_limit, target_address, - caller: interpreter.contract.target_address, + caller: interpreter.input.target_address(), bytecode_address: target_address, value: CallValue::Transfer(U256::ZERO), scheme: CallScheme::ExtStaticCall, is_static: true, is_eof: true, return_memory_offset: 0..0, - }))); - interpreter.instruction_result = InstructionResult::CallOrCreate; + }))), + InstructionResult::CallOrCreate, + ); } -pub fn create( - interpreter: &mut Interpreter, +pub fn create( + interpreter: &mut Interpreter, host: &mut H, ) { require_non_staticcall!(interpreter); @@ -335,74 +376,84 @@ pub fn create( check!(interpreter, PETERSBURG); } - pop!(interpreter, value, code_offset, len); + popn!([value, code_offset, len], interpreter); let len = as_usize_or_fail!(interpreter, len); let mut code = Bytes::new(); if len != 0 { // EIP-3860: Limit and meter initcode - if SPEC::enabled(SHANGHAI) { + if interpreter + .runtime_flag + .spec_id() + .is_enabled_in(SpecId::SHANGHAI) + { // Limit is set as double of max contract bytecode size - let max_initcode_size = host - .env() - .cfg - .limit_contract_code_size - .map(|limit| limit.saturating_mul(2)) - .unwrap_or(MAX_INITCODE_SIZE); + let max_initcode_size = host.cfg().max_code_size().saturating_mul(2); if len > max_initcode_size { - interpreter.instruction_result = InstructionResult::CreateInitCodeSizeLimit; + interpreter + .control + .set_instruction_result(InstructionResult::CreateInitCodeSizeLimit); return; } - gas!(interpreter, gas::initcode_cost(len as u64)); + gas!(interpreter, gas::initcode_cost(len)); } let code_offset = as_usize_or_fail!(interpreter, code_offset); resize_memory!(interpreter, code_offset, len); - code = Bytes::copy_from_slice(interpreter.shared_memory.slice(code_offset, len)); + code = Bytes::copy_from_slice(interpreter.memory.slice_len(code_offset, len).as_ref()); } // EIP-1014: Skinny CREATE2 let scheme = if IS_CREATE2 { - pop!(interpreter, salt); + popn!([salt], interpreter); // SAFETY: len is reasonable in size as gas for it is already deducted. - gas_or_fail!(interpreter, gas::create2_cost(len.try_into().unwrap())); + gas_or_fail!(interpreter, gas::create2_cost(len)); CreateScheme::Create2 { salt } } else { gas!(interpreter, gas::CREATE); CreateScheme::Create }; - let mut gas_limit = interpreter.gas().remaining(); + let mut gas_limit = interpreter.control.gas().remaining(); // EIP-150: Gas cost changes for IO-heavy operations - if SPEC::enabled(TANGERINE) { + if interpreter + .runtime_flag + .spec_id() + .is_enabled_in(SpecId::TANGERINE) + { // take remaining gas and deduce l64 part of it. gas_limit -= gas_limit / 64 } gas!(interpreter, gas_limit); // Call host to interact with target contract - interpreter.next_action = - InterpreterAction::NewFrame(NewFrameAction::Create(Box::new(CreateInputs { - caller: interpreter.contract.target_address, + interpreter.control.set_next_action( + InterpreterAction::NewFrame(FrameInput::Create(Box::new(CreateInputs { + caller: interpreter.input.target_address(), scheme, value, init_code: code, gas_limit, - }))); - interpreter.instruction_result = InstructionResult::CallOrCreate; + }))), + InstructionResult::CallOrCreate, + ); } -pub fn call(interpreter: &mut Interpreter, host: &mut H) { - pop!(interpreter, local_gas_limit); - pop_address!(interpreter, to); +pub fn call( + interpreter: &mut Interpreter, + host: &mut H, +) { + popn!([local_gas_limit, to, value], 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); - pop!(interpreter, value); let has_transfer = !value.is_zero(); - if interpreter.is_static && has_transfer { - interpreter.instruction_result = InstructionResult::CallNotAllowedInsideStatic; + if interpreter.runtime_flag.is_static() && has_transfer { + interpreter + .control + .set_instruction_result(InstructionResult::CallNotAllowedInsideStatic); return; } @@ -411,11 +462,13 @@ pub fn call(interpreter: &mut Interpreter, host: & }; let Some(account_load) = host.load_account_delegated(to) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; + interpreter + .control + .set_instruction_result(InstructionResult::FatalExternalError); return; }; let Some(mut gas_limit) = - calc_call_gas::(interpreter, account_load, has_transfer, local_gas_limit) + calc_call_gas(interpreter, account_load, has_transfer, local_gas_limit) else { return; }; @@ -428,41 +481,46 @@ pub fn call(interpreter: &mut Interpreter, host: & } // Call host to interact with target contract - interpreter.next_action = - InterpreterAction::NewFrame(NewFrameAction::Call(Box::new(CallInputs { + interpreter.control.set_next_action( + InterpreterAction::NewFrame(FrameInput::Call(Box::new(CallInputs { input, gas_limit, target_address: to, - caller: interpreter.contract.target_address, + caller: interpreter.input.target_address(), bytecode_address: to, value: CallValue::Transfer(value), scheme: CallScheme::Call, - is_static: interpreter.is_static, + is_static: interpreter.runtime_flag.is_static(), is_eof: false, return_memory_offset, - }))); - interpreter.instruction_result = InstructionResult::CallOrCreate; + }))), + InstructionResult::CallOrCreate, + ); } -pub fn call_code(interpreter: &mut Interpreter, host: &mut H) { - pop!(interpreter, local_gas_limit); - pop_address!(interpreter, to); +pub fn call_code( + interpreter: &mut Interpreter, + host: &mut H, +) { + popn!([local_gas_limit, to, value], 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); - pop!(interpreter, value); + //pop!(interpreter, value); let Some((input, return_memory_offset)) = get_memory_input_and_out_ranges(interpreter) else { return; }; let Some(mut load) = host.load_account_delegated(to) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; + interpreter + .control + .set_instruction_result(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::(interpreter, load, !value.is_zero(), local_gas_limit) + let Some(mut gas_limit) = calc_call_gas(interpreter, load, !value.is_zero(), local_gas_limit) else { return; }; @@ -475,26 +533,30 @@ pub fn call_code(interpreter: &mut Interpreter, ho } // Call host to interact with target contract - interpreter.next_action = - InterpreterAction::NewFrame(NewFrameAction::Call(Box::new(CallInputs { + interpreter.control.set_next_action( + InterpreterAction::NewFrame(FrameInput::Call(Box::new(CallInputs { input, gas_limit, - target_address: interpreter.contract.target_address, - caller: interpreter.contract.target_address, + target_address: interpreter.input.target_address(), + caller: interpreter.input.target_address(), bytecode_address: to, value: CallValue::Transfer(value), scheme: CallScheme::CallCode, - is_static: interpreter.is_static, + is_static: interpreter.runtime_flag.is_static(), is_eof: false, return_memory_offset, - }))); - interpreter.instruction_result = InstructionResult::CallOrCreate; + }))), + InstructionResult::CallOrCreate, + ); } -pub fn delegate_call(interpreter: &mut Interpreter, host: &mut H) { +pub fn delegate_call( + interpreter: &mut Interpreter, + host: &mut H, +) { check!(interpreter, HOMESTEAD); - pop!(interpreter, local_gas_limit); - pop_address!(interpreter, to); + popn!([local_gas_limit, to], 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); @@ -503,38 +565,44 @@ pub fn delegate_call(interpreter: &mut Interpreter }; let Some(mut load) = host.load_account_delegated(to) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; + interpreter + .control + .set_instruction_result(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::(interpreter, load, false, local_gas_limit) else { + let Some(gas_limit) = calc_call_gas(interpreter, load, false, local_gas_limit) else { return; }; gas!(interpreter, gas_limit); // Call host to interact with target contract - interpreter.next_action = - InterpreterAction::NewFrame(NewFrameAction::Call(Box::new(CallInputs { + interpreter.control.set_next_action( + InterpreterAction::NewFrame(FrameInput::Call(Box::new(CallInputs { input, gas_limit, - target_address: interpreter.contract.target_address, - caller: interpreter.contract.caller, + target_address: interpreter.input.target_address(), + caller: interpreter.input.caller_address(), bytecode_address: to, - value: CallValue::Apparent(interpreter.contract.call_value), + value: CallValue::Apparent(interpreter.input.call_value()), scheme: CallScheme::DelegateCall, - is_static: interpreter.is_static, + is_static: interpreter.runtime_flag.is_static(), is_eof: false, return_memory_offset, - }))); - interpreter.instruction_result = InstructionResult::CallOrCreate; + }))), + InstructionResult::CallOrCreate, + ); } -pub fn static_call(interpreter: &mut Interpreter, host: &mut H) { +pub fn static_call( + interpreter: &mut Interpreter, + host: &mut H, +) { check!(interpreter, BYZANTIUM); - pop!(interpreter, local_gas_limit); - pop_address!(interpreter, to); + popn!([local_gas_limit, to], 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); @@ -543,29 +611,32 @@ pub fn static_call(interpreter: &mut Interpreter, }; let Some(mut load) = host.load_account_delegated(to) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; + interpreter + .control + .set_instruction_result(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::(interpreter, load, false, local_gas_limit) else { + let Some(gas_limit) = calc_call_gas(interpreter, load, false, local_gas_limit) else { return; }; gas!(interpreter, gas_limit); // Call host to interact with target contract - interpreter.next_action = - InterpreterAction::NewFrame(NewFrameAction::Call(Box::new(CallInputs { + interpreter.control.set_next_action( + InterpreterAction::NewFrame(FrameInput::Call(Box::new(CallInputs { input, gas_limit, target_address: to, - caller: interpreter.contract.target_address, + caller: interpreter.input.target_address(), bytecode_address: to, value: CallValue::Transfer(U256::ZERO), scheme: CallScheme::StaticCall, is_static: true, is_eof: false, return_memory_offset, - }))); - interpreter.instruction_result = InstructionResult::CallOrCreate; + }))), + InstructionResult::CallOrCreate, + ); } diff --git a/crates/interpreter/src/instructions/contract/call_helpers.rs b/crates/interpreter/src/instructions/contract/call_helpers.rs index 0de9baa548..320ee9faf9 100644 --- a/crates/interpreter/src/instructions/contract/call_helpers.rs +++ b/crates/interpreter/src/instructions/contract/call_helpers.rs @@ -1,19 +1,24 @@ -use crate::{gas, interpreter::Interpreter, AccountLoad}; +use crate::{ + gas, + interpreter::Interpreter, + interpreter_types::{InterpreterTypes, LoopControl, MemoryTrait, RuntimeFlag, StackTrait}, +}; +use context_interface::journaled_state::AccountLoad; use core::{cmp::min, ops::Range}; use primitives::{Bytes, U256}; -use specification::hardfork::{Spec, SpecId::*}; +use specification::hardfork::SpecId::*; #[inline] pub fn get_memory_input_and_out_ranges( - interpreter: &mut Interpreter, + interpreter: &mut Interpreter, ) -> Option<(Bytes, Range)> { - pop_ret!(interpreter, in_offset, in_len, out_offset, out_len, None); + popn!([in_offset, in_len, out_offset, out_len], interpreter, None); let in_range = resize_memory(interpreter, in_offset, in_len)?; let mut input = Bytes::new(); if !in_range.is_empty() { - input = Bytes::copy_from_slice(interpreter.shared_memory.slice_range(in_range)); + input = Bytes::copy_from_slice(interpreter.memory.slice(in_range).as_ref()); } let ret_range = resize_memory(interpreter, out_offset, out_len)?; @@ -24,7 +29,7 @@ pub fn get_memory_input_and_out_ranges( /// If `len` is 0 dont touch memory and return `usize::MAX` as offset and 0 as length. #[inline] pub fn resize_memory( - interpreter: &mut Interpreter, + interpreter: &mut Interpreter, offset: U256, len: U256, ) -> Option> { @@ -40,20 +45,24 @@ pub fn resize_memory( } #[inline] -pub fn calc_call_gas( - interpreter: &mut Interpreter, +pub fn calc_call_gas( + interpreter: &mut Interpreter, account_load: AccountLoad, has_transfer: bool, local_gas_limit: u64, ) -> Option { - let call_cost = gas::call_cost(SPEC::SPEC_ID, has_transfer, account_load); + let call_cost = gas::call_cost( + interpreter.runtime_flag.spec_id(), + has_transfer, + account_load, + ); gas!(interpreter, call_cost, None); // EIP-150: Gas cost changes for IO-heavy operations - let gas_limit = if SPEC::enabled(TANGERINE) { + let 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(), + interpreter.control.gas().remaining_63_of_64_parts(), local_gas_limit, ) } else { diff --git a/crates/interpreter/src/instructions/control.rs b/crates/interpreter/src/instructions/control.rs index cf4582912c..1759dabda3 100644 --- a/crates/interpreter/src/instructions/control.rs +++ b/crates/interpreter/src/instructions/control.rs @@ -1,227 +1,300 @@ -use super::utility::{read_i16, read_u16}; -use crate::{gas, Host, InstructionResult, Interpreter, InterpreterResult}; +use crate::{ + gas, + interpreter::Interpreter, + interpreter_types::{ + EofCodeInfo, Immediates, InterpreterTypes, Jumps, LoopControl, MemoryTrait, RuntimeFlag, + StackTrait, SubRoutineStack, + }, + Host, InstructionResult, InterpreterAction, InterpreterResult, +}; use primitives::{Bytes, U256}; -use specification::hardfork::Spec; -pub fn rjump(interpreter: &mut Interpreter, _host: &mut H) { +pub fn rjump( + interpreter: &mut Interpreter, + _host: &mut H, +) { require_eof!(interpreter); gas!(interpreter, gas::BASE); - let offset = unsafe { read_i16(interpreter.instruction_pointer) } as isize; + let offset = interpreter.bytecode.read_i16() as isize; // In spec it is +3 but pointer is already incremented in // `Interpreter::step` so for revm is +2. - interpreter.instruction_pointer = unsafe { interpreter.instruction_pointer.offset(offset + 2) }; + interpreter.bytecode.relative_jump(offset + 2); } -pub fn rjumpi(interpreter: &mut Interpreter, _host: &mut H) { +pub fn rjumpi( + interpreter: &mut Interpreter, + _host: &mut H, +) { require_eof!(interpreter); gas!(interpreter, gas::CONDITION_JUMP_GAS); - pop!(interpreter, condition); + popn!([condition], interpreter); // In spec it is +3 but pointer is already incremented in // `Interpreter::step` so for revm is +2. let mut offset = 2; if !condition.is_zero() { - offset += unsafe { read_i16(interpreter.instruction_pointer) } as isize; + offset += interpreter.bytecode.read_i16() as isize; } - interpreter.instruction_pointer = unsafe { interpreter.instruction_pointer.offset(offset) }; + interpreter.bytecode.relative_jump(offset); } -pub fn rjumpv(interpreter: &mut Interpreter, _host: &mut H) { +pub fn rjumpv( + interpreter: &mut Interpreter, + _host: &mut H, +) { require_eof!(interpreter); gas!(interpreter, gas::CONDITION_JUMP_GAS); - pop!(interpreter, case); + popn!([case], interpreter); let case = as_isize_saturated!(case); - let max_index = unsafe { *interpreter.instruction_pointer } as isize; + let max_index = interpreter.bytecode.read_u8() as isize; // for number of items we are adding 1 to max_index, multiply by 2 as each offset is 2 bytes // and add 1 for max_index itself. Note that revm already incremented the instruction pointer let mut offset = (max_index + 1) * 2 + 1; if case <= max_index { - offset += unsafe { - read_i16( - interpreter - .instruction_pointer - // offset for max_index that is one byte - .offset(1 + case * 2), - ) - } as isize; + offset += interpreter.bytecode.read_offset_i16(1 + case * 2) as isize; } - - interpreter.instruction_pointer = unsafe { interpreter.instruction_pointer.offset(offset) }; + interpreter.bytecode.relative_jump(offset); } -pub fn jump(interpreter: &mut Interpreter, _host: &mut H) { +pub fn jump( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::MID); - pop!(interpreter, target); + popn!([target], interpreter); jump_inner(interpreter, target); } -pub fn jumpi(interpreter: &mut Interpreter, _host: &mut H) { +pub fn jumpi( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::HIGH); - pop!(interpreter, target, cond); + popn!([target, cond], interpreter); + if !cond.is_zero() { jump_inner(interpreter, target); } } #[inline] -fn jump_inner(interpreter: &mut Interpreter, target: U256) { +fn jump_inner(interpreter: &mut Interpreter, target: U256) { let target = as_usize_or_fail!(interpreter, target, InstructionResult::InvalidJump); - if !interpreter.contract.is_valid_jump(target) { - interpreter.instruction_result = InstructionResult::InvalidJump; + if !interpreter.bytecode.is_valid_legacy_jump(target) { + interpreter + .control + .set_instruction_result(InstructionResult::InvalidJump); return; } // SAFETY: `is_valid_jump` ensures that `dest` is in bounds. - interpreter.instruction_pointer = unsafe { interpreter.bytecode.as_ptr().add(target) }; + interpreter.bytecode.absolute_jump(target); } -pub fn jumpdest_or_nop(interpreter: &mut Interpreter, _host: &mut H) { +pub fn jumpdest_or_nop( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::JUMPDEST); } -pub fn callf(interpreter: &mut Interpreter, _host: &mut H) { +pub fn callf( + interpreter: &mut Interpreter, + _host: &mut H, +) { require_eof!(interpreter); gas!(interpreter, gas::LOW); - let idx = unsafe { read_u16(interpreter.instruction_pointer) } as usize; - - if interpreter.function_stack.return_stack_len() >= 1024 { - interpreter.instruction_result = InstructionResult::EOFFunctionStackOverflow; - return; - } + let idx = interpreter.bytecode.read_u16() as usize; // get target types - let Some(types) = interpreter.eof().unwrap().body.types_section.get(idx) else { + let Some(types) = interpreter.bytecode.code_section_info(idx) else { panic!("Invalid EOF in execution, expecting correct intermediate in callf") }; // Check max stack height for target code section. // safe to subtract as max_stack_height is always more than inputs. if interpreter.stack.len() + (types.max_stack_size - types.inputs as u16) as usize > 1024 { - interpreter.instruction_result = InstructionResult::StackOverflow; + interpreter + .control + .set_instruction_result(InstructionResult::StackOverflow); return; } // push current idx and PC to the callf stack. // PC is incremented by 2 to point to the next instruction after callf. - interpreter - .function_stack - .push(interpreter.program_counter() + 2, idx); - - interpreter.load_eof_code(idx, 0) + if !(interpreter + .sub_routine + .push(interpreter.bytecode.pc() + 2, idx)) + { + interpreter + .control + .set_instruction_result(InstructionResult::SubRoutineStackOverflow); + return; + }; + let pc = interpreter + .bytecode + .code_section_pc(idx) + .expect("Invalid code section index"); + interpreter.bytecode.absolute_jump(pc); } -pub fn retf(interpreter: &mut Interpreter, _host: &mut H) { +pub fn retf( + interpreter: &mut Interpreter, + _host: &mut H, +) { require_eof!(interpreter); gas!(interpreter, gas::RETF_GAS); - let Some(fframe) = interpreter.function_stack.pop() else { + let Some(jump) = interpreter.sub_routine.pop() else { panic!("Expected function frame") }; - interpreter.load_eof_code(fframe.idx, fframe.pc); + interpreter.bytecode.absolute_jump(jump); } -pub fn jumpf(interpreter: &mut Interpreter, _host: &mut H) { +pub fn jumpf( + interpreter: &mut Interpreter, + _host: &mut H, +) { require_eof!(interpreter); gas!(interpreter, gas::LOW); - let idx = unsafe { read_u16(interpreter.instruction_pointer) } as usize; + let idx = interpreter.bytecode.read_u16() as usize; // get target types - let Some(types) = interpreter.eof().unwrap().body.types_section.get(idx) else { - panic!("Invalid EOF in execution, expecting correct intermediate in jumpf") - }; + let types = interpreter + .bytecode + .code_section_info(idx) + .expect("Invalid code section index"); // Check max stack height for target code section. // safe to subtract as max_stack_height is always more than inputs. if interpreter.stack.len() + (types.max_stack_size - types.inputs as u16) as usize > 1024 { - interpreter.instruction_result = InstructionResult::StackOverflow; + interpreter + .control + .set_instruction_result(InstructionResult::StackOverflow); return; } - - interpreter.function_stack.set_current_code_idx(idx); - interpreter.load_eof_code(idx, 0) + interpreter.sub_routine.set_routine_idx(idx); + let pc = interpreter + .bytecode + .code_section_pc(idx) + .expect("Invalid code section index"); + interpreter.bytecode.absolute_jump(pc); } -pub fn pc(interpreter: &mut Interpreter, _host: &mut H) { +pub fn pc( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::BASE); // - 1 because we have already advanced the instruction pointer in `Interpreter::step` - push!(interpreter, U256::from(interpreter.program_counter() - 1)); + push!(interpreter, U256::from(interpreter.bytecode.pc() - 1)); } #[inline] -fn return_inner(interpreter: &mut Interpreter, instruction_result: InstructionResult) { +fn return_inner( + interpreter: &mut Interpreter, + instruction_result: InstructionResult, +) { // zero gas cost - // gas!(interpreter, gas::ZERO); - pop!(interpreter, offset, len); + // gas!(interpreter, gas::ZERO) + popn!([offset, len], interpreter); let len = as_usize_or_fail!(interpreter, len); // important: offset must be ignored if len is zeros let mut output = Bytes::default(); if len != 0 { let offset = as_usize_or_fail!(interpreter, offset); resize_memory!(interpreter, offset, len); - - output = interpreter.shared_memory.slice(offset, len).to_vec().into() + output = interpreter.memory.slice_len(offset, len).to_vec().into() } - interpreter.instruction_result = instruction_result; - interpreter.next_action = crate::InterpreterAction::Return { - result: InterpreterResult { - output, - gas: interpreter.gas, - result: instruction_result, + + let gas = *interpreter.control.gas(); + interpreter.control.set_next_action( + InterpreterAction::Return { + result: InterpreterResult { + output, + gas, + result: instruction_result, + }, }, - }; + instruction_result, + ); } -pub fn ret(interpreter: &mut Interpreter, _host: &mut H) { +pub fn ret( + interpreter: &mut Interpreter, + _host: &mut H, +) { return_inner(interpreter, InstructionResult::Return); } /// EIP-140: REVERT instruction -pub fn revert(interpreter: &mut Interpreter, _host: &mut H) { +pub fn revert( + interpreter: &mut Interpreter, + _host: &mut H, +) { check!(interpreter, BYZANTIUM); return_inner(interpreter, InstructionResult::Revert); } /// Stop opcode. This opcode halts the execution. -pub fn stop(interpreter: &mut Interpreter, _host: &mut H) { - interpreter.instruction_result = InstructionResult::Stop; +pub fn stop( + interpreter: &mut Interpreter, + _host: &mut H, +) { + interpreter + .control + .set_instruction_result(InstructionResult::Stop); } /// Invalid opcode. This opcode halts the execution. -pub fn invalid(interpreter: &mut Interpreter, _host: &mut H) { - interpreter.instruction_result = InstructionResult::InvalidFEOpcode; +pub fn invalid( + interpreter: &mut Interpreter, + _host: &mut H, +) { + interpreter + .control + .set_instruction_result(InstructionResult::InvalidFEOpcode); } /// Unknown opcode. This opcode halts the execution. -pub fn unknown(interpreter: &mut Interpreter, _host: &mut H) { - interpreter.instruction_result = InstructionResult::OpcodeNotFound; +pub fn unknown( + interpreter: &mut Interpreter, + _host: &mut H, +) { + interpreter + .control + .set_instruction_result(InstructionResult::OpcodeNotFound); } +/* +TODO TEST #[cfg(test)] mod test { use super::*; - use crate::{table::make_instruction_table, DummyHost, FunctionReturnFrame, Gas, Interpreter}; + use crate::{table::make_instruction_table, DummyHost, Gas}; use bytecode::opcode::{CALLF, JUMPF, NOP, RETF, RJUMP, RJUMPI, RJUMPV, STOP}; use bytecode::{ eof::{Eof, TypesSection}, Bytecode, }; use primitives::bytes; - use specification::hardfork::PragueSpec; + use specification::hardfork::SpecId; use std::sync::Arc; - use wiring::DefaultEthereumWiring; + use context_interface::DefaultEthereumWiring; #[test] fn rjump() { - let table = make_instruction_table::, PragueSpec>(); + let table = make_instruction_table::>(); let mut host = DummyHost::default(); let mut interp = Interpreter::new_bytecode(Bytecode::LegacyRaw([RJUMP, 0x00, 0x02, STOP, STOP].into())); interp.is_eof = true; interp.gas = Gas::new(10000); + interp.spec_id = SpecId::PRAGUE; interp.step(&table, &mut host); assert_eq!(interp.program_counter(), 5); @@ -229,7 +302,7 @@ mod test { #[test] fn rjumpi() { - let table = make_instruction_table::, PragueSpec>(); + let table = make_instruction_table::>(); let mut host = DummyHost::default(); let mut interp = Interpreter::new_bytecode(Bytecode::LegacyRaw( [RJUMPI, 0x00, 0x03, RJUMPI, 0x00, 0x01, STOP, STOP].into(), @@ -238,6 +311,7 @@ mod test { interp.stack.push(U256::from(1)).unwrap(); interp.stack.push(U256::from(0)).unwrap(); interp.gas = Gas::new(10000); + interp.spec_id = SpecId::PRAGUE; // dont jump interp.step(&table, &mut host); @@ -249,7 +323,7 @@ mod test { #[test] fn rjumpv() { - let table = make_instruction_table::, PragueSpec>(); + let table = make_instruction_table::>(); let mut host = DummyHost::default(); let mut interp = Interpreter::new_bytecode(Bytecode::LegacyRaw( [ @@ -271,6 +345,7 @@ mod test { )); interp.is_eof = true; interp.gas = Gas::new(1000); + interp.spec_id = SpecId::PRAGUE; // more then max_index interp.stack.push(U256::from(10)).unwrap(); @@ -319,21 +394,24 @@ mod test { eof.header.code_sizes.clear(); eof.header.code_sizes.push(bytes1.len() as u16); - eof.body.code_section.push(bytes1.clone()); + eof.body.code_section.push(bytes1.len()); eof.body.types_section.push(TypesSection::new(0, 0, 11)); eof.header.code_sizes.push(bytes2.len() as u16); - eof.body.code_section.push(bytes2.clone()); + eof.body.code_section.push(bytes2.len() + bytes1.len()); eof.body.types_section.push(types); + eof.body.code = Bytes::from([bytes1, bytes2].concat()); + let mut interp = Interpreter::new_bytecode(Bytecode::Eof(Arc::new(eof))); interp.gas = Gas::new(10000); + interp.spec_id = SpecId::PRAGUE; interp } #[test] fn callf_retf_stop() { - let table = make_instruction_table::<_, PragueSpec>(); + let table = make_instruction_table::(); let mut host = DummyHost::::default(); let bytes1 = Bytes::from([CALLF, 0x00, 0x01, STOP]); @@ -346,7 +424,7 @@ mod test { assert_eq!(interp.function_stack.current_code_idx, 1); assert_eq!( interp.function_stack.return_stack[0], - FunctionReturnFrame::new(0, 3) + SubRoutineReturnFrame::new(0, 3) ); assert_eq!(interp.instruction_pointer, bytes2.as_ptr()); @@ -364,7 +442,7 @@ mod test { #[test] fn callf_stop() { - let table = make_instruction_table::<_, PragueSpec>(); + let table = make_instruction_table::(); let mut host = DummyHost::::default(); let bytes1 = Bytes::from([CALLF, 0x00, 0x01]); @@ -377,7 +455,7 @@ mod test { assert_eq!(interp.function_stack.current_code_idx, 1); assert_eq!( interp.function_stack.return_stack[0], - FunctionReturnFrame::new(0, 3) + SubRoutineReturnFrame::new(0, 3) ); assert_eq!(interp.instruction_pointer, bytes2.as_ptr()); @@ -388,7 +466,7 @@ mod test { #[test] fn callf_stack_overflow() { - let table = make_instruction_table::<_, PragueSpec>(); + let table = make_instruction_table::(); let mut host = DummyHost::::default(); let bytes1 = Bytes::from([CALLF, 0x00, 0x01]); @@ -405,7 +483,7 @@ mod test { #[test] fn jumpf_stop() { - let table = make_instruction_table::<_, PragueSpec>(); + let table = make_instruction_table::(); let mut host = DummyHost::::default(); let bytes1 = Bytes::from([JUMPF, 0x00, 0x01]); @@ -426,7 +504,7 @@ mod test { #[test] fn jumpf_stack_overflow() { - let table = make_instruction_table::<_, PragueSpec>(); + let table = make_instruction_table::(); let mut host = DummyHost::::default(); let bytes1 = Bytes::from([JUMPF, 0x00, 0x01]); @@ -441,3 +519,4 @@ mod test { assert_eq!(interp.instruction_result, InstructionResult::StackOverflow); } } +*/ diff --git a/crates/interpreter/src/instructions/data.rs b/crates/interpreter/src/instructions/data.rs index a9257205b5..38426b3fd1 100644 --- a/crates/interpreter/src/instructions/data.rs +++ b/crates/interpreter/src/instructions/data.rs @@ -1,24 +1,25 @@ use crate::{ gas::{cost_per_word, BASE, DATA_LOAD_GAS, VERYLOW}, - instructions::utility::read_u16, interpreter::Interpreter, + interpreter_types::{ + EofData, Immediates, InterpreterTypes, Jumps, LoopControl, MemoryTrait, RuntimeFlag, + StackTrait, + }, Host, }; -use primitives::U256; +use primitives::{B256, U256}; -pub fn data_load(interpreter: &mut Interpreter, _host: &mut H) { +pub fn data_load( + interpreter: &mut Interpreter, + _host: &mut H, +) { require_eof!(interpreter); gas!(interpreter, DATA_LOAD_GAS); - pop_top!(interpreter, offset); + popn_top!([], offset, interpreter); let offset_usize = as_usize_saturated!(offset); - let slice = interpreter - .contract - .bytecode - .eof() - .expect("eof") - .data_slice(offset_usize, 32); + let slice = interpreter.bytecode.data_slice(offset_usize, 32); let mut word = [0u8; 32]; word[..slice.len()].copy_from_slice(slice); @@ -26,39 +27,42 @@ pub fn data_load(interpreter: &mut Interpreter, _host: &mut H) *offset = U256::from_be_bytes(word); } -pub fn data_loadn(interpreter: &mut Interpreter, _host: &mut H) { +pub fn data_loadn( + interpreter: &mut Interpreter, + _host: &mut H, +) { require_eof!(interpreter); gas!(interpreter, VERYLOW); - let offset = unsafe { read_u16(interpreter.instruction_pointer) } as usize; + let offset = interpreter.bytecode.read_u16() as usize; - let slice = interpreter - .contract - .bytecode - .eof() - .expect("eof") - .data_slice(offset, 32); + let slice = interpreter.bytecode.data_slice(offset, 32); let mut word = [0u8; 32]; word[..slice.len()].copy_from_slice(slice); - push_b256!(interpreter, word.into()); + push!(interpreter, B256::new(word).into()); // add +2 to the instruction pointer to skip the offset - interpreter.instruction_pointer = unsafe { interpreter.instruction_pointer.offset(2) }; + interpreter.bytecode.relative_jump(2); } -pub fn data_size(interpreter: &mut Interpreter, _host: &mut H) { +pub fn data_size( + interpreter: &mut Interpreter, + _host: &mut H, +) { require_eof!(interpreter); gas!(interpreter, BASE); - let data_size = interpreter.eof().expect("eof").header.data_size; - push!(interpreter, U256::from(data_size)); + push!(interpreter, U256::from(interpreter.bytecode.data_size())); } -pub fn data_copy(interpreter: &mut Interpreter, _host: &mut H) { +pub fn data_copy( + interpreter: &mut Interpreter, + _host: &mut H, +) { require_eof!(interpreter); gas!(interpreter, VERYLOW); - pop!(interpreter, mem_offset, offset, size); + popn!([mem_offset, offset, size], interpreter); // sizes more than u64::MAX will spend all the gas in memory resize. let size = as_usize_or_fail!(interpreter, size); @@ -70,27 +74,26 @@ pub fn data_copy(interpreter: &mut Interpreter, _host: &mut H) let mem_offset = as_usize_or_fail!(interpreter, mem_offset); resize_memory!(interpreter, mem_offset, size); - gas_or_fail!(interpreter, cost_per_word(size as u64, VERYLOW)); + gas_or_fail!(interpreter, cost_per_word(size, VERYLOW)); let offset = as_usize_saturated!(offset); - let data = interpreter.contract.bytecode.eof().expect("eof").data(); + let data = interpreter.bytecode.data(); // set data from the eof to the shared memory. Padded it with zeros. - interpreter - .shared_memory - .set_data(mem_offset, offset, size, data); + interpreter.memory.set_data(mem_offset, offset, size, data); } - +/* +TODO test #[cfg(test)] mod test { use bytecode::{Bytecode, Eof}; use primitives::{b256, bytes, Bytes}; - use specification::hardfork::PragueSpec; + use specification::hardfork::SpecId; use std::sync::Arc; - use wiring::DefaultEthereumWiring; + use context_interface::DefaultEthereumWiring; use super::*; - use crate::{table::make_instruction_table, DummyHost, Gas, Interpreter}; + use crate::{table::make_instruction_table, DummyHost, Gas}; use bytecode::opcode::{DATACOPY, DATALOAD, DATALOADN, DATASIZE}; fn dummy_eof(code_bytes: Bytes) -> Bytecode { @@ -102,13 +105,14 @@ mod test { eof.header.data_size = eof.body.data_section.len() as u16; eof.header.code_sizes[0] = code_bytes.len() as u16; - eof.body.code_section[0] = code_bytes; + eof.body.code_section[0] = code_bytes.len(); + eof.body.code = code_bytes; Bytecode::Eof(Arc::new(eof)) } #[test] fn dataload_dataloadn() { - let table = make_instruction_table::, PragueSpec>(); + let table = make_instruction_table::>(); let mut host = DummyHost::default(); let eof = dummy_eof(Bytes::from([ DATALOAD, DATALOADN, 0x00, 0x00, DATALOAD, DATALOADN, 0x00, 35, DATALOAD, DATALOADN, @@ -116,6 +120,7 @@ mod test { ])); let mut interp = Interpreter::new_bytecode(eof); + interp.spec_id = SpecId::PRAGUE; interp.gas = Gas::new(10000); // DATALOAD @@ -164,12 +169,13 @@ mod test { #[test] fn data_copy() { - let table = make_instruction_table::, PragueSpec>(); + let table = make_instruction_table::>(); let mut host = DummyHost::default(); let eof = dummy_eof(Bytes::from([DATACOPY, DATACOPY, DATACOPY, DATACOPY])); let mut interp = Interpreter::new_bytecode(eof); interp.gas = Gas::new(10000); + interp.spec_id = SpecId::PRAGUE; // Data copy // size, offset mem_offset, @@ -216,3 +222,4 @@ mod test { ); } } + */ diff --git a/crates/interpreter/src/instructions/host.rs b/crates/interpreter/src/instructions/host.rs index adf9c5be95..f7128d7f80 100644 --- a/crates/interpreter/src/instructions/host.rs +++ b/crates/interpreter/src/instructions/host.rs @@ -1,89 +1,122 @@ use crate::{ gas::{self, warm_cold_cost, warm_cold_cost_with_delegation, CALL_STIPEND}, + instructions::utility::IntoAddress, interpreter::Interpreter, + interpreter_types::{ + InputsTrait, InterpreterTypes, LoopControl, MemoryTrait, RuntimeFlag, StackTrait, + }, Host, InstructionResult, }; use core::cmp::min; use primitives::{Bytes, Log, LogData, B256, U256}; -use specification::hardfork::{Spec, SpecId::*}; -use std::vec::Vec; +use specification::hardfork::SpecId::*; -pub fn balance(interpreter: &mut Interpreter, host: &mut H) { - pop_address!(interpreter, address); +pub fn balance( + interpreter: &mut Interpreter, + host: &mut H, +) { + popn_top!([], top, interpreter); + let address = top.into_address(); let Some(balance) = host.balance(address) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; + interpreter + .control + .set_instruction_result(InstructionResult::FatalExternalError); return; }; + let spec_id = interpreter.runtime_flag.spec_id(); gas!( interpreter, - if SPEC::enabled(BERLIN) { + if spec_id.is_enabled_in(BERLIN) { warm_cold_cost(balance.is_cold) - } else if SPEC::enabled(ISTANBUL) { + } else if spec_id.is_enabled_in(ISTANBUL) { // EIP-1884: Repricing for trie-size-dependent opcodes 700 - } else if SPEC::enabled(TANGERINE) { + } else if spec_id.is_enabled_in(TANGERINE) { 400 } else { 20 } ); - push!(interpreter, balance.data); + *top = balance.data; } /// EIP-1884: Repricing for trie-size-dependent opcodes -pub fn selfbalance(interpreter: &mut Interpreter, host: &mut H) { +pub fn selfbalance( + interpreter: &mut Interpreter, + host: &mut H, +) { check!(interpreter, ISTANBUL); gas!(interpreter, gas::LOW); - let Some(balance) = host.balance(interpreter.contract.target_address) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; + let Some(balance) = host.balance(interpreter.input.target_address()) else { + interpreter + .control + .set_instruction_result(InstructionResult::FatalExternalError); return; }; push!(interpreter, balance.data); } -pub fn extcodesize(interpreter: &mut Interpreter, host: &mut H) { - pop_address!(interpreter, address); +pub fn extcodesize( + interpreter: &mut Interpreter, + host: &mut H, +) { + popn_top!([], top, interpreter); + let address = top.into_address(); let Some(code) = host.code(address) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; + interpreter + .control + .set_instruction_result(InstructionResult::FatalExternalError); return; }; let (code, load) = code.into_components(); - if SPEC::enabled(BERLIN) { + let spec_id = interpreter.runtime_flag.spec_id(); + if spec_id.is_enabled_in(BERLIN) { gas!(interpreter, warm_cold_cost_with_delegation(load)); - } else if SPEC::enabled(TANGERINE) { + } else if spec_id.is_enabled_in(TANGERINE) { gas!(interpreter, 700); } else { gas!(interpreter, 20); } - push!(interpreter, U256::from(code.len())); + *top = U256::from(code.len()); } /// EIP-1052: EXTCODEHASH opcode -pub fn extcodehash(interpreter: &mut Interpreter, host: &mut H) { +pub fn extcodehash( + interpreter: &mut Interpreter, + host: &mut H, +) { check!(interpreter, CONSTANTINOPLE); - pop_address!(interpreter, address); + popn_top!([], top, interpreter); + let address = top.into_address(); let Some(code_hash) = host.code_hash(address) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; + interpreter + .control + .set_instruction_result(InstructionResult::FatalExternalError); return; }; let (code_hash, load) = code_hash.into_components(); - if SPEC::enabled(BERLIN) { + let spec_id = interpreter.runtime_flag.spec_id(); + if spec_id.is_enabled_in(BERLIN) { gas!(interpreter, warm_cold_cost_with_delegation(load)) - } else if SPEC::enabled(ISTANBUL) { + } else if spec_id.is_enabled_in(ISTANBUL) { gas!(interpreter, 700); } else { gas!(interpreter, 400); } - push_b256!(interpreter, code_hash); + *top = code_hash.into(); } -pub fn extcodecopy(interpreter: &mut Interpreter, host: &mut H) { - pop_address!(interpreter, address); - pop!(interpreter, memory_offset, code_offset, len_u256); - +pub fn extcodecopy( + interpreter: &mut Interpreter, + host: &mut H, +) { + popn!([address, memory_offset, code_offset, len_u256], interpreter); + let address = address.into_address(); let Some(code) = host.code(address) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; + interpreter + .control + .set_instruction_result(InstructionResult::FatalExternalError); return; }; @@ -91,7 +124,7 @@ pub fn extcodecopy(interpreter: &mut Interpreter, let (code, load) = code.into_components(); gas_or_fail!( interpreter, - gas::extcodecopy_cost(SPEC::SPEC_ID, len as u64, load) + gas::extcodecopy_cost(interpreter.runtime_flag.spec_id(), len, load) ); if len == 0 { return; @@ -102,83 +135,119 @@ pub fn extcodecopy(interpreter: &mut Interpreter, // Note: this can't panic because we resized memory to fit. interpreter - .shared_memory + .memory .set_data(memory_offset, code_offset, len, &code); } -pub fn blockhash(interpreter: &mut Interpreter, host: &mut H) { +pub fn blockhash( + interpreter: &mut Interpreter, + host: &mut H, +) { gas!(interpreter, gas::BLOCKHASH); - pop_top!(interpreter, number); + popn_top!([], number, interpreter); let number_u64 = as_u64_saturated!(number); let Some(hash) = host.block_hash(number_u64) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; + interpreter + .control + .set_instruction_result(InstructionResult::FatalExternalError); return; }; *number = U256::from_be_bytes(hash.0); } -pub fn sload(interpreter: &mut Interpreter, host: &mut H) { - pop_top!(interpreter, index); - let Some(value) = host.sload(interpreter.contract.target_address, *index) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; +pub fn sload( + interpreter: &mut Interpreter, + host: &mut H, +) { + popn_top!([], index, interpreter); + let Some(value) = host.sload(interpreter.input.target_address(), *index) else { + interpreter + .control + .set_instruction_result(InstructionResult::FatalExternalError); return; }; - gas!(interpreter, gas::sload_cost(SPEC::SPEC_ID, value.is_cold)); + gas!( + interpreter, + gas::sload_cost(interpreter.runtime_flag.spec_id(), value.is_cold) + ); *index = value.data; } -pub fn sstore(interpreter: &mut Interpreter, host: &mut H) { +pub fn sstore( + interpreter: &mut Interpreter, + host: &mut H, +) { require_non_staticcall!(interpreter); - pop!(interpreter, index, value); - let Some(state_load) = host.sstore(interpreter.contract.target_address, index, value) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; + popn!([index, value], interpreter); + let Some(state_load) = host.sstore(interpreter.input.target_address(), index, value) else { + interpreter + .control + .set_instruction_result(InstructionResult::FatalExternalError); return; }; // EIP-1706 Disable SSTORE with gasleft lower than call stipend - if SPEC::SPEC_ID.is_enabled_in(ISTANBUL) && interpreter.gas.remaining() <= CALL_STIPEND { - interpreter.instruction_result = InstructionResult::ReentrancySentryOOG; + if interpreter.runtime_flag.spec_id().is_enabled_in(ISTANBUL) + && interpreter.control.gas().remaining() <= CALL_STIPEND + { + interpreter + .control + .set_instruction_result(InstructionResult::ReentrancySentryOOG); return; } gas!( interpreter, - gas::sstore_cost(SPEC::SPEC_ID, &state_load.data, state_load.is_cold) - ); - refund!( - interpreter, - gas::sstore_refund(SPEC::SPEC_ID, &state_load.data) + gas::sstore_cost( + interpreter.runtime_flag.spec_id(), + &state_load.data, + state_load.is_cold + ) ); + + interpreter.control.gas().record_refund(gas::sstore_refund( + interpreter.runtime_flag.spec_id(), + &state_load.data, + )); } /// EIP-1153: Transient storage opcodes /// Store value to transient storage -pub fn tstore(interpreter: &mut Interpreter, host: &mut H) { +pub fn tstore( + interpreter: &mut Interpreter, + host: &mut H, +) { check!(interpreter, CANCUN); require_non_staticcall!(interpreter); gas!(interpreter, gas::WARM_STORAGE_READ_COST); - pop!(interpreter, index, value); + popn!([index, value], interpreter); - host.tstore(interpreter.contract.target_address, index, value); + host.tstore(interpreter.input.target_address(), index, value); } /// EIP-1153: Transient storage opcodes /// Load value from transient storage -pub fn tload(interpreter: &mut Interpreter, host: &mut H) { +pub fn tload( + interpreter: &mut Interpreter, + host: &mut H, +) { check!(interpreter, CANCUN); gas!(interpreter, gas::WARM_STORAGE_READ_COST); - pop_top!(interpreter, index); + popn_top!([], index, interpreter); - *index = host.tload(interpreter.contract.target_address, *index); + *index = host.tload(interpreter.input.target_address(), *index); } -pub fn log(interpreter: &mut Interpreter, host: &mut H) { +pub fn log( + interpreter: &mut Interpreter, + host: &mut H, +) { require_non_staticcall!(interpreter); - pop!(interpreter, offset, len); + popn!([offset, len], interpreter); let len = as_usize_or_fail!(interpreter, len); gas_or_fail!(interpreter, gas::log_cost(N as u8, len as u64)); let data = if len == 0 { @@ -186,42 +255,54 @@ pub fn log(interpreter: &mut Interpreter, host } else { let offset = as_usize_or_fail!(interpreter, offset); resize_memory!(interpreter, offset, len); - Bytes::copy_from_slice(interpreter.shared_memory.slice(offset, len)) + Bytes::copy_from_slice(interpreter.memory.slice_len(offset, len).as_ref()) }; - if interpreter.stack.len() < N { - interpreter.instruction_result = InstructionResult::StackUnderflow; + interpreter + .control + .set_instruction_result(InstructionResult::StackUnderflow); return; } - - let mut topics = Vec::with_capacity(N); - for _ in 0..N { - // SAFETY: stack bounds already checked few lines above - topics.push(B256::from(unsafe { interpreter.stack.pop_unsafe() })); - } + let Some(topics) = interpreter.stack.popn::() else { + interpreter + .control + .set_instruction_result(InstructionResult::StackUnderflow); + return; + }; let log = Log { - address: interpreter.contract.target_address, - data: LogData::new(topics, data).expect("LogData should have <=4 topics"), + address: interpreter.input.target_address(), + data: LogData::new(topics.into_iter().map(B256::from).collect(), data) + .expect("LogData should have <=4 topics"), }; host.log(log); } -pub fn selfdestruct(interpreter: &mut Interpreter, host: &mut H) { +pub fn selfdestruct( + interpreter: &mut Interpreter, + host: &mut H, +) { require_non_staticcall!(interpreter); - pop_address!(interpreter, target); - - let Some(res) = host.selfdestruct(interpreter.contract.target_address, target) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; + popn!([target], interpreter); + let target = target.into_address(); + let Some(res) = host.selfdestruct(interpreter.input.target_address(), target) else { + interpreter + .control + .set_instruction_result(InstructionResult::FatalExternalError); return; }; // EIP-3529: Reduction in refunds - if !SPEC::enabled(LONDON) && !res.previously_destroyed { - refund!(interpreter, gas::SELFDESTRUCT) + if !interpreter.runtime_flag.spec_id().is_enabled_in(LONDON) && !res.previously_destroyed { + interpreter.control.gas().record_refund(gas::SELFDESTRUCT) } - gas!(interpreter, gas::selfdestruct_cost(SPEC::SPEC_ID, res)); + gas!( + interpreter, + gas::selfdestruct_cost(interpreter.runtime_flag.spec_id(), res) + ); - interpreter.instruction_result = InstructionResult::SelfDestruct; + interpreter + .control + .set_instruction_result(InstructionResult::SelfDestruct); } diff --git a/crates/interpreter/src/instructions/macros.rs b/crates/interpreter/src/instructions/macros.rs index 75a42a2f6a..0f78221037 100644 --- a/crates/interpreter/src/instructions/macros.rs +++ b/crates/interpreter/src/instructions/macros.rs @@ -1,33 +1,47 @@ //! Utility macros to help implementing opcode instruction functions. +/// `const` Option `?`. +#[macro_export] +macro_rules! tri { + ($e:expr) => { + match $e { + Some(v) => v, + None => return None, + } + }; +} + /// Fails the instruction if the current call is static. #[macro_export] macro_rules! require_non_staticcall { - ($interp:expr) => { - if $interp.is_static { - $interp.instruction_result = $crate::InstructionResult::StateChangeDuringStaticCall; + ($interpreter:expr) => { + if $interpreter.runtime_flag.is_static() { + $interpreter + .control + .set_instruction_result($crate::InstructionResult::StateChangeDuringStaticCall); return; } }; } -/// Error if the current call is executing EOF. #[macro_export] -macro_rules! require_eof { - ($interp:expr) => { - if !$interp.is_eof { - $interp.instruction_result = $crate::InstructionResult::EOFOpcodeDisabledInLegacy; +macro_rules! otry { + ($expression: expr) => {{ + let Some(value) = $expression else { return; - } - }; + }; + value + }}; } -/// Error if not init eof call. +/// Error if the current call is executing EOF. #[macro_export] -macro_rules! require_init_eof { - ($interp:expr) => { - if !$interp.is_eof_init { - $interp.instruction_result = $crate::InstructionResult::ReturnContractInNotInitEOF; +macro_rules! require_eof { + ($interpreter:expr) => { + if !$interpreter.runtime_flag.is_eof() { + $interpreter + .control + .set_instruction_result($crate::InstructionResult::EOFOpcodeDisabledInLegacy); return; } }; @@ -36,12 +50,15 @@ macro_rules! require_init_eof { /// Check if the `SPEC` is enabled, and fail the instruction if it is not. #[macro_export] macro_rules! check { - ($interp:expr, $min:ident) => { - if const { - !::SPEC_ID - .is_enabled_in(specification::hardfork::SpecId::$min) - } { - $interp.instruction_result = $crate::InstructionResult::NotActivated; + ($interpreter:expr, $min:ident) => { + if !$interpreter + .runtime_flag + .spec_id() + .is_enabled_in(specification::hardfork::SpecId::$min) + { + $interpreter + .control + .set_instruction_result($crate::InstructionResult::NotActivated); return; } }; @@ -50,232 +67,93 @@ macro_rules! check { /// Records a `gas` cost and fails the instruction if it would exceed the available gas. #[macro_export] macro_rules! gas { - ($interp:expr, $gas:expr) => { - $crate::gas!($interp, $gas, ()) - }; - ($interp:expr, $gas:expr, $ret:expr) => { - if !$interp.gas.record_cost($gas) { - $interp.instruction_result = $crate::InstructionResult::OutOfGas; + ($interpreter:expr, $gas:expr) => { + $crate::gas!($interpreter, $gas, ()) + }; + ($interpreter:expr, $gas:expr, $ret:expr) => { + if !$interpreter.control.gas().record_cost($gas) { + $interpreter + .control + .set_instruction_result($crate::InstructionResult::OutOfGas); return $ret; } }; } -/// Records a `gas` refund. -#[macro_export] -macro_rules! refund { - ($interp:expr, $gas:expr) => { - $interp.gas.record_refund($gas) - }; -} - /// Same as [`gas!`], but with `gas` as an option. #[macro_export] macro_rules! gas_or_fail { - ($interp:expr, $gas:expr) => { - $crate::gas_or_fail!($interp, $gas, ()) + ($interpreter:expr, $gas:expr) => { + $crate::gas_or_fail!($interpreter, $gas, ()) }; - ($interp:expr, $gas:expr, $ret:expr) => { + ($interpreter:expr, $gas:expr, $ret:expr) => { match $gas { - Some(gas_used) => $crate::gas!($interp, gas_used, $ret), + Some(gas_used) => $crate::gas!($interpreter, gas_used, $ret), None => { - $interp.instruction_result = $crate::InstructionResult::OutOfGas; + $interpreter + .control + .set_instruction_result($crate::InstructionResult::OutOfGas); return $ret; } } }; } -/// Resizes the interpreter memory if necessary. Fails the instruction if the memory or gas limit +/// Resizes the interpreterreter memory if necessary. Fails the instruction if the memory or gas limit /// is exceeded. #[macro_export] macro_rules! resize_memory { - ($interp:expr, $offset:expr, $len:expr) => { - $crate::resize_memory!($interp, $offset, $len, ()) - }; - ($interp:expr, $offset:expr, $len:expr, $ret:expr) => { - let new_size = $offset.saturating_add($len); - if new_size > $interp.shared_memory.len() { - #[cfg(feature = "memory_limit")] - if $interp.shared_memory.limit_reached(new_size) { - $interp.instruction_result = $crate::InstructionResult::MemoryLimitOOG; - return $ret; + ($interpreter:expr, $offset:expr, $len:expr) => { + $crate::resize_memory!($interpreter, $offset, $len, ()) + }; + ($interpreter:expr, $offset:expr, $len:expr, $ret:expr) => { + let words_num = $crate::interpreter::num_words($offset.saturating_add($len)); + match $interpreter + .control + .gas() + .record_memory_expansion(words_num) + { + $crate::gas::MemoryExtensionResult::Extended => { + $interpreter.memory.resize(words_num * 32); } - - // Note: we can't use `Interpreter` directly here because of potential double-borrows. - if !$crate::interpreter::resize_memory( - &mut $interp.shared_memory, - &mut $interp.gas, - new_size, - ) { - $interp.instruction_result = $crate::InstructionResult::MemoryOOG; + $crate::gas::MemoryExtensionResult::OutOfGas => { + $interpreter + .control + .set_instruction_result($crate::InstructionResult::OutOfGas); return $ret; } - } + $crate::gas::MemoryExtensionResult::Same => (), // no action + }; }; } -/// Pops `Address` values from the stack. Fails the instruction if the stack is too small. -#[macro_export] -macro_rules! pop_address { - ($interp:expr, $x1:ident) => { - $crate::pop_address_ret!($interp, $x1, ()) - }; - ($interp:expr, $x1:ident, $x2:ident) => { - $crate::pop_address_ret!($interp, $x1, $x2, ()) - }; -} - -/// Pop `Address` values from the stack, returns `ret` on stack underflow. -#[macro_export] -macro_rules! pop_address_ret { - ($interp:expr, $x1:ident, $ret:expr) => { - if $interp.stack.len() < 1 { - $interp.instruction_result = $crate::InstructionResult::StackUnderflow; - return $ret; - } - // SAFETY: Length is checked above. - let $x1 = ::primitives::Address::from_word(::primitives::B256::from(unsafe { - $interp.stack.pop_unsafe() - })); - }; - ($interp:expr, $x1:ident, $x2:ident, $ret:expr) => { - if $interp.stack.len() < 2 { - $interp.instruction_result = $crate::InstructionResult::StackUnderflow; - return $ret; - } - // SAFETY: Length is checked above. - let $x1 = ::primitives::Address::from_word(::primitives::B256::from(unsafe { - $interp.stack.pop_unsafe() - })); - let $x2 = ::primitives::Address::from_word(::primitives::B256::from(unsafe { - $interp.stack.pop_unsafe() - })); +macro_rules! popn { + ([ $($x:ident),* ],$interpreterreter:expr $(,$ret:expr)? ) => { + let Some([$( $x ),*]) = $interpreterreter.stack.popn() else { + $interpreterreter.control.set_instruction_result($crate::InstructionResult::StackUnderflow); + return $($ret)?; + }; }; } -/// Pops `U256` values from the stack. Fails the instruction if the stack is too small. -#[macro_export] -macro_rules! pop { - ($interp:expr, $x1:ident) => { - $crate::pop_ret!($interp, $x1, ()) - }; - ($interp:expr, $x1:ident, $x2:ident) => { - $crate::pop_ret!($interp, $x1, $x2, ()) - }; - ($interp:expr, $x1:ident, $x2:ident, $x3:ident) => { - $crate::pop_ret!($interp, $x1, $x2, $x3, ()) - }; - ($interp:expr, $x1:ident, $x2:ident, $x3:ident, $x4:ident) => { - $crate::pop_ret!($interp, $x1, $x2, $x3, $x4, ()) - }; - ($interp:expr, $x1:ident, $x2:ident, $x3:ident, $x4:ident, $x5:ident) => { - $crate::pop_ret!($interp, $x1, $x2, $x3, $x4, $x5, ()) +macro_rules! popn_top { + ([ $($x:ident),* ], $top:ident, $interpreterreter:expr $(,$ret:expr)? ) => { + let Some(([$( $x ),*], $top)) = $interpreterreter.stack.popn_top() else { + $interpreterreter.control.set_instruction_result($crate::InstructionResult::StackUnderflow); + return $($ret)?; + }; }; } -/// Pops `U256` values from the stack, and returns `ret`. -/// Fails the instruction if the stack is too small. -#[macro_export] -macro_rules! pop_ret { - ($interp:expr, $x1:ident, $ret:expr) => { - if $interp.stack.len() < 1 { - $interp.instruction_result = $crate::InstructionResult::StackUnderflow; - return $ret; - } - // SAFETY: Length is checked above. - let $x1 = unsafe { $interp.stack.pop_unsafe() }; - }; - ($interp:expr, $x1:ident, $x2:ident, $ret:expr) => { - if $interp.stack.len() < 2 { - $interp.instruction_result = $crate::InstructionResult::StackUnderflow; - return $ret; - } - // SAFETY: Length is checked above. - let ($x1, $x2) = unsafe { $interp.stack.pop2_unsafe() }; - }; - ($interp:expr, $x1:ident, $x2:ident, $x3:ident, $ret:expr) => { - if $interp.stack.len() < 3 { - $interp.instruction_result = $crate::InstructionResult::StackUnderflow; - return $ret; - } - // SAFETY: Length is checked above. - let ($x1, $x2, $x3) = unsafe { $interp.stack.pop3_unsafe() }; - }; - ($interp:expr, $x1:ident, $x2:ident, $x3:ident, $x4:ident, $ret:expr) => { - if $interp.stack.len() < 4 { - $interp.instruction_result = $crate::InstructionResult::StackUnderflow; - return $ret; - } - // SAFETY: Length is checked above. - let ($x1, $x2, $x3, $x4) = unsafe { $interp.stack.pop4_unsafe() }; - }; - ($interp:expr, $x1:ident, $x2:ident, $x3:ident, $x4:ident, $x5:ident, $ret:expr) => { - if $interp.stack.len() < 5 { - $interp.instruction_result = $crate::InstructionResult::StackUnderflow; - return $ret; - } - // SAFETY: Length is checked above. - let ($x1, $x2, $x3, $x4, $x5) = unsafe { $interp.stack.pop5_unsafe() }; - }; -} - -/// Pops `U256` values from the stack, and returns a reference to the top of the stack. -/// Fails the instruction if the stack is too small. -#[macro_export] -macro_rules! pop_top { - ($interp:expr, $x1:ident) => { - if $interp.stack.len() < 1 { - $interp.instruction_result = $crate::InstructionResult::StackUnderflow; - return; - } - // SAFETY: Length is checked above. - let $x1 = unsafe { $interp.stack.top_unsafe() }; - }; - ($interp:expr, $x1:ident, $x2:ident) => { - if $interp.stack.len() < 2 { - $interp.instruction_result = $crate::InstructionResult::StackUnderflow; - return; - } - // SAFETY: Length is checked above. - let ($x1, $x2) = unsafe { $interp.stack.pop_top_unsafe() }; - }; - ($interp:expr, $x1:ident, $x2:ident, $x3:ident) => { - if $interp.stack.len() < 3 { - $interp.instruction_result = $crate::InstructionResult::StackUnderflow; - return; - } - // SAFETY: Length is checked above. - let ($x1, $x2, $x3) = unsafe { $interp.stack.pop2_top_unsafe() }; - }; -} - -/// Pushes `B256` values onto the stack. Fails the instruction if the stack is full. -#[macro_export] -macro_rules! push_b256 { - ($interp:expr, $($x:expr),* $(,)?) => ($( - match $interp.stack.push_b256($x) { - Ok(()) => {}, - Err(e) => { - $interp.instruction_result = e; - return; - }, - } - )*) -} - /// Pushes a `B256` value onto the stack. Fails the instruction if the stack is full. #[macro_export] macro_rules! push { - ($interp:expr, $($x:expr),* $(,)?) => ($( - match $interp.stack.push($x) { - Ok(()) => {}, - Err(e) => { - $interp.instruction_result = e; - return; - } + ($interpreter:expr, $x:expr $(,$ret:item)?) => ( + if !($interpreter.stack.push($x)) { + $interpreter.control.set_instruction_result($crate::InstructionResult::StackOverflow); + return $($ret)?; } - )*) + ) } /// Converts a `U256` value to a `u64`, saturating to `MAX` if the value is too large. @@ -315,11 +193,11 @@ macro_rules! as_isize_saturated { /// Converts a `U256` value to a `usize`, failing the instruction if the value is too large. #[macro_export] macro_rules! as_usize_or_fail { - ($interp:expr, $v:expr) => { - $crate::as_usize_or_fail_ret!($interp, $v, ()) + ($interpreter:expr, $v:expr) => { + $crate::as_usize_or_fail_ret!($interpreter, $v, ()) }; - ($interp:expr, $v:expr, $reason:expr) => { - $crate::as_usize_or_fail_ret!($interp, $v, $reason, ()) + ($interpreter:expr, $v:expr, $reason:expr) => { + $crate::as_usize_or_fail_ret!($interpreter, $v, $reason, ()) }; } @@ -327,20 +205,20 @@ macro_rules! as_usize_or_fail { /// failing the instruction if the value is too large. #[macro_export] macro_rules! as_usize_or_fail_ret { - ($interp:expr, $v:expr, $ret:expr) => { + ($interpreter:expr, $v:expr, $ret:expr) => { $crate::as_usize_or_fail_ret!( - $interp, + $interpreter, $v, $crate::InstructionResult::InvalidOperandOOG, $ret ) }; - ($interp:expr, $v:expr, $reason:expr, $ret:expr) => { + ($interpreter:expr, $v:expr, $reason:expr, $ret:expr) => { match $v.as_limbs() { x => { if (x[0] > usize::MAX as u64) | (x[1] != 0) | (x[2] != 0) | (x[3] != 0) { - $interp.instruction_result = $reason; + $interpreter.control.set_instruction_result($reason); return $ret; } x[0] as usize diff --git a/crates/interpreter/src/instructions/memory.rs b/crates/interpreter/src/instructions/memory.rs index 0ba63903d5..ccad3632e6 100644 --- a/crates/interpreter/src/instructions/memory.rs +++ b/crates/interpreter/src/instructions/memory.rs @@ -1,46 +1,65 @@ -use crate::{gas, Host, Interpreter}; +use crate::{ + gas, + interpreter::Interpreter, + interpreter_types::{InterpreterTypes, LoopControl, MemoryTrait, RuntimeFlag, StackTrait}, + Host, +}; use core::cmp::max; use primitives::U256; -use specification::hardfork::Spec; -pub fn mload(interpreter: &mut Interpreter, _host: &mut H) { +pub fn mload( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, top); + popn_top!([], top, interpreter); let offset = as_usize_or_fail!(interpreter, top); resize_memory!(interpreter, offset, 32); - *top = interpreter.shared_memory.get_u256(offset); + *top = U256::try_from_be_slice(interpreter.memory.slice_len(offset, 32).as_ref()).unwrap() } -pub fn mstore(interpreter: &mut Interpreter, _host: &mut H) { +pub fn mstore( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::VERYLOW); - pop!(interpreter, offset, value); + popn!([offset, value], interpreter); let offset = as_usize_or_fail!(interpreter, offset); resize_memory!(interpreter, offset, 32); - interpreter.shared_memory.set_u256(offset, value); + interpreter.memory.set(offset, &value.to_be_bytes::<32>()); } -pub fn mstore8(interpreter: &mut Interpreter, _host: &mut H) { +pub fn mstore8( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::VERYLOW); - pop!(interpreter, offset, value); + popn!([offset, value], interpreter); let offset = as_usize_or_fail!(interpreter, offset); resize_memory!(interpreter, offset, 1); - interpreter.shared_memory.set_byte(offset, value.byte(0)) + interpreter.memory.set(offset, &[value.byte(0)]); } -pub fn msize(interpreter: &mut Interpreter, _host: &mut H) { +pub fn msize( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::BASE); - push!(interpreter, U256::from(interpreter.shared_memory.len())); + push!(interpreter, U256::from(interpreter.memory.size())); } // EIP-5656: MCOPY - Memory copying instruction -pub fn mcopy(interpreter: &mut Interpreter, _host: &mut H) { +pub fn mcopy( + interpreter: &mut Interpreter, + _host: &mut H, +) { check!(interpreter, CANCUN); - pop!(interpreter, dst, src, len); + popn!([dst, src, len], interpreter); // into usize or fail let len = as_usize_or_fail!(interpreter, len); // deduce gas - gas_or_fail!(interpreter, gas::copy_cost_verylow(len as u64)); + gas_or_fail!(interpreter, gas::copy_cost_verylow(len)); if len == 0 { return; } @@ -50,5 +69,5 @@ pub fn mcopy(interpreter: &mut Interpreter, _host: // resize memory resize_memory!(interpreter, max(dst, src), len); // copy memory in place - interpreter.shared_memory.copy(dst, src, len); + interpreter.memory.copy(dst, src, len); } diff --git a/crates/interpreter/src/instructions/stack.rs b/crates/interpreter/src/instructions/stack.rs index 4d2bf5881a..6ac0e4977c 100644 --- a/crates/interpreter/src/instructions/stack.rs +++ b/crates/interpreter/src/instructions/stack.rs @@ -1,87 +1,125 @@ -use crate::{gas, Host, Interpreter}; +use crate::{ + gas, + instructions::utility::cast_slice_to_u256, + interpreter::Interpreter, + interpreter_types::{ + Immediates, InterpreterTypes, Jumps, LoopControl, RuntimeFlag, StackTrait, + }, + Host, +}; use primitives::U256; -use specification::hardfork::Spec; -pub fn pop(interpreter: &mut Interpreter, _host: &mut H) { +pub fn pop( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::BASE); - if let Err(result) = interpreter.stack.pop() { - interpreter.instruction_result = result; - } + // can ignore return. as relative N jump is safe operation. + popn!([_i], interpreter); } /// EIP-3855: PUSH0 instruction /// /// Introduce a new instruction which pushes the constant value 0 onto the stack. -pub fn push0(interpreter: &mut Interpreter, _host: &mut H) { +pub fn push0( + interpreter: &mut Interpreter, + _host: &mut H, +) { check!(interpreter, SHANGHAI); gas!(interpreter, gas::BASE); - if let Err(result) = interpreter.stack.push(U256::ZERO) { - interpreter.instruction_result = result; - } + push!(interpreter, U256::ZERO); } -pub fn push(interpreter: &mut Interpreter, _host: &mut H) { +pub fn push( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::VERYLOW); - // SAFETY: In analysis we append trailing bytes to the bytecode so that this is safe to do - // without bounds checking. - let ip = interpreter.instruction_pointer; - if let Err(result) = interpreter - .stack - .push_slice(unsafe { core::slice::from_raw_parts(ip, N) }) - { - interpreter.instruction_result = result; - return; - } - interpreter.instruction_pointer = unsafe { ip.add(N) }; + // TODO check performance degradation. + push!(interpreter, U256::ZERO); + popn_top!([], top, interpreter); + + let imm = interpreter.bytecode.read_slice(N); + cast_slice_to_u256(imm, top); + + // can ignore return. as relative N jump is safe operation + interpreter.bytecode.relative_jump(N as isize); } -pub fn dup(interpreter: &mut Interpreter, _host: &mut H) { +pub fn dup( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::VERYLOW); - if let Err(result) = interpreter.stack.dup(N) { - interpreter.instruction_result = result; + if !interpreter.stack.dup(N) { + interpreter + .control + .set_instruction_result(crate::InstructionResult::StackOverflow); } } -pub fn swap(interpreter: &mut Interpreter, _host: &mut H) { +pub fn swap( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::VERYLOW); - if let Err(result) = interpreter.stack.swap(N) { - interpreter.instruction_result = result; + assert!(N != 0); + if !interpreter.stack.exchange(0, N) { + interpreter + .control + .set_instruction_result(crate::InstructionResult::StackOverflow); } } -pub fn dupn(interpreter: &mut Interpreter, _host: &mut H) { +pub fn dupn( + interpreter: &mut Interpreter, + _host: &mut H, +) { require_eof!(interpreter); gas!(interpreter, gas::VERYLOW); - let imm = unsafe { *interpreter.instruction_pointer }; - if let Err(result) = interpreter.stack.dup(imm as usize + 1) { - interpreter.instruction_result = result; + let imm = interpreter.bytecode.read_u8(); + if !interpreter.stack.dup(imm as usize + 1) { + interpreter + .control + .set_instruction_result(crate::InstructionResult::StackOverflow); } - interpreter.instruction_pointer = unsafe { interpreter.instruction_pointer.offset(1) }; + interpreter.bytecode.relative_jump(1); } -pub fn swapn(interpreter: &mut Interpreter, _host: &mut H) { +pub fn swapn( + interpreter: &mut Interpreter, + _host: &mut H, +) { require_eof!(interpreter); gas!(interpreter, gas::VERYLOW); - let imm = unsafe { *interpreter.instruction_pointer }; - if let Err(result) = interpreter.stack.swap(imm as usize + 1) { - interpreter.instruction_result = result; + let imm = interpreter.bytecode.read_u8(); + if !interpreter.stack.exchange(0, imm as usize + 1) { + interpreter + .control + .set_instruction_result(crate::InstructionResult::StackOverflow); } - interpreter.instruction_pointer = unsafe { interpreter.instruction_pointer.offset(1) }; + interpreter.bytecode.relative_jump(1); } -pub fn exchange(interpreter: &mut Interpreter, _host: &mut H) { +pub fn exchange( + interpreter: &mut Interpreter, + _host: &mut H, +) { require_eof!(interpreter); gas!(interpreter, gas::VERYLOW); - let imm = unsafe { *interpreter.instruction_pointer }; + let imm = interpreter.bytecode.read_u8(); let n = (imm >> 4) + 1; let m = (imm & 0x0F) + 1; - if let Err(result) = interpreter.stack.exchange(n as usize, m as usize) { - interpreter.instruction_result = result; + if !interpreter.stack.exchange(n as usize, m as usize) { + interpreter + .control + .set_instruction_result(crate::InstructionResult::StackOverflow); } - - interpreter.instruction_pointer = unsafe { interpreter.instruction_pointer.offset(1) }; + interpreter.bytecode.relative_jump(1); } +/* +TODO TESTS #[cfg(test)] mod test { @@ -89,17 +127,18 @@ mod test { use crate::{table::make_instruction_table, DummyHost, Gas, InstructionResult}; use bytecode::opcode::{DUPN, EXCHANGE, SWAPN}; use bytecode::Bytecode; - use specification::hardfork::PragueSpec; - use wiring::DefaultEthereumWiring; + use specification::hardfork::SpecId; + use context_interface::DefaultEthereumWiring; #[test] fn dupn() { - let table = make_instruction_table::, PragueSpec>(); + let table = make_instruction_table::>(); let mut host = DummyHost::default(); let mut interp = Interpreter::new_bytecode(Bytecode::LegacyRaw( [DUPN, 0x00, DUPN, 0x01, DUPN, 0x02].into(), )); interp.is_eof = true; + interp.spec_id = SpecId::PRAGUE; interp.gas = Gas::new(10000); interp.stack.push(U256::from(10)).unwrap(); @@ -114,12 +153,13 @@ mod test { #[test] fn swapn() { - let table = make_instruction_table::, PragueSpec>(); + let table = make_instruction_table::>(); let mut host = DummyHost::default(); let mut interp = Interpreter::new_bytecode(Bytecode::LegacyRaw([SWAPN, 0x00, SWAPN, 0x01].into())); interp.is_eof = true; interp.gas = Gas::new(10000); + interp.spec_id = SpecId::PRAGUE; interp.stack.push(U256::from(10)).unwrap(); interp.stack.push(U256::from(20)).unwrap(); @@ -134,12 +174,13 @@ mod test { #[test] fn exchange() { - let table = make_instruction_table::, PragueSpec>(); + let table = make_instruction_table::>(); let mut host = DummyHost::default(); let mut interp = Interpreter::new_bytecode(Bytecode::LegacyRaw([EXCHANGE, 0x00, EXCHANGE, 0x11].into())); interp.is_eof = true; interp.gas = Gas::new(10000); + interp.spec_id = SpecId::PRAGUE; interp.stack.push(U256::from(1)).unwrap(); interp.stack.push(U256::from(5)).unwrap(); @@ -155,3 +196,4 @@ mod test { assert_eq!(interp.stack.peek(4), Ok(U256::from(15))); } } +*/ diff --git a/crates/interpreter/src/instructions/system.rs b/crates/interpreter/src/instructions/system.rs index a07a4f083d..f35711e3a8 100644 --- a/crates/interpreter/src/instructions/system.rs +++ b/crates/interpreter/src/instructions/system.rs @@ -1,93 +1,126 @@ -use crate::{gas, Host, InstructionResult, Interpreter}; +use crate::{ + gas, + interpreter::Interpreter, + interpreter_types::{ + InputsTrait, InterpreterTypes, LegacyBytecode, LoopControl, MemoryTrait, ReturnData, + RuntimeFlag, StackTrait, + }, + Host, InstructionResult, +}; use core::ptr; use primitives::{B256, KECCAK_EMPTY, U256}; -use specification::hardfork::Spec; -pub fn keccak256(interpreter: &mut Interpreter, _host: &mut H) { - pop_top!(interpreter, offset, len_ptr); - let len = as_usize_or_fail!(interpreter, len_ptr); - gas_or_fail!(interpreter, gas::keccak256_cost(len as u64)); +pub fn keccak256( + interpreter: &mut Interpreter, + _host: &mut H, +) { + popn_top!([offset], top, interpreter); + let len = as_usize_or_fail!(interpreter, top); + gas_or_fail!(interpreter, gas::keccak256_cost(len)); let hash = if len == 0 { KECCAK_EMPTY } else { let from = as_usize_or_fail!(interpreter, offset); resize_memory!(interpreter, from, len); - primitives::keccak256(interpreter.shared_memory.slice(from, len)) + primitives::keccak256(interpreter.memory.slice_len(from, len).as_ref()) }; - *len_ptr = hash.into(); + *top = hash.into(); } -pub fn address(interpreter: &mut Interpreter, _host: &mut H) { +pub fn address( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::BASE); - push_b256!(interpreter, interpreter.contract.target_address.into_word()); + push!( + interpreter, + interpreter.input.target_address().into_word().into() + ); } -pub fn caller(interpreter: &mut Interpreter, _host: &mut H) { +pub fn caller( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::BASE); - push_b256!(interpreter, interpreter.contract.caller.into_word()); + push!( + interpreter, + interpreter.input.caller_address().into_word().into() + ); } -pub fn codesize(interpreter: &mut Interpreter, _host: &mut H) { +pub fn codesize( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::BASE); - // Inform the optimizer that the bytecode cannot be EOF to remove a bounds check. - assume!(!interpreter.contract.bytecode.is_eof()); - push!(interpreter, U256::from(interpreter.contract.bytecode.len())); + push!(interpreter, U256::from(interpreter.bytecode.bytecode_len())); } -pub fn codecopy(interpreter: &mut Interpreter, _host: &mut H) { - pop!(interpreter, memory_offset, code_offset, len); +pub fn codecopy( + interpreter: &mut Interpreter, + _host: &mut H, +) { + popn!([memory_offset, code_offset, len], interpreter); let len = as_usize_or_fail!(interpreter, len); let Some(memory_offset) = memory_resize(interpreter, memory_offset, len) else { return; }; let code_offset = as_usize_saturated!(code_offset); - // Inform the optimizer that the bytecode cannot be EOF to remove a bounds check. - assume!(!interpreter.contract.bytecode.is_eof()); // Note: this can't panic because we resized memory to fit. - interpreter.shared_memory.set_data( + interpreter.memory.set_data( memory_offset, code_offset, len, - interpreter.contract.bytecode.original_byte_slice(), + interpreter.bytecode.bytecode_slice(), ); } -pub fn calldataload(interpreter: &mut Interpreter, _host: &mut H) { +pub fn calldataload( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, offset_ptr); + //pop_top!(interpreter, offset_ptr); + popn_top!([], offset_ptr, interpreter); let mut word = B256::ZERO; let offset = as_usize_saturated!(offset_ptr); - if offset < interpreter.contract.input.len() { - let count = 32.min(interpreter.contract.input.len() - offset); + let input = interpreter.input.input(); + let input_len = input.len(); + if offset < input_len { + let count = 32.min(input_len - offset); // SAFETY: count is bounded by the calldata length. // This is `word[..count].copy_from_slice(input[offset..offset + count])`, written using // raw pointers as apparently the compiler cannot optimize the slice version, and using // `get_unchecked` twice is uglier. - debug_assert!(count <= 32 && offset + count <= interpreter.contract.input.len()); - unsafe { - ptr::copy_nonoverlapping( - interpreter.contract.input.as_ptr().add(offset), - word.as_mut_ptr(), - count, - ) - }; + debug_assert!(count <= 32 && offset + count <= input_len); + unsafe { ptr::copy_nonoverlapping(input.as_ptr().add(offset), word.as_mut_ptr(), count) }; } *offset_ptr = word.into(); } -pub fn calldatasize(interpreter: &mut Interpreter, _host: &mut H) { +pub fn calldatasize( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::BASE); - push!(interpreter, U256::from(interpreter.contract.input.len())); + push!(interpreter, U256::from(interpreter.input.input().len())); } -pub fn callvalue(interpreter: &mut Interpreter, _host: &mut H) { +pub fn callvalue( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::BASE); - push!(interpreter, interpreter.contract.call_value); + push!(interpreter, interpreter.input.call_value()); } -pub fn calldatacopy(interpreter: &mut Interpreter, _host: &mut H) { - pop!(interpreter, memory_offset, data_offset, len); +pub fn calldatacopy( + interpreter: &mut Interpreter, + _host: &mut H, +) { + popn!([memory_offset, data_offset, len], interpreter); let len = as_usize_or_fail!(interpreter, len); let Some(memory_offset) = memory_resize(interpreter, memory_offset, len) else { return; @@ -95,28 +128,31 @@ pub fn calldatacopy(interpreter: &mut Interpreter, _host: &mut let data_offset = as_usize_saturated!(data_offset); // Note: this can't panic because we resized memory to fit. - interpreter.shared_memory.set_data( - memory_offset, - data_offset, - len, - &interpreter.contract.input, - ); + interpreter + .memory + .set_data(memory_offset, data_offset, len, interpreter.input.input()); } /// EIP-211: New opcodes: RETURNDATASIZE and RETURNDATACOPY -pub fn returndatasize(interpreter: &mut Interpreter, _host: &mut H) { +pub fn returndatasize( + interpreter: &mut Interpreter, + _host: &mut H, +) { check!(interpreter, BYZANTIUM); gas!(interpreter, gas::BASE); push!( interpreter, - U256::from(interpreter.return_data_buffer.len()) + U256::from(interpreter.return_data.buffer().len()) ); } /// EIP-211: New opcodes: RETURNDATASIZE and RETURNDATACOPY -pub fn returndatacopy(interpreter: &mut Interpreter, _host: &mut H) { +pub fn returndatacopy( + interpreter: &mut Interpreter, + _host: &mut H, +) { check!(interpreter, BYZANTIUM); - pop!(interpreter, memory_offset, offset, len); + popn!([memory_offset, offset, len], interpreter); let len = as_usize_or_fail!(interpreter, len); let data_offset = as_usize_saturated!(offset); @@ -124,8 +160,10 @@ pub fn returndatacopy(interpreter: &mut Interprete // Old legacy behavior is to panic if data_end is out of scope of return buffer. // This behavior is changed in EOF. let data_end = data_offset.saturating_add(len); - if data_end > interpreter.return_data_buffer.len() && !interpreter.is_eof { - interpreter.instruction_result = InstructionResult::OutOfOffset; + if data_end > interpreter.return_data.buffer().len() && !interpreter.runtime_flag.is_eof() { + interpreter + .control + .set_instruction_result(InstructionResult::OutOfOffset); return; } @@ -134,49 +172,59 @@ pub fn returndatacopy(interpreter: &mut Interprete }; // Note: this can't panic because we resized memory to fit. - interpreter.shared_memory.set_data( + interpreter.memory.set_data( memory_offset, data_offset, len, - interpreter.return_data_buffer.as_ref(), + interpreter.return_data.buffer(), ); } /// Part of EOF ``. -pub fn returndataload(interpreter: &mut Interpreter, _host: &mut H) { +pub fn returndataload( + interpreter: &mut Interpreter, + _host: &mut H, +) { require_eof!(interpreter); gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, offset); + popn_top!([], offset, interpreter); let offset_usize = as_usize_saturated!(offset); let mut output = [0u8; 32]; if let Some(available) = interpreter - .return_data_buffer + .return_data + .buffer() .len() .checked_sub(offset_usize) { let copy_len = available.min(32); output[..copy_len].copy_from_slice( - &interpreter.return_data_buffer[offset_usize..offset_usize + copy_len], + &interpreter.return_data.buffer()[offset_usize..offset_usize + copy_len], ); } *offset = B256::from(output).into(); } -pub fn gas(interpreter: &mut Interpreter, _host: &mut H) { +pub fn gas( + interpreter: &mut Interpreter, + _host: &mut H, +) { gas!(interpreter, gas::BASE); - push!(interpreter, U256::from(interpreter.gas.remaining())); + push!( + interpreter, + U256::from(interpreter.control.gas().remaining()) + ); } // common logic for copying data from a source buffer to the EVM's memory pub fn memory_resize( - interpreter: &mut Interpreter, + interpreter: &mut Interpreter, memory_offset: U256, len: usize, ) -> Option { // safe to cast usize to u64 - gas_or_fail!(interpreter, gas::copy_cost_verylow(len as u64), None); + gas_or_fail!(interpreter, gas::copy_cost_verylow(len), None); if len == 0 { return None; } @@ -186,6 +234,8 @@ pub fn memory_resize( Some(memory_offset) } +/* +TODO tests #[cfg(test)] mod test { use super::*; @@ -193,12 +243,12 @@ mod test { use bytecode::opcode::{RETURNDATACOPY, RETURNDATALOAD}; use bytecode::Bytecode; use primitives::bytes; - use specification::hardfork::PragueSpec; - use wiring::DefaultEthereumWiring; + use specification::hardfork::{PragueSpec, SpecId}; + use context_interface::DefaultEthereumWiring; #[test] fn returndataload() { - let table = make_instruction_table::, PragueSpec>(); + let table = make_instruction_table::>(); let mut host = DummyHost::default(); let mut interp = Interpreter::new_bytecode(Bytecode::LegacyRaw( @@ -211,6 +261,7 @@ mod test { .into(), )); interp.is_eof = true; + interp.spec_id = SpecId::PRAGUE; interp.gas = Gas::new(10000); interp.stack.push(U256::from(0)).unwrap(); @@ -256,7 +307,7 @@ mod test { #[test] fn returndatacopy() { - let table = make_instruction_table::<_, PragueSpec>(); + let table = make_instruction_table::(); let mut host = DummyHost::::default(); let mut interp = Interpreter::new_bytecode(Bytecode::LegacyRaw( @@ -271,6 +322,7 @@ mod test { .into(), )); interp.is_eof = true; + interp.spec_id = SpecId::PRAGUE; interp.gas = Gas::new(10000); interp.return_data_buffer = @@ -342,3 +394,5 @@ mod test { assert_eq!(&interp.shared_memory.slice(0, 32), &[0u8; 32]); } } + +*/ diff --git a/crates/interpreter/src/instructions/tx_info.rs b/crates/interpreter/src/instructions/tx_info.rs index 52f6fe359e..2b84b4822f 100644 --- a/crates/interpreter/src/instructions/tx_info.rs +++ b/crates/interpreter/src/instructions/tx_info.rs @@ -1,31 +1,42 @@ -use crate::{gas, Host, Interpreter}; +use crate::{ + gas, + interpreter::Interpreter, + interpreter_types::{InterpreterTypes, LoopControl, RuntimeFlag, StackTrait}, + Host, +}; +use context_interface::{transaction::Eip4844Tx, Block, Transaction, TransactionType}; use primitives::U256; -use specification::hardfork::Spec; -use transaction::Eip4844Tx; -use wiring::{Block, Transaction, TransactionType}; -pub fn gasprice(interpreter: &mut Interpreter, host: &mut H) { +pub fn gasprice( + interpreter: &mut Interpreter, + host: &mut H, +) { gas!(interpreter, gas::BASE); - let env = host.env(); - let basefee = *env.block.basefee(); - push!(interpreter, env.tx.effective_gas_price(basefee)); + let basefee = *host.block().basefee(); + push!(interpreter, host.tx().effective_gas_price(basefee)); } -pub fn origin(interpreter: &mut Interpreter, host: &mut H) { +pub fn origin( + interpreter: &mut Interpreter, + host: &mut H, +) { gas!(interpreter, gas::BASE); - push_b256!( + push!( interpreter, - host.env().tx.common_fields().caller().into_word() + host.tx().common_fields().caller().into_word().into() ); } // EIP-4844: Shard Blob Transactions -pub fn blob_hash(interpreter: &mut Interpreter, host: &mut H) { +pub fn blob_hash( + interpreter: &mut Interpreter, + host: &mut H, +) { check!(interpreter, CANCUN); gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, index); + popn_top!([], index, interpreter); let i = as_usize_saturated!(index); - let tx = &host.env().tx; + let tx = &host.tx(); *index = if tx.tx_type().into() == TransactionType::Eip4844 { tx.eip4844() .blob_versioned_hashes() diff --git a/crates/interpreter/src/instructions/utility.rs b/crates/interpreter/src/instructions/utility.rs index 55b1212771..f6273c8f2d 100644 --- a/crates/interpreter/src/instructions/utility.rs +++ b/crates/interpreter/src/instructions/utility.rs @@ -1,7 +1,109 @@ -pub(crate) unsafe fn read_i16(ptr: *const u8) -> i16 { - i16::from_be_bytes(core::slice::from_raw_parts(ptr, 2).try_into().unwrap()) +pub use crate::InstructionResult; +pub use primitives::U256; +use primitives::{Address, B256}; + +/// Pushes an arbitrary length slice of bytes onto the stack, padding the last word with zeros +/// if necessary. +/// +/// # Panics +/// +/// Panics if slice is longer than 32 bytes. +#[inline] +pub fn cast_slice_to_u256(slice: &[u8], dest: &mut U256) { + if slice.is_empty() { + return; + } + assert!(slice.len() <= 32, "slice too long"); + + let n_words = (slice.len() + 31) / 32; + + // SAFETY: length checked above. + unsafe { + //let dst = self.data.as_mut_ptr().add(self.data.len()).cast::(); + //self.data.set_len(new_len); + let dst = dest.as_limbs_mut().as_mut_ptr(); + + let mut i = 0; + + // write full words + let words = slice.chunks_exact(32); + let partial_last_word = words.remainder(); + for word in words { + // Note: we unroll `U256::from_be_bytes` here to write directly into the buffer, + // instead of creating a 32 byte array on the stack and then copying it over. + for l in word.rchunks_exact(8) { + dst.add(i).write(u64::from_be_bytes(l.try_into().unwrap())); + i += 1; + } + } + + if partial_last_word.is_empty() { + return; + } + + // write limbs of partial last word + let limbs = partial_last_word.rchunks_exact(8); + let partial_last_limb = limbs.remainder(); + for l in limbs { + dst.add(i).write(u64::from_be_bytes(l.try_into().unwrap())); + i += 1; + } + + // write partial last limb by padding with zeros + if !partial_last_limb.is_empty() { + let mut tmp = [0u8; 8]; + tmp[8 - partial_last_limb.len()..].copy_from_slice(partial_last_limb); + dst.add(i).write(u64::from_be_bytes(tmp)); + i += 1; + } + + debug_assert_eq!((i + 3) / 4, n_words, "wrote too much"); + + // zero out upper bytes of last word + let m = i % 4; // 32 / 8 + if m != 0 { + dst.add(i).write_bytes(0, 4 - m); + } + } +} + +pub trait IntoU256 { + fn into_u256(self) -> U256; +} + +impl IntoU256 for Address { + fn into_u256(self) -> U256 { + self.into_word().into_u256() + } } -pub(crate) unsafe fn read_u16(ptr: *const u8) -> u16 { - u16::from_be_bytes(core::slice::from_raw_parts(ptr, 2).try_into().unwrap()) +impl IntoU256 for B256 { + fn into_u256(self) -> U256 { + U256::from_be_bytes(self.0) + } +} + +pub trait IntoAddress { + fn into_address(self) -> Address; +} + +impl IntoAddress for U256 { + fn into_address(self) -> Address { + Address::from_word(B256::from(self.to_be_bytes())) + } +} + +#[cfg(test)] +mod tests { + use primitives::address; + + use super::*; + + #[test] + fn test_into_u256() { + let addr = address!("0000000000000000000000000000000000000001"); + let u256 = addr.into_u256(); + assert_eq!(u256, U256::from(0x01)); + assert_eq!(u256.into_address(), addr); + } } diff --git a/crates/interpreter/src/interpreter.rs b/crates/interpreter/src/interpreter.rs index 8198eec0f2..a2198d3a84 100644 --- a/crates/interpreter/src/interpreter.rs +++ b/crates/interpreter/src/interpreter.rs @@ -1,411 +1,206 @@ -mod contract; +pub mod ext_bytecode; +mod input; +mod loop_control; +mod return_data; +mod runtime_flags; #[cfg(feature = "serde")] pub mod serde; mod shared_memory; mod stack; - -pub use contract::Contract; -pub use shared_memory::{num_words, SharedMemory, EMPTY_SHARED_MEMORY}; -pub use stack::{Stack, STACK_LIMIT}; +mod subroutine_stack; use crate::{ - gas, push, push_b256, return_ok, return_revert, CallOutcome, CreateOutcome, FunctionStack, Gas, - Host, InstructionResult, InterpreterAction, + interpreter_types::*, table::CustomInstruction, Gas, Host, Instruction, InstructionResult, + InterpreterAction, }; -use bytecode::{Bytecode, Eof}; -use core::cmp::min; -use primitives::{Bytes, U256}; -use std::borrow::ToOwned; -use std::sync::Arc; - -/// EVM bytecode interpreter. -#[derive(Debug)] -pub struct Interpreter { - /// The current instruction pointer. - pub instruction_pointer: *const u8, - /// The gas state. - pub gas: Gas, - /// Contract information and invoking data - pub contract: Contract, - /// The execution control flag. If this is not set to `Continue`, the interpreter will stop - /// execution. - pub instruction_result: InstructionResult, - /// Currently run Bytecode that instruction result will point to. - /// Bytecode is owned by the contract. - pub bytecode: Bytes, - /// Whether we are Interpreting the Ethereum Object Format (EOF) bytecode. - /// This is local field that is set from `contract.is_eof()`. - pub is_eof: bool, - /// Is init flag for eof create - pub is_eof_init: bool, - /// Shared memory. - /// - /// Note: This field is only set while running the interpreter loop. - /// Otherwise it is taken and replaced with empty shared memory. - pub shared_memory: SharedMemory, - /// Stack. - pub stack: Stack, - /// EOF function stack. - pub function_stack: FunctionStack, - /// The return data buffer for internal calls. - /// It has multi usage: - /// - /// * It contains the output bytes of call sub call. - /// * When this interpreter finishes execution it contains the output bytes of this contract. - pub return_data_buffer: Bytes, - /// Whether the interpreter is in "staticcall" mode, meaning no state changes can happen. - pub is_static: bool, - /// Actions that the EVM should do. - /// - /// Set inside CALL or CREATE instructions and RETURN or REVERT instructions. Additionally those instructions will set - /// InstructionResult to CallOrCreate/Return/Revert so we know the reason. - pub next_action: InterpreterAction, -} - -impl Default for Interpreter { - fn default() -> Self { - Self::new(Contract::default(), u64::MAX, false) - } +use bytecode::Bytecode; + +use core::cell::RefCell; +pub use ext_bytecode::ExtBytecode; +pub use input::InputsImpl; +use loop_control::LoopControl as LoopControlImpl; +use primitives::Bytes; +use return_data::ReturnDataImpl; +pub use runtime_flags::RuntimeFlags; +pub use shared_memory::{num_words, MemoryGetter, SharedMemory, EMPTY_SHARED_MEMORY}; +use specification::hardfork::SpecId; +pub use stack::{Stack, STACK_LIMIT}; +use std::rc::Rc; +use subroutine_stack::SubRoutineImpl; + +#[derive(Debug, Clone)] +pub struct Interpreter { + pub bytecode: WIRE::Bytecode, + pub stack: WIRE::Stack, + pub return_data: WIRE::ReturnData, + pub memory: WIRE::Memory, + pub input: WIRE::Input, + pub sub_routine: WIRE::SubRoutineStack, + pub control: WIRE::Control, + pub runtime_flag: WIRE::RuntimeFlag, + pub extend: WIRE::Extend, } -impl Interpreter { +impl Interpreter> { /// Create new interpreter - pub fn new(contract: Contract, gas_limit: u64, is_static: bool) -> Self { - if !contract.bytecode.is_execution_ready() { - panic!("Contract is not execution ready {:?}", contract.bytecode); - } - let is_eof = contract.bytecode.is_eof(); - let bytecode = contract.bytecode.bytecode().clone(); - Self { - instruction_pointer: bytecode.as_ptr(), - bytecode, - contract, - gas: Gas::new(gas_limit), - instruction_result: InstructionResult::Continue, - function_stack: FunctionStack::default(), + pub fn new( + memory: Rc>, + bytecode: Bytecode, + inputs: InputsImpl, + is_static: bool, + is_eof_init: bool, + spec_id: SpecId, + gas_limit: u64, + ) -> Self { + let runtime_flag = RuntimeFlags { + spec_id, is_static, - is_eof, - is_eof_init: false, - return_data_buffer: Bytes::new(), - shared_memory: EMPTY_SHARED_MEMORY, + is_eof: bytecode.is_eof(), + is_eof_init, + }; + Self { + bytecode: ExtBytecode::new(bytecode), stack: Stack::new(), - next_action: InterpreterAction::None, + return_data: ReturnDataImpl::default(), + memory, + input: inputs, + sub_routine: SubRoutineImpl::default(), + control: LoopControlImpl::new(gas_limit), + runtime_flag, + extend: EXT::default(), } } +} - /// Set is_eof_init to true, this is used to enable `RETURNCONTRACT` opcode. - #[inline] - pub fn set_is_eof_init(&mut self) { - self.is_eof_init = true; - } - - #[inline] - pub fn eof(&self) -> Option<&Arc> { - self.contract.bytecode.eof() - } - - /// Test related helper - #[cfg(test)] - pub fn new_bytecode(bytecode: Bytecode) -> Self { - Self::new( - Contract::new( - Bytes::new(), - bytecode, - None, - primitives::Address::default(), - None, - primitives::Address::default(), - U256::ZERO, - ), - 0, - false, - ) - } - - /// Load EOF code into interpreter. PC is assumed to be correctly set - pub(crate) fn load_eof_code(&mut self, idx: usize, pc: usize) { - // SAFETY: eof flag is true only if bytecode is Eof. - let Bytecode::Eof(eof) = &self.contract.bytecode else { - panic!("Expected EOF code section") - }; - let Some(code) = eof.body.code(idx) else { - panic!("Code not found") - }; - self.bytecode = code.clone(); - self.instruction_pointer = unsafe { self.bytecode.as_ptr().add(pc) }; - } +pub struct EthInterpreter { + _phantom: core::marker::PhantomData (EXT, MG)>, +} - /// Inserts the output of a `create` call into the interpreter. - /// - /// This function is used after a `create` call has been executed. It processes the outcome - /// of that call and updates the state of the interpreter accordingly. - /// - /// # Arguments - /// - /// * `create_outcome` - A `CreateOutcome` struct containing the results of the `create` call. - /// - /// # Behavior - /// - /// The function updates the `return_data_buffer` with the data from `create_outcome`. - /// Depending on the `InstructionResult` indicated by `create_outcome`, it performs one of the following: - /// - /// - `Ok`: Pushes the address from `create_outcome` to the stack, updates gas costs, and records any gas refunds. - /// - `Revert`: Pushes `U256::ZERO` to the stack and updates gas costs. - /// - `FatalExternalError`: Sets the `instruction_result` to `InstructionResult::FatalExternalError`. - /// - `Default`: Pushes `U256::ZERO` to the stack. - /// - /// # Side Effects - /// - /// - Updates `return_data_buffer` with the data from `create_outcome`. - /// - Modifies the stack by pushing values depending on the `InstructionResult`. - /// - Updates gas costs and records refunds in the interpreter's `gas` field. - /// - May alter `instruction_result` in case of external errors. - pub fn insert_create_outcome(&mut self, create_outcome: CreateOutcome) { - self.instruction_result = InstructionResult::Continue; - - let instruction_result = create_outcome.instruction_result(); - self.return_data_buffer = if instruction_result.is_revert() { - // Save data to return data buffer if the create reverted - create_outcome.output().to_owned() - } else { - // Otherwise clear it - Bytes::new() - }; +impl InterpreterTypes for EthInterpreter { + type Stack = Stack; + type Memory = Rc>; + type Bytecode = ExtBytecode; + type ReturnData = ReturnDataImpl; + type Input = InputsImpl; + type SubRoutineStack = SubRoutineImpl; + type Control = LoopControlImpl; + type RuntimeFlag = RuntimeFlags; + type Extend = EXT; +} - match instruction_result { - return_ok!() => { - let address = create_outcome.address; - push_b256!(self, address.unwrap_or_default().into_word()); - self.gas.erase_cost(create_outcome.gas().remaining()); - self.gas.record_refund(create_outcome.gas().refunded()); - } - return_revert!() => { - push!(self, U256::ZERO); - self.gas.erase_cost(create_outcome.gas().remaining()); - } - InstructionResult::FatalExternalError => { - panic!("Fatal external error in insert_create_outcome"); - } - _ => { - push!(self, U256::ZERO); - } - } - } +pub trait InstructionProvider: Clone { + type WIRE: InterpreterTypes; + type Host; - pub fn insert_eofcreate_outcome(&mut self, create_outcome: CreateOutcome) { - self.instruction_result = InstructionResult::Continue; - let instruction_result = create_outcome.instruction_result(); + fn new(context: &mut Self::Host) -> Self; - self.return_data_buffer = if *instruction_result == InstructionResult::Revert { - // Save data to return data buffer if the create reverted - create_outcome.output().to_owned() - } else { - // Otherwise clear it. Note that RETURN opcode should abort. - Bytes::new() - }; + fn table(&mut self) -> &[impl CustomInstruction; 256]; +} - match instruction_result { - InstructionResult::ReturnContract => { - push_b256!( - self, - create_outcome.address.expect("EOF Address").into_word() - ); - self.gas.erase_cost(create_outcome.gas().remaining()); - self.gas.record_refund(create_outcome.gas().refunded()); - } - return_revert!() => { - push!(self, U256::ZERO); - self.gas.erase_cost(create_outcome.gas().remaining()); - } - InstructionResult::FatalExternalError => { - panic!("Fatal external error in insert_eofcreate_outcome"); - } - _ => { - push!(self, U256::ZERO); - } - } - } +pub struct EthInstructionProvider { + instruction_table: Rc<[Instruction; 256]>, +} - /// Inserts the outcome of a call into the virtual machine's state. - /// - /// This function takes the result of a call, represented by `CallOutcome`, - /// and updates the virtual machine's state accordingly. It involves updating - /// the return data buffer, handling gas accounting, and setting the memory - /// in shared storage based on the outcome of the call. - /// - /// # Arguments - /// - /// * `shared_memory` - A mutable reference to the shared memory used by the virtual machine. - /// * `call_outcome` - The outcome of the call to be processed, containing details such as - /// instruction result, gas information, and output data. - /// - /// # Behavior - /// - /// The function first copies the output data from the call outcome to the virtual machine's - /// return data buffer. It then checks the instruction result from the call outcome: - /// - /// - `return_ok!()`: Processes successful execution, refunds gas, and updates shared memory. - /// - `return_revert!()`: Handles a revert by only updating the gas usage and shared memory. - /// - `InstructionResult::FatalExternalError`: Sets the instruction result to a fatal external error. - /// - Any other result: No specific action is taken. - pub fn insert_call_outcome( - &mut self, - shared_memory: &mut SharedMemory, - call_outcome: CallOutcome, - ) { - self.instruction_result = InstructionResult::Continue; - - let out_offset = call_outcome.memory_start(); - let out_len = call_outcome.memory_length(); - let out_ins_result = *call_outcome.instruction_result(); - let out_gas = call_outcome.gas(); - self.return_data_buffer = call_outcome.result.output; - - let target_len = min(out_len, self.return_data_buffer.len()); - match out_ins_result { - return_ok!() => { - // return unspend gas. - self.gas.erase_cost(out_gas.remaining()); - self.gas.record_refund(out_gas.refunded()); - shared_memory.set(out_offset, &self.return_data_buffer[..target_len]); - push!( - self, - if self.is_eof { - U256::ZERO - } else { - U256::from(1) - } - ); - } - return_revert!() => { - self.gas.erase_cost(out_gas.remaining()); - shared_memory.set(out_offset, &self.return_data_buffer[..target_len]); - push!( - self, - if self.is_eof { - U256::from(1) - } else { - U256::ZERO - } - ); - } - InstructionResult::FatalExternalError => { - panic!("Fatal external error in insert_call_outcome"); - } - _ => { - push!( - self, - if self.is_eof { - U256::from(2) - } else { - U256::ZERO - } - ); - } +impl Clone for EthInstructionProvider +where + WIRE: InterpreterTypes, +{ + fn clone(&self) -> Self { + Self { + instruction_table: self.instruction_table.clone(), } } +} - /// Returns the opcode at the current instruction pointer. - #[inline] - pub fn current_opcode(&self) -> u8 { - unsafe { *self.instruction_pointer } - } +impl InstructionProvider for EthInstructionProvider +where + WIRE: InterpreterTypes, + HOST: Host, +{ + type WIRE = WIRE; + type Host = HOST; - /// Returns a reference to the contract. - #[inline] - pub fn contract(&self) -> &Contract { - &self.contract + fn new(_context: &mut Self::Host) -> Self { + Self { + instruction_table: Rc::new(crate::table::make_instruction_table::()), + } } - /// Returns a reference to the interpreter's gas state. - #[inline] - pub fn gas(&self) -> &Gas { - &self.gas + // TODO make impl a associate type. With this associate type we can implement + // InspectorInstructionProvider over generic type. + fn table(&mut self) -> &[impl CustomInstruction; 256] { + self.instruction_table.as_ref() } +} - /// Returns a reference to the interpreter's stack. - #[inline] - pub fn stack(&self) -> &Stack { - &self.stack - } +impl CustomInstruction for Instruction { + type Wire = IW; + type Host = H; - /// Returns a mutable reference to the interpreter's stack. #[inline] - pub fn stack_mut(&mut self) -> &mut Stack { - &mut self.stack + fn exec(&self, interpreter: &mut Interpreter, host: &mut Self::Host) { + (self)(interpreter, host); } - /// Returns the current program counter. #[inline] - pub fn program_counter(&self) -> usize { - // SAFETY: `instruction_pointer` should be at an offset from the start of the bytecode. - // In practice this is always true unless a caller modifies the `instruction_pointer` field manually. - unsafe { self.instruction_pointer.offset_from(self.bytecode.as_ptr()) as usize } + fn from_base(instruction: Instruction) -> Self { + instruction } +} +impl Interpreter { /// Executes the instruction at the current instruction pointer. /// /// Internally it will increment instruction pointer by one. #[inline] - pub(crate) fn step(&mut self, instruction_table: &[FN; 256], host: &mut H) + pub(crate) fn step(&mut self, instruction_table: &[FN; 256], host: &mut H) where - FN: Fn(&mut Interpreter, &mut H), + FN: CustomInstruction, { // Get current opcode. - let opcode = unsafe { *self.instruction_pointer }; + let opcode = self.bytecode.opcode(); // SAFETY: In analysis we are doing padding of bytecode so that we are sure that last // byte instruction is STOP so we are safe to just increment program_counter bcs on last instruction // it will do noop and just stop execution of this contract - self.instruction_pointer = unsafe { self.instruction_pointer.offset(1) }; + self.bytecode.relative_jump(1); // execute instruction. - (instruction_table[opcode as usize])(self, host) - } - - /// Take memory and replace it with empty memory. - pub fn take_memory(&mut self) -> SharedMemory { - core::mem::replace(&mut self.shared_memory, EMPTY_SHARED_MEMORY) + instruction_table[opcode as usize].exec(self, host) } /// Executes the interpreter until it returns or stops. - pub fn run( + pub fn run( &mut self, - shared_memory: SharedMemory, instruction_table: &[FN; 256], host: &mut H, ) -> InterpreterAction where - FN: Fn(&mut Interpreter, &mut H), + FN: CustomInstruction, { - self.next_action = InterpreterAction::None; - self.shared_memory = shared_memory; + self.control + .set_next_action(InterpreterAction::None, InstructionResult::Continue); + // main loop - while self.instruction_result == InstructionResult::Continue { + while self.control.instruction_result().is_continue() { self.step(instruction_table, host); } // Return next action if it is some. - if self.next_action.is_some() { - return core::mem::take(&mut self.next_action); + let action = self.control.take_next_action(); + if action.is_some() { + return action; } // If not, return action without output as it is a halt. InterpreterAction::Return { result: InterpreterResult { - result: self.instruction_result, + result: self.control.instruction_result(), // return empty bytecode output: Bytes::new(), - gas: self.gas, + gas: *self.control.gas(), }, } } - - /// Resize the memory to the new size. Returns whether the gas was enough to resize the memory. - #[inline] - #[must_use] - pub fn resize_memory(&mut self, new_size: usize) -> bool { - resize_memory(&mut self.shared_memory, &mut self.gas, new_size) - } } /// The result of an interpreter operation. @@ -449,45 +244,44 @@ impl InterpreterResult { } } -/// Resize the memory to the new size. Returns whether the gas was enough to resize the memory. -#[inline(never)] -#[cold] -#[must_use] -pub fn resize_memory(memory: &mut SharedMemory, gas: &mut Gas, new_size: usize) -> bool { - let new_words = num_words(new_size as u64); - let new_cost = gas::memory_gas(new_words); - let current_cost = memory.current_expansion_cost(); - let cost = new_cost - current_cost; - let success = gas.record_cost(cost); - if success { - memory.resize((new_words as usize) * 32); - } - success -} +// /// Resize the memory to the new size. Returns whether the gas was enough to resize the memory. +// #[inline(never)] +// #[cold] +// #[must_use] +// pub fn resize_memory(memory: &mut SharedMemory, gas: &mut Gas, new_size: usize) -> bool { +// let new_words = num_words(new_size as u64); +// let new_cost = gas::memory_gas(new_words); +// let current_cost = memory.current_expansion_cost(); +// let cost = new_cost - current_cost; +// let success = gas.record_cost(cost); +// if success { +// memory.resize((new_words as usize) * 32); +// } +// success +// } #[cfg(test)] mod tests { - use super::*; - use crate::{table::InstructionTable, DummyHost}; - use specification::hardfork::CancunSpec; - use wiring::DefaultEthereumWiring; - - #[test] - fn object_safety() { - let mut interp = Interpreter::new(Contract::default(), u64::MAX, false); - - let mut host = crate::DummyHost::::default(); - let table: &InstructionTable> = - &crate::table::make_instruction_table::, CancunSpec>(); - let _ = interp.run(EMPTY_SHARED_MEMORY, table, &mut host); - - let host: &mut dyn Host = - &mut host as &mut dyn Host; - let table: &InstructionTable> = - &crate::table::make_instruction_table::< - dyn Host, - CancunSpec, - >(); - let _ = interp.run(EMPTY_SHARED_MEMORY, table, host); - } + // use super::*; + // use crate::{table::InstructionTable, DummyHost}; + + // #[test] + // fn object_safety() { + // let mut interp = Interpreter::new(Contract::default(), u64::MAX, false); + // interp.spec_id = SpecId::CANCUN; + // let mut host = crate::DummyHost::::default(); + // let table: &InstructionTable> = + // &crate::table::make_instruction_table::>( + // ); + // let _ = interp.run(EMPTY_SHARED_MEMORY, table, &mut host); + + // let host: &mut dyn Host = + // &mut host as &mut dyn Host; + // let table: &InstructionTable> = + // &crate::table::make_instruction_table::< + // Interpreter, + // dyn Host, + // >(); + // let _ = interp.run(EMPTY_SHARED_MEMORY, table, host); + // } } diff --git a/crates/interpreter/src/interpreter/contract.rs b/crates/interpreter/src/interpreter/contract.rs deleted file mode 100644 index 0863701cf8..0000000000 --- a/crates/interpreter/src/interpreter/contract.rs +++ /dev/null @@ -1,104 +0,0 @@ -use crate::CallInputs; -use bytecode::Bytecode; -use primitives::{Address, Bytes, TxKind, B256, U256}; -use wiring::{default::EnvWiring, EvmWiring, Transaction}; - -/// EVM contract information. -#[derive(Clone, Debug, Default)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct Contract { - /// Contracts data - pub input: Bytes, - /// Bytecode contains contract code, size of original code, analysis with gas block and jump table. - /// Note that current code is extended with push padding and STOP at end. - pub bytecode: Bytecode, - /// Bytecode hash for legacy. For EOF this would be None. - pub hash: Option, - /// Target address of the account. Storage of this address is going to be modified. - pub target_address: Address, - /// Address of the account the bytecode was loaded from. This can be different from target_address - /// in the case of DELEGATECALL or CALLCODE - pub bytecode_address: Option
, - /// Caller of the EVM. - pub caller: Address, - /// Value send to contract from transaction or from CALL opcodes. - pub call_value: U256, -} - -impl Contract { - /// Instantiates a new contract by analyzing the given bytecode. - #[inline] - pub fn new( - input: Bytes, - bytecode: Bytecode, - hash: Option, - target_address: Address, - bytecode_address: Option
, - caller: Address, - call_value: U256, - ) -> Self { - let bytecode = bytecode.into_analyzed(); - - Self { - input, - bytecode, - hash, - target_address, - bytecode_address, - caller, - call_value, - } - } - - /// Creates a new contract from the given [`EnvWiring`]. - #[inline] - pub fn new_env( - env: &EnvWiring, - bytecode: Bytecode, - hash: Option, - ) -> Self { - let bytecode_address = match env.tx.kind() { - TxKind::Call(caller) => Some(caller), - TxKind::Create => None, - }; - let target_address = bytecode_address.unwrap_or_default(); - - Self::new( - env.tx.common_fields().input().clone(), - bytecode, - hash, - target_address, - bytecode_address, - env.tx.common_fields().caller(), - env.tx.common_fields().value(), - ) - } - - /// Creates a new contract from the given inputs. - #[inline] - pub fn new_with_context( - input: Bytes, - bytecode: Bytecode, - hash: Option, - call_context: &CallInputs, - ) -> Self { - Self::new( - input, - bytecode, - hash, - call_context.target_address, - Some(call_context.bytecode_address), - call_context.caller, - call_context.call_value(), - ) - } - - /// Returns whether the given position is a valid jump destination. - #[inline] - pub fn is_valid_jump(&self, pos: usize) -> bool { - self.bytecode - .legacy_jump_table() - .map(|i| i.is_valid(pos)) - .unwrap_or(false) - } -} diff --git a/crates/interpreter/src/interpreter/ext_bytecode.rs b/crates/interpreter/src/interpreter/ext_bytecode.rs new file mode 100644 index 0000000000..9beeaca4d2 --- /dev/null +++ b/crates/interpreter/src/interpreter/ext_bytecode.rs @@ -0,0 +1,157 @@ +use bytecode::{ + eof::TypesSection, + utils::{read_i16, read_u16}, + Bytecode, +}; +use primitives::Bytes; + +use super::{EofCodeInfo, EofContainer, EofData, Immediates, Jumps, LegacyBytecode}; + +#[derive(Debug)] +pub struct ExtBytecode { + pub base: Bytecode, + pub instruction_pointer: *const u8, +} + +impl ExtBytecode { + /// Create new extended bytecode and set the instruction pointer to the start of the bytecode. + pub fn new(base: Bytecode) -> Self { + let instruction_pointer = base.bytecode().as_ptr(); + Self { + base, + instruction_pointer, + } + } +} + +impl Jumps for ExtBytecode { + #[inline] + fn relative_jump(&mut self, offset: isize) { + self.instruction_pointer = unsafe { self.instruction_pointer.offset(offset) }; + } + #[inline] + fn absolute_jump(&mut self, offset: usize) { + self.instruction_pointer = unsafe { self.base.bytecode().as_ptr().add(offset) }; + } + #[inline] + fn is_valid_legacy_jump(&mut self, offset: usize) -> bool { + self.base + .legacy_jump_table() + .expect("Panic if not legacy") + .is_valid(offset) + } + + #[inline] + fn opcode(&self) -> u8 { + // SAFETY: `instruction_pointer` always point to bytecode. + unsafe { *self.instruction_pointer } + } + #[inline] + fn pc(&self) -> usize { + // SAFETY: `instruction_pointer` should be at an offset from the start of the bytecode. + // In practice this is always true unless a caller modifies the `instruction_pointer` field manually. + unsafe { + self.instruction_pointer + .offset_from(self.base.bytecode().as_ptr()) as usize + } + } +} + +impl Immediates for ExtBytecode { + #[inline] + fn read_i16(&self) -> i16 { + unsafe { read_i16(self.instruction_pointer) } + } + + #[inline] + fn read_u16(&self) -> u16 { + unsafe { read_u16(self.instruction_pointer) } + } + + #[inline] + fn read_i8(&self) -> i8 { + unsafe { core::mem::transmute(*self.instruction_pointer) } + } + + #[inline] + fn read_u8(&self) -> u8 { + unsafe { *self.instruction_pointer } + } + + #[inline] + fn read_slice(&self, len: usize) -> &[u8] { + unsafe { core::slice::from_raw_parts(self.instruction_pointer, len) } + } + + #[inline] + fn read_offset_i16(&self, offset: isize) -> i16 { + unsafe { + read_i16( + self.instruction_pointer + // offset for max_index that is one byte + .offset(offset), + ) + } + } + + #[inline] + fn read_offset_u16(&self, offset: isize) -> u16 { + unsafe { + read_u16( + self.instruction_pointer + // offset for max_index that is one byte + .offset(offset), + ) + } + } +} + +impl EofCodeInfo for ExtBytecode { + fn code_section_info(&self, idx: usize) -> Option<&TypesSection> { + self.base + .eof() + .and_then(|eof| eof.body.types_section.get(idx)) + } + + fn code_section_pc(&self, idx: usize) -> Option { + self.base + .eof() + .and_then(|eof| eof.body.eof_code_section_start(idx)) + } +} + +impl EofData for ExtBytecode { + fn data(&self) -> &[u8] { + self.base.eof().expect("eof").data() + } + + fn data_slice(&self, offset: usize, len: usize) -> &[u8] { + self.base.eof().expect("eof").data_slice(offset, len) + } + + fn data_size(&self) -> usize { + self.base.eof().expect("eof").header.data_size as usize + } +} + +impl EofContainer for ExtBytecode { + fn eof_container(&self, index: usize) -> Option<&Bytes> { + self.base + .eof() + .and_then(|eof| eof.body.container_section.get(index)) + } +} + +impl LegacyBytecode for ExtBytecode { + fn bytecode_len(&self) -> usize { + // Inform the optimizer that the bytecode cannot be EOF to remove a bounds check. + assume!(!self.base.is_eof()); + self.base.len() + } + + fn bytecode_slice(&self) -> &[u8] { + // Inform the optimizer that the bytecode cannot be EOF to remove a bounds check. + assume!(!self.base.is_eof()); + self.base.original_byte_slice() + } +} diff --git a/crates/interpreter/src/interpreter/input.rs b/crates/interpreter/src/interpreter/input.rs new file mode 100644 index 0000000000..2adb0e5827 --- /dev/null +++ b/crates/interpreter/src/interpreter/input.rs @@ -0,0 +1,27 @@ +use crate::interpreter_types::InputsTrait; +use primitives::{Address, Bytes, U256}; + +pub struct InputsImpl { + pub target_address: Address, + pub caller_address: Address, + pub input: Bytes, + pub call_value: U256, +} + +impl InputsTrait for InputsImpl { + fn target_address(&self) -> Address { + self.target_address + } + + fn caller_address(&self) -> Address { + self.caller_address + } + + fn input(&self) -> &[u8] { + &self.input + } + + fn call_value(&self) -> U256 { + self.call_value + } +} diff --git a/crates/interpreter/src/interpreter/loop_control.rs b/crates/interpreter/src/interpreter/loop_control.rs new file mode 100644 index 0000000000..a0f5d00b8c --- /dev/null +++ b/crates/interpreter/src/interpreter/loop_control.rs @@ -0,0 +1,46 @@ +use crate::interpreter_types::LoopControl as LoopControlTrait; +use crate::{Gas, InstructionResult, InterpreterAction}; + +pub struct LoopControl { + /// The execution control flag. If this is not set to `Continue`, the interpreter will stop + /// execution. + pub instruction_result: InstructionResult, + /// Actions that the EVM should do. + /// + /// Set inside CALL or CREATE instructions and RETURN or REVERT instructions. Additionally those instructions will set + /// InstructionResult to CallOrCreate/Return/Revert so we know the reason. + pub next_action: InterpreterAction, + pub gas: Gas, +} + +impl LoopControl { + pub fn new(gas_limit: u64) -> Self { + Self { + instruction_result: InstructionResult::Continue, + next_action: InterpreterAction::None, + gas: Gas::new(gas_limit), + } + } +} + +impl LoopControlTrait for LoopControl { + fn set_instruction_result(&mut self, result: InstructionResult) { + self.instruction_result = result; + } + + fn set_next_action(&mut self, action: InterpreterAction, result: InstructionResult) { + self.next_action = action; + self.instruction_result = result; + } + + fn gas(&mut self) -> &mut Gas { + &mut self.gas + } + + fn instruction_result(&self) -> InstructionResult { + self.instruction_result + } + fn take_next_action(&mut self) -> InterpreterAction { + core::mem::take(&mut self.next_action) + } +} diff --git a/crates/interpreter/src/interpreter/return_data.rs b/crates/interpreter/src/interpreter/return_data.rs new file mode 100644 index 0000000000..e1a6655e2e --- /dev/null +++ b/crates/interpreter/src/interpreter/return_data.rs @@ -0,0 +1,15 @@ +use crate::interpreter::ReturnData; +use primitives::Bytes; + +#[derive(Clone, Debug, Default)] +pub struct ReturnDataImpl(Bytes); + +impl ReturnData for ReturnDataImpl { + fn buffer(&self) -> &[u8] { + self.0.as_ref() + } + + fn buffer_mut(&mut self) -> &mut Bytes { + &mut self.0 + } +} diff --git a/crates/interpreter/src/interpreter/runtime_flags.rs b/crates/interpreter/src/interpreter/runtime_flags.rs new file mode 100644 index 0000000000..a6e93c25f2 --- /dev/null +++ b/crates/interpreter/src/interpreter/runtime_flags.rs @@ -0,0 +1,28 @@ +use specification::hardfork::SpecId; + +use super::RuntimeFlag; + +pub struct RuntimeFlags { + pub is_static: bool, + pub is_eof_init: bool, + pub is_eof: bool, + pub spec_id: SpecId, +} + +impl RuntimeFlag for RuntimeFlags { + fn is_static(&self) -> bool { + self.is_static + } + + fn is_eof(&self) -> bool { + self.is_eof + } + + fn is_eof_init(&self) -> bool { + self.is_eof_init + } + + fn spec_id(&self) -> SpecId { + self.spec_id + } +} diff --git a/crates/interpreter/src/interpreter/serde.rs b/crates/interpreter/src/interpreter/serde.rs index 829b1baef3..cf980d2b93 100644 --- a/crates/interpreter/src/interpreter/serde.rs +++ b/crates/interpreter/src/interpreter/serde.rs @@ -1,126 +1,130 @@ -use super::Interpreter; -use crate::{ - Contract, FunctionStack, Gas, InstructionResult, InterpreterAction, SharedMemory, Stack, -}; -use primitives::Bytes; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; +// use super::{subroutine_stack::SubRoutineImpl, Interpreter}; +// use crate::{Contract, Gas, InstructionResult, InterpreterAction, SharedMemory, Stack}; +// use primitives::Bytes; +// use serde::{Deserialize, Deserializer, Serialize, Serializer}; +// use specification::hardfork::SpecId; -#[derive(Serialize)] -struct InterpreterSerde<'a> { - program_counter: usize, +// #[derive(Serialize)] +// struct InterpreterSerde<'a> { +// program_counter: usize, - gas: &'a Gas, - contract: &'a Contract, - instruction_result: InstructionResult, - bytecode: &'a Bytes, - is_eof: bool, - is_eof_init: bool, - shared_memory: &'a SharedMemory, - stack: &'a Stack, - function_stack: &'a FunctionStack, - return_data_buffer: &'a Bytes, - is_static: bool, - next_action: &'a InterpreterAction, -} +// gas: &'a Gas, +// contract: &'a Contract, +// instruction_result: InstructionResult, +// bytecode: &'a Bytes, +// is_eof: bool, +// is_eof_init: bool, +// shared_memory: &'a SharedMemory, +// stack: &'a Stack, +// function_stack: &'a SubRoutineImpl, +// return_data_buffer: &'a Bytes, +// is_static: bool, +// next_action: &'a InterpreterAction, +// spec_id: SpecId, +// } -#[derive(Deserialize)] -struct InterpreterDe { - program_counter: usize, +// #[derive(Deserialize)] +// struct InterpreterDe { +// program_counter: usize, - gas: Gas, - contract: Contract, - instruction_result: InstructionResult, - bytecode: Bytes, - is_eof: bool, - is_eof_init: bool, - shared_memory: SharedMemory, - stack: Stack, - function_stack: FunctionStack, - return_data_buffer: Bytes, - is_static: bool, - next_action: InterpreterAction, -} +// gas: Gas, +// contract: Contract, +// instruction_result: InstructionResult, +// bytecode: Bytes, +// is_eof: bool, +// is_eof_init: bool, +// shared_memory: SharedMemory, +// stack: Stack, +// function_stack: SubRoutineImpl, +// return_data_buffer: Bytes, +// is_static: bool, +// next_action: InterpreterAction, +// spec_id: SpecId, +// } -impl Serialize for Interpreter { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - InterpreterSerde { - program_counter: self.program_counter(), - gas: &self.gas, - contract: &self.contract, - instruction_result: self.instruction_result, - bytecode: &self.bytecode, - is_eof: self.is_eof, - is_eof_init: self.is_eof_init, - shared_memory: &self.shared_memory, - stack: &self.stack, - function_stack: &self.function_stack, - return_data_buffer: &self.return_data_buffer, - is_static: self.is_static, - next_action: &self.next_action, - } - .serialize(serializer) - } -} +// impl Serialize for Interpreter { +// fn serialize(&self, serializer: S) -> Result +// where +// S: Serializer, +// { +// InterpreterSerde { +// program_counter: self.bytecode().pc(), +// gas: &self.gas, +// contract: &self.contract, +// instruction_result: self.instruction_result, +// bytecode: &self.bytecode, +// is_eof: self.is_eof, +// is_eof_init: self.is_eof_init, +// shared_memory: &self.shared_memory, +// stack: &self.stack, +// function_stack: &self.function_stack, +// return_data_buffer: &self.return_data_buffer, +// is_static: self.is_static, +// next_action: &self.next_action, +// spec_id: self.spec_id, +// } +// .serialize(serializer) +// } +// } -impl<'de> Deserialize<'de> for Interpreter { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let InterpreterDe { - program_counter, - gas, - contract, - instruction_result, - bytecode, - is_eof, - is_eof_init, - shared_memory, - stack, - function_stack, - return_data_buffer, - is_static, - next_action, - } = InterpreterDe::deserialize(deserializer)?; +// impl<'de> Deserialize<'de> for Interpreter { +// fn deserialize(deserializer: D) -> Result +// where +// D: Deserializer<'de>, +// { +// let InterpreterDe { +// program_counter, +// gas, +// contract, +// instruction_result, +// bytecode, +// is_eof, +// is_eof_init, +// shared_memory, +// stack, +// function_stack, +// return_data_buffer, +// is_static, +// next_action, +// spec_id, +// } = InterpreterDe::deserialize(deserializer)?; - // Reconstruct the instruction pointer from usize - if program_counter >= bytecode.len() { - return Err(serde::de::Error::custom("program_counter out of bounds")); - } +// // Reconstruct the instruction pointer from usize +// if program_counter >= bytecode.len() { +// return Err(serde::de::Error::custom("program_counter out of bounds")); +// } - // SAFETY: range of program_counter checked above - let instruction_pointer = unsafe { bytecode.as_ptr().add(program_counter) }; +// // SAFETY: range of program_counter checked above +// let instruction_pointer = unsafe { bytecode.as_ptr().add(program_counter) }; - Ok(Interpreter { - instruction_pointer, - gas, - contract, - instruction_result, - bytecode, - is_eof, - is_eof_init, - shared_memory, - stack, - function_stack, - return_data_buffer, - is_static, - next_action, - }) - } -} +// Ok(Interpreter { +// instruction_pointer, +// gas, +// contract, +// instruction_result, +// bytecode, +// is_eof, +// is_eof_init, +// shared_memory, +// stack, +// function_stack, +// return_data_buffer, +// is_static, +// next_action, +// spec_id, +// }) +// } +// } -#[cfg(test)] -mod tests { - use super::*; +// #[cfg(test)] +// mod tests { +// use super::*; - #[test] - fn test_serde() { - let interp = Interpreter::new(Contract::default(), u64::MAX, false); - let serialized = bincode::serialize(&interp).unwrap(); - let de: Interpreter = bincode::deserialize(&serialized).unwrap(); - assert_eq!(interp.program_counter(), de.program_counter()); - } -} +// #[test] +// fn test_serde() { +// let interp = Interpreter::new(Contract::default(), u64::MAX, false); +// let serialized = bincode::serialize(&interp).unwrap(); +// let de: Interpreter = bincode::deserialize(&serialized).unwrap(); +// assert_eq!(interp.program_counter(), de.program_counter()); +// } +// } diff --git a/crates/interpreter/src/interpreter/shared_memory.rs b/crates/interpreter/src/interpreter/shared_memory.rs index 4bb5a9f09d..324d4acd22 100644 --- a/crates/interpreter/src/interpreter/shared_memory.rs +++ b/crates/interpreter/src/interpreter/shared_memory.rs @@ -1,6 +1,13 @@ -use core::{cmp::min, fmt, ops::Range}; +use core::{ + cell::{Ref, RefCell}, + cmp::min, + fmt, + ops::{Deref, Range}, +}; use primitives::{hex, B256, U256}; -use std::vec::Vec; +use std::{rc::Rc, vec::Vec}; + +use super::MemoryTrait; /// A sequential memory shared between calls, which uses /// a `Vec` for internal representation. @@ -16,7 +23,7 @@ pub struct SharedMemory { checkpoints: Vec, /// Invariant: equals `self.checkpoints.last()` last_checkpoint: usize, - /// Memory limit. See [`CfgEnv`](wiring::default::CfgEnv). + /// Memory limit. See [`Cfg`](context_interface::Cfg). #[cfg(feature = "memory_limit")] memory_limit: u64, } @@ -48,6 +55,54 @@ impl Default for SharedMemory { } } +pub trait MemoryGetter { + fn memory_mut(&mut self) -> &mut SharedMemory; + fn memory(&self) -> &SharedMemory; +} + +impl MemoryGetter for SharedMemory { + #[inline] + fn memory_mut(&mut self) -> &mut SharedMemory { + self + } + + #[inline] + fn memory(&self) -> &SharedMemory { + self + } +} + +impl MemoryTrait for Rc> { + fn set_data(&mut self, memory_offset: usize, data_offset: usize, len: usize, data: &[u8]) { + self.borrow_mut() + .memory_mut() + .set_data(memory_offset, data_offset, len, data); + } + + fn set(&mut self, memory_offset: usize, data: &[u8]) { + self.borrow_mut().memory_mut().set(memory_offset, data); + } + + fn size(&self) -> usize { + self.borrow().memory().len() + } + + fn copy(&mut self, destination: usize, source: usize, len: usize) { + self.borrow_mut() + .memory_mut() + .copy(destination, source, len); + } + + fn slice(&self, range: Range) -> impl Deref + '_ { + Ref::map(self.borrow(), |i| i.memory().slice_range(range)) + } + + fn resize(&mut self, new_size: usize) -> bool { + self.borrow_mut().memory_mut().resize(new_size); + true + } +} + impl SharedMemory { /// Creates a new memory instance that can be shared between calls. /// @@ -120,12 +175,6 @@ impl SharedMemory { self.len() == 0 } - /// Returns the gas cost for the current memory expansion. - #[inline] - pub fn current_expansion_cost(&self) -> u64 { - crate::gas::memory_gas_for_len(self.len()) - } - /// Resizes the memory in-place so that `len` is equal to `new_len`. #[inline] pub fn resize(&mut self, new_size: usize) { @@ -139,7 +188,7 @@ impl SharedMemory { /// Panics on out of bounds. #[inline] #[cfg_attr(debug_assertions, track_caller)] - pub fn slice(&self, offset: usize, size: usize) -> &[u8] { + pub fn slice_len(&self, offset: usize, size: usize) -> &[u8] { self.slice_range(offset..offset + size) } @@ -179,7 +228,7 @@ impl SharedMemory { /// Panics on out of bounds. #[inline] pub fn get_byte(&self, offset: usize) -> u8 { - self.slice(offset, 1)[0] + self.slice_len(offset, 1)[0] } /// Returns a 32-byte slice of the memory region at the given offset. @@ -189,7 +238,7 @@ impl SharedMemory { /// Panics on out of bounds. #[inline] pub fn get_word(&self, offset: usize) -> B256 { - self.slice(offset, 32).try_into().unwrap() + self.slice_len(offset, 32).try_into().unwrap() } /// Returns a U256 of the memory region at the given offset. @@ -308,7 +357,7 @@ impl SharedMemory { /// Returns number of words what would fit to provided number of bytes, /// i.e. it rounds up the number bytes to number of words. #[inline] -pub const fn num_words(len: u64) -> u64 { +pub const fn num_words(len: usize) -> usize { len.saturating_add(31) / 32 } @@ -326,7 +375,7 @@ mod tests { assert_eq!(num_words(63), 2); assert_eq!(num_words(64), 2); assert_eq!(num_words(65), 3); - assert_eq!(num_words(u64::MAX), u64::MAX / 32); + assert_eq!(num_words(usize::MAX), usize::MAX / 32); } #[test] diff --git a/crates/interpreter/src/interpreter/stack.rs b/crates/interpreter/src/interpreter/stack.rs index 39d84b4627..9ae250aeaa 100644 --- a/crates/interpreter/src/interpreter/stack.rs +++ b/crates/interpreter/src/interpreter/stack.rs @@ -1,8 +1,10 @@ use crate::InstructionResult; use core::{fmt, ptr}; -use primitives::{B256, U256}; +use primitives::U256; use std::vec::Vec; +use super::StackTrait; + /// EVM interpreter stack limit. pub const STACK_LIMIT: usize = 1024; @@ -45,6 +47,42 @@ impl Clone for Stack { } } +impl StackTrait for Stack { + fn len(&self) -> usize { + self.len() + } + + #[inline] + fn popn(&mut self) -> Option<[U256; N]> { + if self.len() < N { + return None; + } + // SAFETY: stack length is checked above. + Some(unsafe { self.popn::() }) + } + + #[inline] + fn popn_top(&mut self) -> Option<([U256; POPN], &mut U256)> { + if self.len() < POPN + 1 { + return None; + } + // SAFETY: stack length is checked above. + Some(unsafe { self.popn_top::() }) + } + + fn exchange(&mut self, n: usize, m: usize) -> bool { + self.exchange(n, m) + } + + fn dup(&mut self, n: usize) -> bool { + self.dup(n) + } + + fn push(&mut self, value: U256) -> bool { + self.push(value) + } +} + impl Stack { /// Instantiate a new stack with the [default stack limit][STACK_LIMIT]. #[inline] @@ -88,6 +126,7 @@ impl Stack { /// Removes the topmost element from the stack and returns it, or `StackUnderflow` if it is /// empty. #[inline] + #[cfg_attr(debug_assertions, track_caller)] pub fn pop(&mut self) -> Result { self.data.pop().ok_or(InstructionResult::StackUnderflow) } @@ -98,6 +137,7 @@ impl Stack { /// /// The caller is responsible for checking the length of the stack. #[inline] + #[cfg_attr(debug_assertions, track_caller)] pub unsafe fn pop_unsafe(&mut self) -> U256 { self.data.pop().unwrap_unchecked() } @@ -108,114 +148,58 @@ impl Stack { /// /// The caller is responsible for checking the length of the stack. #[inline] + #[cfg_attr(debug_assertions, track_caller)] pub unsafe fn top_unsafe(&mut self) -> &mut U256 { let len = self.data.len(); self.data.get_unchecked_mut(len - 1) } - /// Pop the topmost value, returning the value and the new topmost value. - /// - /// # Safety - /// - /// The caller is responsible for checking the length of the stack. - #[inline] - pub unsafe fn pop_top_unsafe(&mut self) -> (U256, &mut U256) { - let pop = self.pop_unsafe(); - let top = self.top_unsafe(); - (pop, top) - } - - /// Pops 2 values from the stack. + /// Pops `N` values from the stack. /// /// # Safety /// /// The caller is responsible for checking the length of the stack. #[inline] - pub unsafe fn pop2_unsafe(&mut self) -> (U256, U256) { - let pop1 = self.pop_unsafe(); - let pop2 = self.pop_unsafe(); - (pop1, pop2) + #[cfg_attr(debug_assertions, track_caller)] + pub unsafe fn popn(&mut self) -> [U256; N] { + if N == 0 { + return [U256::ZERO; N]; + } + let mut result = [U256::ZERO; N]; + for v in result.iter_mut() { + *v = self.data.pop().unwrap_unchecked(); + } + result } - /// Pops 2 values from the stack and returns them, in addition to the new topmost value. + /// Pops `N` values from the stack and returns the top of the stack. /// /// # Safety /// /// The caller is responsible for checking the length of the stack. #[inline] - pub unsafe fn pop2_top_unsafe(&mut self) -> (U256, U256, &mut U256) { - let pop1 = self.pop_unsafe(); - let pop2 = self.pop_unsafe(); + #[cfg_attr(debug_assertions, track_caller)] + pub unsafe fn popn_top(&mut self) -> ([U256; POPN], &mut U256) { + let result = self.popn::(); let top = self.top_unsafe(); - - (pop1, pop2, top) - } - - /// Pops 3 values from the stack. - /// - /// # Safety - /// - /// The caller is responsible for checking the length of the stack. - #[inline] - pub unsafe fn pop3_unsafe(&mut self) -> (U256, U256, U256) { - let pop1 = self.pop_unsafe(); - let pop2 = self.pop_unsafe(); - let pop3 = self.pop_unsafe(); - - (pop1, pop2, pop3) - } - - /// Pops 4 values from the stack. - /// - /// # Safety - /// - /// The caller is responsible for checking the length of the stack. - #[inline] - pub unsafe fn pop4_unsafe(&mut self) -> (U256, U256, U256, U256) { - let pop1 = self.pop_unsafe(); - let pop2 = self.pop_unsafe(); - let pop3 = self.pop_unsafe(); - let pop4 = self.pop_unsafe(); - - (pop1, pop2, pop3, pop4) - } - - /// Pops 5 values from the stack. - /// - /// # Safety - /// - /// The caller is responsible for checking the length of the stack. - #[inline] - pub unsafe fn pop5_unsafe(&mut self) -> (U256, U256, U256, U256, U256) { - let pop1 = self.pop_unsafe(); - let pop2 = self.pop_unsafe(); - let pop3 = self.pop_unsafe(); - let pop4 = self.pop_unsafe(); - let pop5 = self.pop_unsafe(); - - (pop1, pop2, pop3, pop4, pop5) - } - - /// Push a new value into the stack. If it will exceed the stack limit, - /// returns `StackOverflow` error and leaves the stack unchanged. - #[inline] - pub fn push_b256(&mut self, value: B256) -> Result<(), InstructionResult> { - self.push(value.into()) + (result, top) } /// Push a new value onto the stack. /// - /// If it will exceed the stack limit, returns `StackOverflow` error and leaves the stack + /// If it will exceed the stack limit, returns false and leaves the stack /// unchanged. #[inline] - pub fn push(&mut self, value: U256) -> Result<(), InstructionResult> { + #[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); if self.data.len() == STACK_LIMIT { - return Err(InstructionResult::StackOverflow); + return false; } self.data.push(value); - Ok(()) + true } /// Peek a value at given index for the stack, where the top of @@ -236,14 +220,13 @@ impl Stack { /// /// Panics if `n` is 0. #[inline] + #[must_use] #[cfg_attr(debug_assertions, track_caller)] - pub fn dup(&mut self, n: usize) -> Result<(), InstructionResult> { + pub fn dup(&mut self, n: usize) -> bool { assume!(n > 0, "attempted to dup 0"); let len = self.data.len(); - if len < n { - Err(InstructionResult::StackUnderflow) - } else if len + 1 > STACK_LIMIT { - Err(InstructionResult::StackOverflow) + if len < n || len + 1 > STACK_LIMIT { + false } else { // SAFETY: check for out of bounds is done above and it makes this safe to do. unsafe { @@ -251,7 +234,7 @@ impl Stack { ptr::copy_nonoverlapping(ptr.sub(n), ptr, 1); self.data.set_len(len + 1); } - Ok(()) + true } } @@ -262,7 +245,7 @@ impl Stack { /// Panics if `n` is 0. #[inline(always)] #[cfg_attr(debug_assertions, track_caller)] - pub fn swap(&mut self, n: usize) -> Result<(), InstructionResult> { + pub fn swap(&mut self, n: usize) -> bool { self.exchange(0, n) } @@ -275,12 +258,12 @@ impl Stack { /// Panics if `m` is zero. #[inline] #[cfg_attr(debug_assertions, track_caller)] - pub fn exchange(&mut self, n: usize, m: usize) -> Result<(), InstructionResult> { + pub fn exchange(&mut self, n: usize, m: usize) -> bool { assume!(m > 0, "overlapping exchange"); let len = self.data.len(); let n_m_index = n + m; if n_m_index >= len { - return Err(InstructionResult::StackUnderflow); + return false; } // SAFETY: `n` and `n_m` are checked to be within bounds, and they don't overlap. unsafe { @@ -291,7 +274,7 @@ impl Stack { let top = self.data.as_mut_ptr().add(len - 1); core::ptr::swap_nonoverlapping(top.sub(n), top.sub(n_m_index), 1); } - Ok(()) + true } /// Pushes an arbitrary length slice of bytes onto the stack, padding the last word with zeros @@ -466,7 +449,7 @@ mod tests { // Test cloning a partially filled stack let mut partial_stack = Stack::new(); for i in 0..10 { - partial_stack.push(U256::from(i)).unwrap(); + assert!(partial_stack.push(U256::from(i))); } let mut cloned_partial = partial_stack.clone(); assert_eq!(partial_stack, cloned_partial); @@ -474,7 +457,7 @@ mod tests { assert_eq!(cloned_partial.data().capacity(), STACK_LIMIT); // Test that modifying the clone doesn't affect the original - cloned_partial.push(U256::from(100)).unwrap(); + assert!(cloned_partial.push(U256::from(100))); assert_ne!(partial_stack, cloned_partial); assert_eq!(partial_stack.len(), 10); assert_eq!(cloned_partial.len(), 11); @@ -482,7 +465,7 @@ mod tests { // Test cloning a full stack let mut full_stack = Stack::new(); for i in 0..STACK_LIMIT { - full_stack.push(U256::from(i)).unwrap(); + assert!(full_stack.push(U256::from(i))); } let mut cloned_full = full_stack.clone(); assert_eq!(full_stack, cloned_full); @@ -490,13 +473,7 @@ mod tests { assert_eq!(cloned_full.data().capacity(), STACK_LIMIT); // Test push to the full original or cloned stack should return StackOverflow - assert_eq!( - full_stack.push(U256::from(100)), - Err(InstructionResult::StackOverflow) - ); - assert_eq!( - cloned_full.push(U256::from(100)), - Err(InstructionResult::StackOverflow) - ); + assert!(!full_stack.push(U256::from(100))); + assert!(!cloned_full.push(U256::from(100))); } } diff --git a/crates/interpreter/src/function_stack.rs b/crates/interpreter/src/interpreter/subroutine_stack.rs similarity index 56% rename from crates/interpreter/src/function_stack.rs rename to crates/interpreter/src/interpreter/subroutine_stack.rs index 400283ccdb..f6b3470041 100644 --- a/crates/interpreter/src/function_stack.rs +++ b/crates/interpreter/src/interpreter/subroutine_stack.rs @@ -1,17 +1,19 @@ use std::vec::Vec; +use crate::interpreter_types::SubRoutineStack; + /// Function return frame. /// Needed information for returning from a function. #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct FunctionReturnFrame { +pub struct SubRoutineReturnFrame { /// The index of the code container that this frame is executing. pub idx: usize, /// The program counter where frame execution should continue. pub pc: usize, } -impl FunctionReturnFrame { +impl SubRoutineReturnFrame { /// Return new function frame. pub fn new(idx: usize, pc: usize) -> Self { Self { idx, pc } @@ -21,12 +23,12 @@ impl FunctionReturnFrame { /// Function Stack #[derive(Clone, Debug, Default, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct FunctionStack { - pub return_stack: Vec, +pub struct SubRoutineImpl { + pub return_stack: Vec, pub current_code_idx: usize, } -impl FunctionStack { +impl SubRoutineImpl { /// Returns new function stack. pub fn new() -> Self { Self { @@ -35,13 +37,12 @@ impl FunctionStack { } } - /// Pushes a new frame to the stack. and sets current_code_idx to new value. - pub fn push(&mut self, program_counter: usize, new_idx: usize) { - self.return_stack.push(FunctionReturnFrame { - idx: self.current_code_idx, - pc: program_counter, - }); - self.current_code_idx = new_idx; + pub fn len(&self) -> usize { + self.return_stack.len() + } + + pub fn is_empty(&self) -> bool { + self.return_stack.is_empty() } /// Return stack length @@ -49,15 +50,41 @@ impl FunctionStack { self.return_stack.len() } - /// Pops a frame from the stack and sets current_code_idx to the popped frame's idx. - pub fn pop(&mut self) -> Option { - self.return_stack - .pop() - .inspect(|frame| self.current_code_idx = frame.idx) - } - /// Sets current_code_idx, this is needed for JUMPF opcode. pub fn set_current_code_idx(&mut self, idx: usize) { self.current_code_idx = idx; } } + +impl SubRoutineStack for SubRoutineImpl { + fn len(&self) -> usize { + self.return_stack.len() + } + + fn routine_idx(&self) -> usize { + self.current_code_idx + } + + fn push(&mut self, program_counter: usize, new_idx: usize) -> bool { + if self.return_stack.len() >= 1024 { + return false; + } + self.return_stack.push(SubRoutineReturnFrame { + idx: self.current_code_idx, + pc: program_counter, + }); + self.current_code_idx = new_idx; + true + } + + fn pop(&mut self) -> Option { + self.return_stack.pop().map(|i| { + self.current_code_idx = i.idx; + i.pc + }) + } + + fn set_routine_idx(&mut self, idx: usize) { + self.current_code_idx = idx; + } +} diff --git a/crates/interpreter/src/interpreter_action.rs b/crates/interpreter/src/interpreter_action.rs index 5b606a5e71..eca98dc4e7 100644 --- a/crates/interpreter/src/interpreter_action.rs +++ b/crates/interpreter/src/interpreter_action.rs @@ -15,7 +15,7 @@ use std::boxed::Box; #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum NewFrameAction { +pub enum FrameInput { /// CALL, CALLCODE, DELEGATECALL, STATICCALL /// or EOF EXT*CALL instruction called. Call(Box), @@ -25,11 +25,17 @@ pub enum NewFrameAction { EOFCreate(Box), } +impl AsMut for FrameInput { + fn as_mut(&mut self) -> &mut Self { + self + } +} + #[derive(Clone, Debug, Default, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum InterpreterAction { /// New frame - NewFrame(NewFrameAction), + NewFrame(FrameInput), /// Interpreter finished execution. Return { result: InterpreterResult }, /// No action @@ -40,15 +46,12 @@ pub enum InterpreterAction { impl InterpreterAction { /// Returns true if action is call. pub fn is_call(&self) -> bool { - matches!(self, InterpreterAction::NewFrame(NewFrameAction::Call(..))) + matches!(self, InterpreterAction::NewFrame(FrameInput::Call(..))) } /// Returns true if action is create. pub fn is_create(&self) -> bool { - matches!( - self, - InterpreterAction::NewFrame(NewFrameAction::Create(..)) - ) + matches!(self, InterpreterAction::NewFrame(FrameInput::Create(..))) } /// Returns true if action is return. diff --git a/crates/interpreter/src/interpreter_action/create_inputs.rs b/crates/interpreter/src/interpreter_action/create_inputs.rs index adab30c430..241facba6d 100644 --- a/crates/interpreter/src/interpreter_action/create_inputs.rs +++ b/crates/interpreter/src/interpreter_action/create_inputs.rs @@ -1,5 +1,5 @@ +use context_interface::CreateScheme; use primitives::{Address, Bytes, U256}; -use wiring::default::CreateScheme; /// Inputs for a create call. #[derive(Clone, Debug, PartialEq, Eq, Hash)] diff --git a/crates/interpreter/src/interpreter_types.rs b/crates/interpreter/src/interpreter_types.rs new file mode 100644 index 0000000000..c9cee736fb --- /dev/null +++ b/crates/interpreter/src/interpreter_types.rs @@ -0,0 +1,220 @@ +use bytecode::eof::TypesSection; +use specification::hardfork::SpecId; + +use crate::{Gas, InstructionResult, InterpreterAction}; +use core::ops::{Deref, Range}; +use primitives::{Address, Bytes, B256, U256}; + +/// Helper function to read immediates data from the bytecode. +pub trait Immediates { + fn read_i16(&self) -> i16; + fn read_u16(&self) -> u16; + + fn read_i8(&self) -> i8; + fn read_u8(&self) -> u8; + + fn read_offset_i16(&self, offset: isize) -> i16; + fn read_offset_u16(&self, offset: isize) -> u16; + + fn read_slice(&self, len: usize) -> &[u8]; +} + +pub trait InputsTrait { + fn target_address(&self) -> Address; + fn caller_address(&self) -> Address; + fn input(&self) -> &[u8]; + fn call_value(&self) -> U256; +} + +pub trait LegacyBytecode { + fn bytecode_len(&self) -> usize; + fn bytecode_slice(&self) -> &[u8]; +} + +/// Trait for interpreter to be able to jump. +pub trait Jumps { + /// Relative jumps does not require checking for overflow + fn relative_jump(&mut self, offset: isize); + /// Absolute jumps require checking for overflow and if target is a jump destination + /// from jump table. + fn absolute_jump(&mut self, offset: usize); + /// Check legacy jump destination from jump table. + fn is_valid_legacy_jump(&mut self, offset: usize) -> bool; + /// Return current program counter. + fn pc(&self) -> usize; + /// Instruction opcode + fn opcode(&self) -> u8; +} + +pub trait MemoryTrait { + fn set_data(&mut self, memory_offset: usize, data_offset: usize, len: usize, data: &[u8]); + fn set(&mut self, memory_offset: usize, data: &[u8]); + + fn size(&self) -> usize; + fn copy(&mut self, destination: usize, source: usize, len: usize); + + /// Memory slice with range. + /// + /// # Panics + /// + /// Panics if range is out of scope of allocated memory. + fn slice(&self, range: Range) -> impl Deref + '_; + + /// Memory slice len + /// + /// Uses [`MemoryTrait::slice`] internally. + fn slice_len(&self, offset: usize, len: usize) -> impl Deref + '_ { + self.slice(offset..offset + len) + } + + /// Resize memory to new size. + /// + /// # Note + /// + /// It checks memory limits. + fn resize(&mut self, new_size: usize) -> bool; +} + +pub trait EofContainer { + fn eof_container(&self, index: usize) -> Option<&Bytes>; +} + +pub trait SubRoutineStack { + fn len(&self) -> usize; + + fn is_empty(&self) -> bool { + self.len() == 0 + } + + fn routine_idx(&self) -> usize; + + /// Sets new code section without touching subroutine stack. + fn set_routine_idx(&mut self, idx: usize); + + /// Pushes a new frame to the stack and new code index. + fn push(&mut self, old_program_counter: usize, new_idx: usize) -> bool; + + /// Pops previous subroutine, sets previous code index and returns program counter. + fn pop(&mut self) -> Option; + + // /// Return code info from EOF body. + // fn eof_code_info(&self, idx: usize) -> Option<&TypesSection>; +} + +pub trait StackTrait { + /// Returns stack length. + fn len(&self) -> usize; + + /// Returns `true` if stack is empty. + fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Pushes values to the stack + /// Return `true` if push was successful, `false` if stack overflow. + /// + /// # Note + /// + /// Error is internally set in interpreter. + #[must_use] + fn push(&mut self, value: U256) -> bool; + + #[must_use] + fn push_b256(&mut self, value: B256) -> bool { + self.push(value.into()) + } + + /// Pop value from the stack. + #[must_use] + fn popn(&mut self) -> Option<[U256; N]>; + + /// Pop N values from the stack and return top value. + #[must_use] + fn popn_top(&mut self) -> Option<([U256; POPN], &mut U256)>; + + /// Return top value from the stack. + #[must_use] + fn top(&mut self) -> Option<&mut U256> { + self.popn_top::<0>().map(|(_, top)| top) + } + + /// Pop one value from the stack. + #[must_use] + fn pop(&mut self) -> Option { + self.popn::<1>().map(|[value]| value) + } + + #[must_use] + fn pop_address(&mut self) -> Option
{ + self.pop().map(|value| Address::from(value.to_be_bytes())) + } + + /// Exchange two values on the stack. + /// + /// Indexes are based from the top of the stack. + /// + /// Return `true` if swap was successful, `false` if stack underflow. + #[must_use] + fn exchange(&mut self, n: usize, m: usize) -> bool; + + /// Duplicates the `N`th value from the top of the stack. + /// + /// Index is based from the top of the stack. + /// + /// Return `true` if duplicate was successful, `false` if stack underflow. + #[must_use] + fn dup(&mut self, n: usize) -> bool; +} + +pub trait EofData { + fn data(&self) -> &[u8]; + fn data_slice(&self, offset: usize, len: usize) -> &[u8]; + fn data_size(&self) -> usize; +} + +pub trait EofCodeInfo { + /// Returns code information containing stack information. + fn code_section_info(&self, idx: usize) -> Option<&TypesSection>; + + /// Returns program counter at the start of code section. + fn code_section_pc(&self, idx: usize) -> Option; +} + +pub trait ReturnData { + fn buffer(&self) -> &[u8]; + fn buffer_mut(&mut self) -> &mut Bytes; +} + +pub trait LoopControl { + fn set_instruction_result(&mut self, result: InstructionResult); + fn set_next_action(&mut self, action: InterpreterAction, result: InstructionResult); + fn gas(&mut self) -> &mut Gas; + fn instruction_result(&self) -> InstructionResult; + fn take_next_action(&mut self) -> InterpreterAction; +} + +pub trait RuntimeFlag { + fn is_static(&self) -> bool; + fn is_eof(&self) -> bool; + fn is_eof_init(&self) -> bool; + fn spec_id(&self) -> SpecId; +} + +pub trait Interp { + type Instruction; + type Action; + + fn run(&mut self, instructions: &[Self::Instruction; 256]) -> Self::Action; +} + +pub trait InterpreterTypes { + type Stack: StackTrait; + type Memory: MemoryTrait; + type Bytecode: Jumps + Immediates + LegacyBytecode + EofData + EofContainer + EofCodeInfo; + type ReturnData: ReturnData; + type Input: InputsTrait; + type SubRoutineStack: SubRoutineStack; + type Control: LoopControl; + type RuntimeFlag: RuntimeFlag; + type Extend; +} diff --git a/crates/interpreter/src/lib.rs b/crates/interpreter/src/lib.rs index c124d80d50..c39360924f 100644 --- a/crates/interpreter/src/lib.rs +++ b/crates/interpreter/src/lib.rs @@ -17,30 +17,28 @@ use serde_json as _; #[cfg(test)] use walkdir as _; -mod function_stack; pub mod gas; mod host; mod instruction_result; pub mod instructions; pub mod interpreter; pub mod interpreter_action; +pub mod interpreter_types; pub mod table; // Reexport primary types. -pub use function_stack::{FunctionReturnFrame, FunctionStack}; +pub use context_interface::CreateScheme; pub use gas::Gas; -pub use host::{ - AccountLoad, DummyHost, Eip7702CodeLoad, Host, SStoreResult, SelfDestructResult, StateLoad, -}; +pub use host::{DummyHost, Host, SStoreResult, SelfDestructResult, StateLoad}; pub use instruction_result::*; pub use interpreter::{ - num_words, Contract, Interpreter, InterpreterResult, SharedMemory, Stack, EMPTY_SHARED_MEMORY, - STACK_LIMIT, + num_words, InputsImpl, Interpreter, InterpreterResult, MemoryGetter, SharedMemory, Stack, + EMPTY_SHARED_MEMORY, STACK_LIMIT, }; pub use interpreter_action::{ CallInputs, CallOutcome, CallScheme, CallValue, CreateInputs, CreateOutcome, EOFCreateInputs, - EOFCreateKind, InterpreterAction, NewFrameAction, + EOFCreateKind, FrameInput, InterpreterAction, }; +pub use interpreter_types::InterpreterTypes; pub use specification::constants::{MAX_CODE_SIZE, MAX_INITCODE_SIZE}; pub use table::Instruction; -pub use wiring::default::CreateScheme; diff --git a/crates/interpreter/src/table.rs b/crates/interpreter/src/table.rs index 1dc4afaac7..506d3f9ee5 100644 --- a/crates/interpreter/src/table.rs +++ b/crates/interpreter/src/table.rs @@ -1,80 +1,91 @@ #![allow(clippy::wrong_self_convention)] -use crate::{instructions::control, instructions::instruction, Host, Interpreter}; -use specification::hardfork::Spec; +use crate::{ + instructions::{control, instruction}, + interpreter::Interpreter, + interpreter_types::InterpreterTypes, + Host, +}; use std::boxed::Box; /// EVM opcode function signature. -pub type Instruction = fn(&mut Interpreter, &mut H); +pub type Instruction = for<'a> fn(&'a mut Interpreter, &'a mut H); /// Instruction table is list of instruction function pointers mapped to 256 EVM opcodes. -pub type InstructionTable = [Instruction; 256]; +pub type InstructionTable = [Instruction; 256]; /// EVM dynamic opcode function signature. -pub type DynInstruction<'a, H> = dyn Fn(&mut Interpreter, &mut H) + 'a; - -/// EVM boxed dynamic opcode function signature. -pub type BoxedInstruction<'a, H> = Box>; +pub type DynInstruction = dyn Fn(&mut Interpreter, &mut H); /// A table of boxed instructions. -pub type BoxedInstructionTable<'a, H> = [BoxedInstruction<'a, H>; 256]; +pub type CustomInstructionTable = [IT; 256]; + +pub trait CustomInstruction { + type Wire: InterpreterTypes; + type Host; + + fn exec(&self, interpreter: &mut Interpreter, host: &mut Self::Host); + + fn from_base(instruction: Instruction) -> Self; +} /// Either a plain, static instruction table, or a boxed, dynamic instruction table. /// /// Note that `Plain` variant is about 10-20% faster in Interpreter execution. -pub enum InstructionTables<'a, H: ?Sized> { - Plain(InstructionTable), - Boxed(BoxedInstructionTable<'a, H>), -} - -impl<'a, H: Host + ?Sized> InstructionTables<'a, H> { - /// Creates a plain instruction table for the given spec. See [`make_instruction_table`]. - #[inline] - pub const fn new_plain() -> Self { - Self::Plain(make_instruction_table::()) - } +pub enum InstructionTables< + W: InterpreterTypes, + H: ?Sized, + CI: CustomInstruction, +> { + Plain(Box>), + Custom(Box>), } -impl<'a, H: Host + ?Sized + 'a> InstructionTables<'a, H> { +impl InstructionTables +where + WIRE: InterpreterTypes, + H: Host + ?Sized, + CI: CustomInstruction, +{ /// Inserts the instruction into the table with the specified index. #[inline] - pub fn insert(&mut self, opcode: u8, instruction: Instruction) { + pub fn insert(&mut self, opcode: u8, instruction: Instruction) { match self { Self::Plain(table) => table[opcode as usize] = instruction, - Self::Boxed(table) => table[opcode as usize] = Box::new(instruction), + Self::Custom(table) => table[opcode as usize] = CI::from_base(instruction), } } /// Converts the current instruction table to a boxed variant if it is not already, and returns /// a mutable reference to the boxed table. #[inline] - pub fn to_boxed(&mut self) -> &mut BoxedInstructionTable<'a, H> { - self.to_boxed_with(|i| Box::new(i)) + pub fn to_custom(&mut self) -> &mut CustomInstructionTable { + self.to_custom_with(|i| CI::from_base(i)) } /// Converts the current instruction table to a boxed variant if it is not already with `f`, /// and returns a mutable reference to the boxed table. #[inline] - pub fn to_boxed_with(&mut self, f: F) -> &mut BoxedInstructionTable<'a, H> + pub fn to_custom_with(&mut self, f: F) -> &mut CustomInstructionTable where - F: FnMut(Instruction) -> BoxedInstruction<'a, H>, + F: FnMut(Instruction) -> CI, { match self { - Self::Plain(_) => self.to_boxed_with_slow(f), - Self::Boxed(boxed) => boxed, + Self::Plain(_) => self.to_custom_with_slow(f), + Self::Custom(boxed) => boxed, } } #[cold] - fn to_boxed_with_slow(&mut self, f: F) -> &mut BoxedInstructionTable<'a, H> + fn to_custom_with_slow(&mut self, f: F) -> &mut CustomInstructionTable where - F: FnMut(Instruction) -> BoxedInstruction<'a, H>, + F: FnMut(Instruction) -> CI, { let Self::Plain(table) = self else { unreachable!() }; - *self = Self::Boxed(make_boxed_instruction_table(table, f)); - let Self::Boxed(boxed) = self else { + *self = Self::Custom(Box::new(make_custom_instruction_table(table, f))); + let Self::Custom(boxed) = self else { unreachable!() }; boxed @@ -82,62 +93,33 @@ impl<'a, H: Host + ?Sized + 'a> InstructionTables<'a, H> { /// Returns a mutable reference to the boxed instruction at the specified index. #[inline] - pub fn get_boxed(&mut self, opcode: u8) -> &mut BoxedInstruction<'a, H> { - &mut self.to_boxed()[opcode as usize] + pub fn get_custom(&mut self, opcode: u8) -> &mut CI { + &mut self.to_custom()[opcode as usize] } /// Inserts a boxed instruction into the table at the specified index. #[inline] - pub fn insert_boxed(&mut self, opcode: u8, instruction: BoxedInstruction<'a, H>) { - *self.get_boxed(opcode) = instruction; + pub fn insert_custom(&mut self, opcode: u8, instruction: CI) { + *self.get_custom(opcode) = instruction; } /// Replaces a boxed instruction into the table at the specified index, returning the previous /// instruction. #[inline] - pub fn replace_boxed( - &mut self, - opcode: u8, - instruction: BoxedInstruction<'a, H>, - ) -> BoxedInstruction<'a, H> { - core::mem::replace(self.get_boxed(opcode), instruction) - } - - /// Updates a single instruction in the table at the specified index with `f`. - #[inline] - pub fn update_boxed(&mut self, opcode: u8, f: F) - where - F: Fn(&DynInstruction<'a, H>, &mut Interpreter, &mut H) + 'a, - { - update_boxed_instruction(self.get_boxed(opcode), f) - } - - /// Updates every instruction in the table by calling `f`. - #[inline] - pub fn update_all(&mut self, f: F) - where - F: Fn(&DynInstruction<'a, H>, &mut Interpreter, &mut H) + Copy + 'a, - { - // Don't go through `to_boxed` to avoid allocating the plain table twice. - match self { - Self::Plain(_) => { - self.to_boxed_with(|prev| Box::new(move |i, h| f(&prev, i, h))); - } - Self::Boxed(boxed) => boxed - .iter_mut() - .for_each(|instruction| update_boxed_instruction(instruction, f)), - } + pub fn replace_boxed(&mut self, opcode: u8, instruction: CI) -> CI { + core::mem::replace(self.get_custom(opcode), instruction) } } /// Make instruction table. #[inline] -pub const fn make_instruction_table() -> InstructionTable { +pub const fn make_instruction_table( +) -> InstructionTable { const { - let mut tables: InstructionTable = [control::unknown; 256]; + let mut tables: InstructionTable = [control::unknown; 256]; let mut i = 0; while i < 256 { - tables[i] = instruction::(i as u8); + tables[i] = instruction::(i as u8); i += 1; } tables @@ -146,25 +128,30 @@ pub const fn make_instruction_table() -> Instructi /// Make boxed instruction table that calls `f` closure for every instruction. #[inline] -pub fn make_boxed_instruction_table<'a, H, FN>( - table: &InstructionTable, +pub fn make_custom_instruction_table>( + table: &InstructionTable, mut f: FN, -) -> BoxedInstructionTable<'a, H> +) -> CustomInstructionTable where + W: InterpreterTypes, H: Host + ?Sized, - FN: FnMut(Instruction) -> BoxedInstruction<'a, H>, + FN: FnMut(Instruction) -> CI, { core::array::from_fn(|i| f(table[i])) } -/// Updates a boxed instruction with a new one. -#[inline] -pub fn update_boxed_instruction<'a, H, F>(instruction: &mut BoxedInstruction<'a, H>, f: F) -where - H: Host + ?Sized + 'a, - F: Fn(&DynInstruction<'a, H>, &mut Interpreter, &mut H) + 'a, -{ - // NOTE: This first allocation gets elided by the compiler. - let prev = core::mem::replace(instruction, Box::new(|_, _| {})); - *instruction = Box::new(move |i, h| f(&prev, i, h)); -} +// TODO +// /// Updates a boxed instruction with a new one. +// #[inline] +// pub fn update_custom_instruction( +// instruction: &mut impl CustomInstruction, +// f: F, +// ) where +// W: InterpreterTypes, +// H: Host + ?Sized, +// F: Fn(&DynInstruction, &mut W, &mut H), +// { +// // NOTE: This first allocation gets elided by the compiler. +// let prev = core::mem::replace(instruction, Box::new(|_, _| {})); +// *instruction = Box::new(move |i, h| f(&prev, i, h)); +// } diff --git a/crates/optimism/Cargo.toml b/crates/optimism/Cargo.toml index cc188727f4..1896d73440 100644 --- a/crates/optimism/Cargo.toml +++ b/crates/optimism/Cargo.toml @@ -25,9 +25,10 @@ all = "warn" # revm revm.workspace = true precompile = { workspace = true, features = ["secp256r1"] } +inspector.workspace = true -# misc -enumn = { version = "0.1" } +# static precompile sets. +once_cell = { version = "1.19", default-features = false, features = ["alloc"] } # Optional serde = { version = "1.0", default-features = false, features = [ @@ -41,7 +42,7 @@ database.workspace = true anyhow = "1.0.89" criterion = "0.5" indicatif = "0.17" -rstest = "0.22.0" +rstest = "0.23.0" alloy-sol-types = "0.8" [features] diff --git a/crates/optimism/src/bn128.rs b/crates/optimism/src/bn128.rs index 6603e70394..70549f8427 100644 --- a/crates/optimism/src/bn128.rs +++ b/crates/optimism/src/bn128.rs @@ -1,17 +1,17 @@ use precompile::{ - bn128, {Precompile, PrecompileError, PrecompileResult, PrecompileWithAddress}, + bn128, {PrecompileError, PrecompileResult, PrecompileWithAddress}, }; -pub(crate) mod pair { +pub mod pair { use super::*; - const GRANITE_MAX_INPUT_SIZE: usize = 112687; - pub(crate) const GRANITE: PrecompileWithAddress = PrecompileWithAddress( - bn128::pair::ADDRESS, - Precompile::Standard(|input, gas_limit| run_pair(input, gas_limit)), - ); + pub const GRANITE_MAX_INPUT_SIZE: usize = 112687; + pub const GRANITE: PrecompileWithAddress = + PrecompileWithAddress(bn128::pair::ADDRESS, |input, gas_limit| { + run_pair(input, gas_limit) + }); - pub(crate) fn run_pair(input: &[u8], gas_limit: u64) -> PrecompileResult { + pub fn run_pair(input: &[u8], gas_limit: u64) -> PrecompileResult { if input.len() > GRANITE_MAX_INPUT_SIZE { return Err(PrecompileError::Bn128PairLength.into()); } diff --git a/crates/optimism/src/evm.rs b/crates/optimism/src/evm.rs new file mode 100644 index 0000000000..35a479bab1 --- /dev/null +++ b/crates/optimism/src/evm.rs @@ -0,0 +1,45 @@ +use crate::{ + handler::{ + precompiles::OpPrecompileProvider, OpExecution, OpHandler, OpPreExecution, OpValidation, + }, + L1BlockInfo, OpSpec, OpTransaction, +}; +use inspector::{InspectorContext, InspectorEthFrame}; +use revm::{ + context::{block::BlockEnv, tx::TxEnv, CfgEnv, Context}, + context_interface::result::{EVMError, InvalidTransaction}, + database_interface::Database, + Evm, +}; + +/// Optimism Error. +pub type OpError = EVMError<::Error, InvalidTransaction>; + +/// Optimism Context. +pub type OpContext = Context, CfgEnv, DB, L1BlockInfo>; + +/// Optimism EVM type. +pub type OpEvm = Evm, OpContext, OpHandler, OpError>>; + +pub type InspCtxType = + InspectorContext, DB, L1BlockInfo>; + +pub type InspectorOpEvm = Evm< + OpError, + InspCtxType, + OpHandler< + InspCtxType, + OpError, + OpValidation, OpError>, + OpPreExecution, OpError>, + OpExecution< + InspCtxType, + OpError, + InspectorEthFrame< + InspCtxType, + OpError, + OpPrecompileProvider, OpError>, + >, + >, + >, +>; diff --git a/crates/optimism/src/fast_lz.rs b/crates/optimism/src/fast_lz.rs index 847170531e..94ebef12f1 100644 --- a/crates/optimism/src/fast_lz.rs +++ b/crates/optimism/src/fast_lz.rs @@ -103,90 +103,90 @@ fn u24(input: &[u8], idx: u32) -> u32 { + (u32::from(input[(idx + 2) as usize]) << 16) } -#[cfg(test)] -mod tests { - use crate::wiring::OptimismEvmWiring; - use crate::OpTransaction; - - use super::*; - use alloy_sol_types::sol; - use alloy_sol_types::SolCall; - use database::BenchmarkDB; - use revm::{ - bytecode::Bytecode, - primitives::{address, bytes, Bytes, TxKind, U256}, - Evm, - }; - use std::vec::Vec; - - use rstest::rstest; - - #[rstest] - #[case::empty(&[], 0)] - #[case::thousand_zeros(&[0; 1000], 21)] - #[case::thousand_forty_twos(&[42; 1000], 21)] - #[case::short_hex(&bytes!("FACADE"), 4)] - #[case::sample_contract_call(&bytes!("02f901550a758302df1483be21b88304743f94f80e51afb613d764fa61751affd3313c190a86bb870151bd62fd12adb8e41ef24f3f000000000000000000000000000000000000000000000000000000000000006e000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831000000000000000000000000000000000000000000000000000000000003c1e5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000148c89ed219d02f1a5be012c689b4f5b731827bebe000000000000000000000000c001a033fd89cb37c31b2cba46b6466e040c61fc9b2a3675a7f5f493ebd5ad77c497f8a07cdf65680e238392693019b4092f610222e71b7cec06449cb922b93b6a12744e"), 202)] - #[case::base_0x5dadeb52979f29fc7a7494c43fdabc5be1d8ff404f3aafe93d729fa8e5d00769(&bytes!("b9047c02f904788221050883036ee48409c6c87383037f6f941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b90404d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000f6476f90447748c19248ccaa31e6b8bfda4eb9d830f5f47df7f0998f7c2123d9e6137761b75d3184efb0f788e3b14516000000000000000000000000000000000000000000000000000044364c5bb000000000000000000000000000f38e53bd45c8225a7c94b513beadaa7afe5d222d0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084d6574614d61736b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d656852577a743347745961776343347564745657557233454c587261436746434259416b66507331696f48610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cd0d83d9e840f8e27d5c2e365fd365ff1c05b2480000000000000000000000000000000000000000000000000000000000000ce40000000000000000000000000000000000000000000000000000000000000041e4480d358dbae20880960a0a464d63b06565a0c9f9b1b37aa94b522247b23ce149c81359bf4239d1a879eeb41047ec710c15f5c0f67453da59a383e6abd742971c00000000000000000000000000000000000000000000000000000000000000c001a0b57f0ff8516ea29cb26a44ac5055a5420847d1e16a8e7b03b70f0c02291ff2d5a00ad3771e5f39ccacfff0faa8c5d25ef7a1c179f79e66e828ffddcb994c8b512e"), 471)] - fn test_flz_compress_len(#[case] input: &[u8], #[case] expected: u32) { - assert_eq!(flz_compress_len(input), expected); - } - - #[test] - fn test_flz_compress_len_no_repeats() { - let mut input = Vec::new(); - let mut len = 0; - - for i in 0..256 { - input.push(i as u8); - let prev_len = len; - len = flz_compress_len(&input); - assert!(len > prev_len); - } - } - - #[rstest] - #[case::short_hex(bytes!("FACADE"))] - #[case::sample_contract_call(bytes!("02f901550a758302df1483be21b88304743f94f80e51afb613d764fa61751affd3313c190a86bb870151bd62fd12adb8e41ef24f3f000000000000000000000000000000000000000000000000000000000000006e000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831000000000000000000000000000000000000000000000000000000000003c1e5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000148c89ed219d02f1a5be012c689b4f5b731827bebe000000000000000000000000c001a033fd89cb37c31b2cba46b6466e040c61fc9b2a3675a7f5f493ebd5ad77c497f8a07cdf65680e238392693019b4092f610222e71b7cec06449cb922b93b6a12744e"))] - #[case::base_0x5dadeb52979f29fc7a7494c43fdabc5be1d8ff404f3aafe93d729fa8e5d00769(bytes!("b9047c02f904788221050883036ee48409c6c87383037f6f941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b90404d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000f6476f90447748c19248ccaa31e6b8bfda4eb9d830f5f47df7f0998f7c2123d9e6137761b75d3184efb0f788e3b14516000000000000000000000000000000000000000000000000000044364c5bb000000000000000000000000000f38e53bd45c8225a7c94b513beadaa7afe5d222d0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084d6574614d61736b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d656852577a743347745961776343347564745657557233454c587261436746434259416b66507331696f48610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cd0d83d9e840f8e27d5c2e365fd365ff1c05b2480000000000000000000000000000000000000000000000000000000000000ce40000000000000000000000000000000000000000000000000000000000000041e4480d358dbae20880960a0a464d63b06565a0c9f9b1b37aa94b522247b23ce149c81359bf4239d1a879eeb41047ec710c15f5c0f67453da59a383e6abd742971c00000000000000000000000000000000000000000000000000000000000000c001a0b57f0ff8516ea29cb26a44ac5055a5420847d1e16a8e7b03b70f0c02291ff2d5a00ad3771e5f39ccacfff0faa8c5d25ef7a1c179f79e66e828ffddcb994c8b512e"))] - #[case::base_0xfaada76a2dac09fc17f5a28d066aaabefc6d82ef6589b211ed8c9f766b070721(bytes!("b87602f873822105528304320f8409cfe5c98252089480c67432656d59144ceff962e8faf8926599bcf888011dfe52d06b633f80c001a08632f069f837aea7a28bab0affee14dda116956bd5a850a355c045d25afedd17a0084b8f273efffe17ece527116053e5781a4915ff89ab9c379f1e62c25b697687"))] - #[case::base_0x112864e9b971af6a1dac840018833c5a5a659acc187cfdaba919ad1da013678d(bytes!("b8b302f8b0822105308304320f8409cfe5c9827496944ed4e862860bed51a9570b96d89af5e1b0efefed80b844095ea7b3000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba300000000000000000000000000000000000000000000015e10fb0973595fffffc001a02020e39f07917c1a852feb131c857e12478c7e88a20772b91a8bf5cee38c5aeea06055981727f9aaa3471c1af800555b35a77916c154be3f9d02ad1a63029455ab"))] - #[case::base_0x6905051352691641888d0c427fb137c5b95afb5870d5169ff014eff1d0952195(bytes!("b87202f86f8221058303dc6c8310db1f84068fa8d7838954409436af2ff952a7355c8045fcd5e88bc9f6c8257f7b8080c001a0b89e7ff3d7694109e73e7f4244e032581670313c36e48e485c9c94b853bd81d2a038ffaf8f10859ce21d1f7f7046c3d08027fb8aa15b69038f6102be97aaa1179a"))] - #[case::base_0x6a38e9a26d7202a2268de69d2d47531c1a9829867579a483fb48d78e9e0b080d(bytes!("b9049b02f904978221058201618506fc23ac008506fc23ac008306ddd0943fc91a3afd70395cd496c647d5a6cc9d4b2b7fad80b904243593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000006641d67b00000000000000000000000000000000000000000000000000000000000000030a000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000160000000000000000000000000088487bd8c3222d64d1d0b3fa7098dcf9d94d79e000000000000000000000000ffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000006669635d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fc91a3afd70395cd496c647d5a6cc9d4b2b7fad000000000000000000000000000000000000000000000000000000006641d78900000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000418661369ca026f92ff88347bd0e3625a7b5ed65071b366368c68ad7c55aed136c18659b34f9246e30a784227a53dd374fbd3d2124696808c678cd987c4e954a681b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000549e5c020c764dbfffff00000000000000000000000000000000000000000000000002e5a629c093a2b600000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002b088487bd8c3222d64d1d0b3fa7098dcf9d94d79e0027104200000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000c001a014a3acef764ff6d3bb9bd81e420bfa94171a5734ab997dfbc9b41b653ce018a4a01ff5fccb01ef5c60ba3aef67d4e74f3f47312dd78bfbbff9e5090fbf2d3d62bb"))] - fn test_flz_native_evm_parity(#[case] input: Bytes) { - // This bytecode and ABI is for a contract, which wraps the LibZip library for easier fuzz testing. - // The source of this contract is here: https://github.com/danyalprout/fastlz/blob/main/src/FastLz.sol#L6-L10 - sol! { - interface FastLz { - function fastLz(bytes input) external view returns (uint256); - } - } - - let contract_bytecode = Bytecode::new_raw(bytes!("608060405234801561001057600080fd5b506004361061002b5760003560e01c8063920a769114610030575b600080fd5b61004361003e366004610374565b610055565b60405190815260200160405180910390f35b600061006082610067565b5192915050565b60606101e0565b818153600101919050565b600082840393505b838110156100a25782810151828201511860001a1590930292600101610081565b9392505050565b825b602082106100d75782516100c0601f8361006e565b5260209290920191601f19909101906021016100ab565b81156100a25782516100ec600184038361006e565b520160010192915050565b60006001830392505b61010782106101385761012a8360ff1661012560fd6101258760081c60e0018961006e565b61006e565b935061010682039150610100565b600782106101655761015e8360ff16610125600785036101258760081c60e0018961006e565b90506100a2565b61017e8360ff166101258560081c8560051b018761006e565b949350505050565b80516101d890838303906101bc90600081901a600182901a60081b1760029190911a60101b17639e3779b90260131c611fff1690565b8060021b6040510182815160e01c1860e01b8151188152505050565b600101919050565b5060405161800038823961800081016020830180600d8551820103826002015b81811015610313576000805b50508051604051600082901a600183901a60081b1760029290921a60101b91909117639e3779b9810260111c617ffc16909101805160e081811c878603811890911b9091189091528401908183039084841061026857506102a3565b600184019350611fff821161029d578251600081901a600182901a60081b1760029190911a60101b17810361029d57506102a3565b5061020c565b8383106102b1575050610313565b600183039250858311156102cf576102cc87878886036100a9565b96505b6102e3600985016003850160038501610079565b91506102f08782846100f7565b9650506103088461030386848601610186565b610186565b915050809350610200565b5050617fe061032884848589518601036100a9565b03925050506020820180820383525b81811161034e57617fe08101518152602001610337565b5060008152602001604052919050565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561038657600080fd5b813567ffffffffffffffff8082111561039e57600080fd5b818401915084601f8301126103b257600080fd5b8135818111156103c4576103c461035e565b604051601f8201601f19908116603f011681019083821181831017156103ec576103ec61035e565b8160405282815287602084870101111561040557600080fd5b82602086016020830137600092810160200192909252509594505050505056fea264697066735822122000646b2953fc4a6f501bd0456ac52203089443937719e16b3190b7979c39511264736f6c63430008190033")); - - let native_val = flz_compress_len(&input); - - let mut evm = Evm::>::builder() - .with_db(BenchmarkDB::new_bytecode(contract_bytecode.clone())) - .with_default_ext_ctx() - .modify_tx_env(|tx| { - let OpTransaction::Base { tx, enveloped_tx } = tx else { - panic!("Default is base tx"); - }; - tx.caller = address!("1000000000000000000000000000000000000000"); - tx.transact_to = TxKind::Call(address!("0000000000000000000000000000000000000000")); - tx.data = FastLz::fastLzCall::new((input,)).abi_encode().into(); - tx.gas_limit = 300_000; - *enveloped_tx = Some(Bytes::default()); - }) - .build(); - - let result_and_state = evm.transact().unwrap(); - let output = result_and_state.result.output().unwrap(); - let evm_val = FastLz::fastLzCall::abi_decode_returns(output, true) - .unwrap() - ._0; - - assert_eq!(U256::from(native_val), evm_val); - } -} +// #[cfg(test)] +// mod tests { +// use crate::wiring::OptimismEvmWiring; +// use crate::OpTransaction; + +// use super::*; +// use alloy_sol_types::sol; +// use alloy_sol_types::SolCall; +// use database::BenchmarkDB; +// use revm::{ +// bytecode::Bytecode, +// primitives::{address, bytes, Bytes, TxKind, U256}, +// Evm, +// }; +// use std::vec::Vec; + +// use rstest::rstest; + +// #[rstest] +// #[case::empty(&[], 0)] +// #[case::thousand_zeros(&[0; 1000], 21)] +// #[case::thousand_forty_twos(&[42; 1000], 21)] +// #[case::short_hex(&bytes!("FACADE"), 4)] +// #[case::sample_contract_call(&bytes!("02f901550a758302df1483be21b88304743f94f80e51afb613d764fa61751affd3313c190a86bb870151bd62fd12adb8e41ef24f3f000000000000000000000000000000000000000000000000000000000000006e000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831000000000000000000000000000000000000000000000000000000000003c1e5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000148c89ed219d02f1a5be012c689b4f5b731827bebe000000000000000000000000c001a033fd89cb37c31b2cba46b6466e040c61fc9b2a3675a7f5f493ebd5ad77c497f8a07cdf65680e238392693019b4092f610222e71b7cec06449cb922b93b6a12744e"), 202)] +// #[case::base_0x5dadeb52979f29fc7a7494c43fdabc5be1d8ff404f3aafe93d729fa8e5d00769(&bytes!("b9047c02f904788221050883036ee48409c6c87383037f6f941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b90404d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000f6476f90447748c19248ccaa31e6b8bfda4eb9d830f5f47df7f0998f7c2123d9e6137761b75d3184efb0f788e3b14516000000000000000000000000000000000000000000000000000044364c5bb000000000000000000000000000f38e53bd45c8225a7c94b513beadaa7afe5d222d0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084d6574614d61736b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d656852577a743347745961776343347564745657557233454c587261436746434259416b66507331696f48610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cd0d83d9e840f8e27d5c2e365fd365ff1c05b2480000000000000000000000000000000000000000000000000000000000000ce40000000000000000000000000000000000000000000000000000000000000041e4480d358dbae20880960a0a464d63b06565a0c9f9b1b37aa94b522247b23ce149c81359bf4239d1a879eeb41047ec710c15f5c0f67453da59a383e6abd742971c00000000000000000000000000000000000000000000000000000000000000c001a0b57f0ff8516ea29cb26a44ac5055a5420847d1e16a8e7b03b70f0c02291ff2d5a00ad3771e5f39ccacfff0faa8c5d25ef7a1c179f79e66e828ffddcb994c8b512e"), 471)] +// fn test_flz_compress_len(#[case] input: &[u8], #[case] expected: u32) { +// assert_eq!(flz_compress_len(input), expected); +// } + +// #[test] +// fn test_flz_compress_len_no_repeats() { +// let mut input = Vec::new(); +// let mut len = 0; + +// for i in 0..256 { +// input.push(i as u8); +// let prev_len = len; +// len = flz_compress_len(&input); +// assert!(len > prev_len); +// } +// } + +// #[rstest] +// #[case::short_hex(bytes!("FACADE"))] +// #[case::sample_contract_call(bytes!("02f901550a758302df1483be21b88304743f94f80e51afb613d764fa61751affd3313c190a86bb870151bd62fd12adb8e41ef24f3f000000000000000000000000000000000000000000000000000000000000006e000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831000000000000000000000000000000000000000000000000000000000003c1e5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000148c89ed219d02f1a5be012c689b4f5b731827bebe000000000000000000000000c001a033fd89cb37c31b2cba46b6466e040c61fc9b2a3675a7f5f493ebd5ad77c497f8a07cdf65680e238392693019b4092f610222e71b7cec06449cb922b93b6a12744e"))] +// #[case::base_0x5dadeb52979f29fc7a7494c43fdabc5be1d8ff404f3aafe93d729fa8e5d00769(bytes!("b9047c02f904788221050883036ee48409c6c87383037f6f941195cf65f83b3a5768f3c496d3a05ad6412c64b78644364c5bb000b90404d123b4d80000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000038000000000000000000000000000000000f6476f90447748c19248ccaa31e6b8bfda4eb9d830f5f47df7f0998f7c2123d9e6137761b75d3184efb0f788e3b14516000000000000000000000000000000000000000000000000000044364c5bb000000000000000000000000000f38e53bd45c8225a7c94b513beadaa7afe5d222d0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000084d6574614d61736b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035697066733a2f2f516d656852577a743347745961776343347564745657557233454c587261436746434259416b66507331696f48610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000cd0d83d9e840f8e27d5c2e365fd365ff1c05b2480000000000000000000000000000000000000000000000000000000000000ce40000000000000000000000000000000000000000000000000000000000000041e4480d358dbae20880960a0a464d63b06565a0c9f9b1b37aa94b522247b23ce149c81359bf4239d1a879eeb41047ec710c15f5c0f67453da59a383e6abd742971c00000000000000000000000000000000000000000000000000000000000000c001a0b57f0ff8516ea29cb26a44ac5055a5420847d1e16a8e7b03b70f0c02291ff2d5a00ad3771e5f39ccacfff0faa8c5d25ef7a1c179f79e66e828ffddcb994c8b512e"))] +// #[case::base_0xfaada76a2dac09fc17f5a28d066aaabefc6d82ef6589b211ed8c9f766b070721(bytes!("b87602f873822105528304320f8409cfe5c98252089480c67432656d59144ceff962e8faf8926599bcf888011dfe52d06b633f80c001a08632f069f837aea7a28bab0affee14dda116956bd5a850a355c045d25afedd17a0084b8f273efffe17ece527116053e5781a4915ff89ab9c379f1e62c25b697687"))] +// #[case::base_0x112864e9b971af6a1dac840018833c5a5a659acc187cfdaba919ad1da013678d(bytes!("b8b302f8b0822105308304320f8409cfe5c9827496944ed4e862860bed51a9570b96d89af5e1b0efefed80b844095ea7b3000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba300000000000000000000000000000000000000000000015e10fb0973595fffffc001a02020e39f07917c1a852feb131c857e12478c7e88a20772b91a8bf5cee38c5aeea06055981727f9aaa3471c1af800555b35a77916c154be3f9d02ad1a63029455ab"))] +// #[case::base_0x6905051352691641888d0c427fb137c5b95afb5870d5169ff014eff1d0952195(bytes!("b87202f86f8221058303dc6c8310db1f84068fa8d7838954409436af2ff952a7355c8045fcd5e88bc9f6c8257f7b8080c001a0b89e7ff3d7694109e73e7f4244e032581670313c36e48e485c9c94b853bd81d2a038ffaf8f10859ce21d1f7f7046c3d08027fb8aa15b69038f6102be97aaa1179a"))] +// #[case::base_0x6a38e9a26d7202a2268de69d2d47531c1a9829867579a483fb48d78e9e0b080d(bytes!("b9049b02f904978221058201618506fc23ac008506fc23ac008306ddd0943fc91a3afd70395cd496c647d5a6cc9d4b2b7fad80b904243593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000006641d67b00000000000000000000000000000000000000000000000000000000000000030a000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000160000000000000000000000000088487bd8c3222d64d1d0b3fa7098dcf9d94d79e000000000000000000000000ffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000006669635d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003fc91a3afd70395cd496c647d5a6cc9d4b2b7fad000000000000000000000000000000000000000000000000000000006641d78900000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000418661369ca026f92ff88347bd0e3625a7b5ed65071b366368c68ad7c55aed136c18659b34f9246e30a784227a53dd374fbd3d2124696808c678cd987c4e954a681b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000549e5c020c764dbfffff00000000000000000000000000000000000000000000000002e5a629c093a2b600000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002b088487bd8c3222d64d1d0b3fa7098dcf9d94d79e0027104200000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000c001a014a3acef764ff6d3bb9bd81e420bfa94171a5734ab997dfbc9b41b653ce018a4a01ff5fccb01ef5c60ba3aef67d4e74f3f47312dd78bfbbff9e5090fbf2d3d62bb"))] +// fn test_flz_native_evm_parity(#[case] input: Bytes) { +// // This bytecode and ABI is for a contract, which wraps the LibZip library for easier fuzz testing. +// // The source of this contract is here: https://github.com/danyalprout/fastlz/blob/main/src/FastLz.sol#L6-L10 +// sol! { +// interface FastLz { +// function fastLz(bytes input) external view returns (uint256); +// } +// } + +// let contract_bytecode = Bytecode::new_raw(bytes!("608060405234801561001057600080fd5b506004361061002b5760003560e01c8063920a769114610030575b600080fd5b61004361003e366004610374565b610055565b60405190815260200160405180910390f35b600061006082610067565b5192915050565b60606101e0565b818153600101919050565b600082840393505b838110156100a25782810151828201511860001a1590930292600101610081565b9392505050565b825b602082106100d75782516100c0601f8361006e565b5260209290920191601f19909101906021016100ab565b81156100a25782516100ec600184038361006e565b520160010192915050565b60006001830392505b61010782106101385761012a8360ff1661012560fd6101258760081c60e0018961006e565b61006e565b935061010682039150610100565b600782106101655761015e8360ff16610125600785036101258760081c60e0018961006e565b90506100a2565b61017e8360ff166101258560081c8560051b018761006e565b949350505050565b80516101d890838303906101bc90600081901a600182901a60081b1760029190911a60101b17639e3779b90260131c611fff1690565b8060021b6040510182815160e01c1860e01b8151188152505050565b600101919050565b5060405161800038823961800081016020830180600d8551820103826002015b81811015610313576000805b50508051604051600082901a600183901a60081b1760029290921a60101b91909117639e3779b9810260111c617ffc16909101805160e081811c878603811890911b9091189091528401908183039084841061026857506102a3565b600184019350611fff821161029d578251600081901a600182901a60081b1760029190911a60101b17810361029d57506102a3565b5061020c565b8383106102b1575050610313565b600183039250858311156102cf576102cc87878886036100a9565b96505b6102e3600985016003850160038501610079565b91506102f08782846100f7565b9650506103088461030386848601610186565b610186565b915050809350610200565b5050617fe061032884848589518601036100a9565b03925050506020820180820383525b81811161034e57617fe08101518152602001610337565b5060008152602001604052919050565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561038657600080fd5b813567ffffffffffffffff8082111561039e57600080fd5b818401915084601f8301126103b257600080fd5b8135818111156103c4576103c461035e565b604051601f8201601f19908116603f011681019083821181831017156103ec576103ec61035e565b8160405282815287602084870101111561040557600080fd5b82602086016020830137600092810160200192909252509594505050505056fea264697066735822122000646b2953fc4a6f501bd0456ac52203089443937719e16b3190b7979c39511264736f6c63430008190033")); + +// let native_val = flz_compress_len(&input); + +// let mut evm = Evm::>::builder() +// .with_db(BenchmarkDB::new_bytecode(contract_bytecode.clone())) +// .with_default_ext_context() +// .modify_tx_env(|tx| { +// let OpTransaction::Base { tx, enveloped_tx } = tx else { +// panic!("Default is base tx"); +// }; +// tx.caller = address!("1000000000000000000000000000000000000000"); +// tx.transact_to = TxKind::Call(address!("0000000000000000000000000000000000000000")); +// tx.data = FastLz::fastLzCall::new((input,)).abi_encode().into(); +// tx.gas_limit = 300_000; +// *enveloped_tx = Some(Bytes::default()); +// }) +// .build(); + +// let result_and_state = evm.transact().unwrap(); +// let output = result_and_state.result.output().unwrap(); +// let evm_val = FastLz::fastLzCall::abi_decode_returns(output, true) +// .unwrap() +// ._0; + +// assert_eq!(U256::from(native_val), evm_val); +// } +// } diff --git a/crates/optimism/src/handler.rs b/crates/optimism/src/handler.rs new file mode 100644 index 0000000000..93ce21b888 --- /dev/null +++ b/crates/optimism/src/handler.rs @@ -0,0 +1,811 @@ +//! Handler related to Optimism chain + +pub mod precompiles; + +use crate::{ + transaction::{ + abstraction::OpTxGetter, deposit::DepositTransaction, OpTransactionType, OpTxTrait, + }, + L1BlockInfoGetter, OpSpec, OpSpecId, OpTransactionError, OptimismHaltReason, + BASE_FEE_RECIPIENT, L1_FEE_RECIPIENT, +}; +use core::ops::Mul; +use precompiles::OpPrecompileProvider; +use revm::{ + context_interface::{ + result::{ExecutionResult, FromStringError, InvalidTransaction, ResultAndState}, + transaction::CommonTxFields, + Block, Cfg, CfgGetter, DatabaseGetter, JournaledState, Transaction, TransactionGetter, + }, + handler::{ + EthExecution, EthExecutionContext, EthExecutionError, EthFrame, EthFrameContext, + EthFrameError, EthHandler, EthPostExecution, EthPostExecutionContext, + EthPostExecutionError, EthPreExecution, EthPreExecutionContext, EthPreExecutionError, + EthValidation, EthValidationContext, EthValidationError, FrameResult, + }, + handler_interface::{ + util::FrameOrFrameResult, ExecutionHandler, Frame, PostExecutionHandler, + PreExecutionHandler, ValidationHandler, + }, + interpreter::{ + interpreter::{EthInstructionProvider, EthInterpreter}, + FrameInput, Gas, + }, + primitives::{hash_map::HashMap, U256}, + specification::hardfork::SpecId, + state::Account, + Database, +}; + +pub type OpHandler< + CTX, + ERROR, + VAL = OpValidation, + PREEXEC = OpPreExecution, + EXEC = OpExecution, + POSTEXEC = OpPostExecution, +> = EthHandler; + +pub struct OpValidation { + pub eth: EthValidation, +} + +impl ValidationHandler for OpValidation +where + CTX: EthValidationContext + OpTxGetter, + // Have Cfg with OpSpec + ::Cfg: Cfg, + // Have transaction with OpTransactionType + ::Transaction: Transaction, + // Add additional error type. + ERROR: EthValidationError + From, +{ + type Context = CTX; + type Error = ERROR; + + /// Validate env. + fn validate_env(&self, context: &Self::Context) -> Result<(), Self::Error> { + // Do not perform any extra validation for deposit transactions, they are pre-verified on L1. + let tx_type = context.tx().tx_type(); + if tx_type == OpTransactionType::Deposit { + let tx = context.op_tx().deposit(); + // Do not allow for a system transaction to be processed if Regolith is enabled. + // TODO check if this is correct. + if tx.is_system_transaction() && context.cfg().spec().is_enabled_in(OpSpecId::REGOLITH) + { + return Err(OpTransactionError::DepositSystemTxPostRegolith.into()); + } + return Ok(()); + } + self.eth.validate_env(context) + } + + /// Validate transactions against state. + fn validate_tx_against_state(&self, context: &mut Self::Context) -> Result<(), Self::Error> { + if context.tx().tx_type() == OpTransactionType::Deposit { + return Ok(()); + } + self.eth.validate_tx_against_state(context) + } + + /// Validate initial gas. + fn validate_initial_tx_gas(&self, context: &Self::Context) -> Result { + self.eth.validate_initial_tx_gas(context) + } +} + +pub struct OpPreExecution { + pub eth: EthPreExecution, +} + +impl PreExecutionHandler for OpPreExecution +where + CTX: EthPreExecutionContext + DatabaseGetter + OpTxGetter + L1BlockInfoGetter, + ::Cfg: Cfg, + ::Transaction: Transaction, + ERROR: EthPreExecutionError + From<<::Database as Database>::Error>, +{ + type Context = CTX; + type Error = ERROR; + + fn load_accounts(&self, context: &mut Self::Context) -> Result<(), Self::Error> { + // the L1-cost fee is only computed for Optimism non-deposit transactions. + let spec = context.cfg().spec(); + if context.tx().tx_type() != OpTransactionType::Deposit { + let l1_block_info: crate::L1BlockInfo = + super::L1BlockInfo::try_fetch(context.db(), spec)?; + + // storage l1 block info for later use. + *context.l1_block_info_mut() = l1_block_info; + } + + self.eth.load_accounts(context) + } + + fn apply_eip7702_auth_list(&self, context: &mut Self::Context) -> Result { + self.eth.apply_eip7702_auth_list(context) + } + + fn deduct_caller(&self, context: &mut Self::Context) -> Result<(), Self::Error> { + let caller = context.tx().common_fields().caller(); + let is_deposit = context.tx().tx_type() == OpTransactionType::Deposit; + + // 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 tx_l1_cost = U256::ZERO; + if is_deposit { + let tx = context.op_tx().deposit(); + if let Some(mint) = tx.mint() { + let mut caller_account = context.journal().load_account(caller)?; + caller_account.info.balance += U256::from(mint); + } + } else { + let enveloped_tx = context + .op_tx() + .enveloped_tx() + .expect("all not deposit tx have enveloped tx") + .clone(); + tx_l1_cost = context + .l1_block_info() + .calculate_tx_l1_cost(&enveloped_tx, context.cfg().spec()); + } + + // We deduct caller max balance after minting and before deducing the + // l1 cost, max values is already checked in pre_validate but l1 cost wasn't. + self.eth.deduct_caller(context)?; + + // 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. + if !is_deposit { + let mut caller_account = context.journal().load_account(caller)?; + + if tx_l1_cost > caller_account.info.balance { + return Err(InvalidTransaction::LackOfFundForMaxFee { + fee: tx_l1_cost.into(), + balance: caller_account.info.balance.into(), + } + .into()); + } + caller_account.info.balance = caller_account.info.balance.saturating_sub(tx_l1_cost); + } + Ok(()) + } +} + +pub struct OpExecution< + CTX, + ERROR, + FRAME = EthFrame< + CTX, + ERROR, + EthInterpreter<()>, + OpPrecompileProvider, + EthInstructionProvider, CTX>, + >, +> { + pub eth: EthExecution, +} + +impl ExecutionHandler for OpExecution +where + CTX: EthExecutionContext + EthFrameContext + OpTxGetter, + ERROR: EthExecutionError + EthFrameError, + ::Cfg: Cfg, + ::Transaction: Transaction, + FRAME: Frame, +{ + type Context = CTX; + type Error = ERROR; + type Frame = FRAME; + type ExecResult = FrameResult; + + fn init_first_frame( + &mut self, + context: &mut Self::Context, + gas_limit: u64, + ) -> Result, Self::Error> { + self.eth.init_first_frame(context, gas_limit) + } + + fn last_frame_result( + &self, + context: &mut Self::Context, + mut frame_result: ::FrameResult, + ) -> Result { + let tx = context.tx(); + let is_deposit = tx.tx_type() == OpTransactionType::Deposit; + let tx_gas_limit = tx.common_fields().gas_limit(); + let is_regolith = context.cfg().spec().is_enabled_in(OpSpecId::REGOLITH); + + let instruction_result = frame_result.interpreter_result().result; + let gas = frame_result.gas_mut(); + let remaining = gas.remaining(); + let refunded = gas.refunded(); + + // Spend the gas limit. Gas is reimbursed when the tx returns successfully. + *gas = Gas::new_spent(tx_gas_limit); + + if instruction_result.is_ok() { + // On Optimism, deposit transactions report gas usage uniquely to other + // transactions due to them being pre-paid on L1. + // + // Hardfork Behavior: + // - Bedrock (success path): + // - Deposit transactions (non-system) report their gas limit as the usage. + // No refunds. + // - Deposit transactions (system) report 0 gas used. No refunds. + // - Regular transactions report gas usage as normal. + // - Regolith (success path): + // - Deposit transactions (all) report their gas used as normal. Refunds + // enabled. + // - Regular transactions report their gas used as normal. + if !is_deposit || is_regolith { + // For regular transactions prior to Regolith and all transactions after + // Regolith, gas is reported as normal. + gas.erase_cost(remaining); + gas.record_refund(refunded); + } else if is_deposit { + let tx = context.op_tx().deposit(); + if tx.is_system_transaction() { + // System transactions were a special type of deposit transaction in + // the Bedrock hardfork that did not incur any gas costs. + gas.erase_cost(tx_gas_limit); + } + } + } else if instruction_result.is_revert() { + // On Optimism, deposit transactions report gas usage uniquely to other + // transactions due to them being pre-paid on L1. + // + // Hardfork Behavior: + // - Bedrock (revert path): + // - Deposit transactions (all) report the gas limit as the amount of gas + // used on failure. No refunds. + // - Regular transactions receive a refund on remaining gas as normal. + // - Regolith (revert path): + // - Deposit transactions (all) report the actual gas used as the amount of + // gas used on failure. Refunds on remaining gas enabled. + // - Regular transactions receive a refund on remaining gas as normal. + if !is_deposit || is_regolith { + gas.erase_cost(remaining); + } + } + Ok(frame_result) + } +} + +pub struct OpPostExecution { + pub eth: EthPostExecution, +} + +pub trait IsTxError { + fn is_tx_error(&self) -> bool; +} + +impl PostExecutionHandler for OpPostExecution +where + CTX: EthPostExecutionContext + OpTxGetter + L1BlockInfoGetter + DatabaseGetter, + ERROR: EthPostExecutionError + + EthFrameError + + From + + FromStringError + + IsTxError, + ::Cfg: Cfg, + ::Transaction: Transaction, +{ + type Context = CTX; + type Error = ERROR; + type ExecResult = FrameResult; + type Output = ResultAndState; + + fn refund( + &self, + context: &mut Self::Context, + exec_result: &mut Self::ExecResult, + eip7702_refund: i64, + ) { + exec_result.gas_mut().record_refund(eip7702_refund); + + let is_deposit = context.tx().tx_type() == OpTransactionType::Deposit; + let is_regolith = context.cfg().spec().is_enabled_in(OpSpecId::REGOLITH); + + // Prior to Regolith, deposit transactions did not receive gas refunds. + let is_gas_refund_disabled = is_deposit && !is_regolith; + if !is_gas_refund_disabled { + exec_result + .gas_mut() + .set_final_refund(context.cfg().spec().is_enabled_in(SpecId::LONDON)); + } + } + + fn reimburse_caller( + &self, + context: &mut Self::Context, + exec_result: &mut Self::ExecResult, + ) -> Result<(), Self::Error> { + self.eth.reimburse_caller(context, exec_result) + } + + fn reward_beneficiary( + &self, + context: &mut Self::Context, + exec_result: &mut Self::ExecResult, + ) -> Result<(), Self::Error> { + self.eth.reward_beneficiary(context, exec_result)?; + + let is_deposit = context.tx().tx_type() == OpTransactionType::Deposit; + + // transfer fee to coinbase/beneficiary. + if !is_deposit { + self.eth.reward_beneficiary(context, exec_result)?; + let basefee = *context.block().basefee(); + + // If the transaction is not a deposit transaction, fees are paid out + // to both the Base Fee Vault as well as the L1 Fee Vault. + let l1_block_info = context.l1_block_info(); + + let Some(enveloped_tx) = &context.op_tx().enveloped_tx() else { + return Err(ERROR::from_string( + "[OPTIMISM] Failed to load enveloped transaction.".into(), + )); + }; + + let l1_cost = l1_block_info.calculate_tx_l1_cost(enveloped_tx, context.cfg().spec()); + + // Send the L1 cost of the transaction to the L1 Fee Vault. + let mut l1_fee_vault_account = context.journal().load_account(L1_FEE_RECIPIENT)?; + l1_fee_vault_account.mark_touch(); + l1_fee_vault_account.info.balance += l1_cost; + + // Send the base fee of the transaction to the Base Fee Vault. + let mut base_fee_vault_account = context.journal().load_account(BASE_FEE_RECIPIENT)?; + base_fee_vault_account.mark_touch(); + base_fee_vault_account.info.balance += basefee.mul(U256::from( + exec_result.gas().spent() - exec_result.gas().refunded() as u64, + )); + } + Ok(()) + } + + fn output( + &self, + context: &mut Self::Context, + result: Self::ExecResult, + ) -> Result { + let result = self.eth.output(context, result)?; + if result.result.is_halt() { + // Post-regolith, if the transaction is a deposit transaction and it halts, + // we bubble up to the global return handler. The mint value will be persisted + // and the caller nonce will be incremented there. + let is_deposit = context.tx().tx_type() == OpTransactionType::Deposit; + if is_deposit && context.cfg().spec().is_enabled_in(OpSpecId::REGOLITH) { + return Err(ERROR::from(OpTransactionError::HaltedDepositPostRegolith)); + } + } + Ok(result) + } + + fn clear(&self, context: &mut Self::Context) { + self.eth.clear(context); + } + + fn end( + &self, + context: &mut Self::Context, + end_output: Result, + ) -> Result { + //end_output + + let is_deposit = context.tx().tx_type() == OpTransactionType::Deposit; + end_output.or_else(|err| { + if err.is_tx_error() && is_deposit { + let spec = context.cfg().spec(); + let tx = context.op_tx().deposit(); + let caller = tx.caller(); + let mint = tx.mint(); + let is_system_tx = tx.is_system_transaction(); + let gas_limit = tx.gas_limit(); + // If the transaction is a deposit transaction and it failed + // for any reason, the caller nonce must be bumped, and the + // gas reported must be altered depending on the Hardfork. This is + // also returned as a special Halt variant so that consumers can more + // easily distinguish between a failed deposit and a failed + // normal transaction. + + // Increment sender nonce and account balance for the mint amount. Deposits + // always persist the mint amount, even if the transaction fails. + let account = { + let mut acc = Account::from( + context + .db() + .basic(caller) + .unwrap_or_default() + .unwrap_or_default(), + ); + 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(); + acc + }; + let state = HashMap::from_iter([(caller, account)]); + + // The gas used of a failed deposit post-regolith is the gas + // limit of the transaction. pre-regolith, it is the gas limit + // of the transaction for non system transactions and 0 for system + // transactions. + let gas_used = if spec.is_enabled_in(OpSpecId::REGOLITH) || !is_system_tx { + gas_limit + } else { + 0 + }; + + Ok(ResultAndState { + result: ExecutionResult::Halt { + reason: OptimismHaltReason::FailedDeposit, + gas_used, + }, + state, + }) + } else { + Err(err) + } + }) + } +} + +// /// Optimism end handle changes output if the transaction is a deposit transaction. +// /// Deposit transaction can't be reverted and is always successful. +// #[inline] +// pub fn end( +// context: &mut Context, +// evm_output: EVMResult, +// ) -> EVMResult { + +// } + +// #[cfg(test)] +// mod tests { +// use super::*; +// use crate::{ +// context_interface::OptimismEvmWiring, transaction::deposit::TxDeposit, BedrockSpec, +// L1BlockInfo, LatestSpec, OpTransaction, RegolithSpec, +// }; +// use database::InMemoryDB; +// use revm::{ +// context_interface::default::{block::BlockEnv, Env, TxEnv}, +// database_interface::EmptyDB, +// interpreter::{CallOutcome, InstructionResult, InterpreterResult}, +// primitives::{bytes, Address, Bytes, B256}, +// state::AccountInfo, +// }; +// use std::boxed::Box; + +// type TestEmptyOpWiring = OptimismEvmWiring; +// type TestMemOpWiring = OptimismEvmWiring; + +// /// Creates frame result. +// fn call_last_frame_return( +// env: EnvWiring, +// instruction_result: InstructionResult, +// gas: Gas, +// ) -> Gas +// where +// SPEC: OptimismSpec, +// { +// let mut context = Context::::new_with_db(EmptyDB::default()); +// context.evm.inner.env = Box::new(env); +// let mut first_frame = FrameResult::Call(CallOutcome::new( +// InterpreterResult { +// result: instruction_result, +// output: Bytes::new(), +// gas, +// }, +// 0..0, +// )); +// last_frame_return::(&mut context, &mut first_frame).unwrap(); +// refund::(&mut context, first_frame.gas_mut(), 0); +// *first_frame.gas() +// } + +// #[test] +// fn test_revert_gas() { +// let mut env = Envcontext_interface::::default(); +// let tx = TxEnv { +// gas_limit: 100, +// ..Default::default() +// }; +// env.tx = OpTransaction::Base { +// tx, +// enveloped_tx: None, +// }; + +// let gas = +// call_last_frame_return::(env, InstructionResult::Revert, Gas::new(90)); +// assert_eq!(gas.remaining(), 90); +// assert_eq!(gas.spent(), 10); +// assert_eq!(gas.refunded(), 0); +// } + +// #[test] +// fn test_consume_gas() { +// let mut env = Envcontext_interface::::default(); +// //env.tx.base.gas_limit = 100; +// //env.tx.source_hash = Some(B256::ZERO); + +// let deposit = TxDeposit { +// gas_limit: 100, +// source_hash: B256::ZERO, +// ..Default::default() +// }; +// env.tx = OpTransaction::Deposit(deposit); + +// let gas = +// call_last_frame_return::(env, InstructionResult::Stop, Gas::new(90)); +// assert_eq!(gas.remaining(), 90); +// assert_eq!(gas.spent(), 10); +// assert_eq!(gas.refunded(), 0); +// } + +// #[test] +// fn test_consume_gas_with_refund() { +// let mut env = Envcontext_interface::::default(); +// //env.tx.base.gas_limit = 100; +// //env.tx.source_hash = Some(B256::ZERO); +// let deposit = TxDeposit { +// gas_limit: 100, +// source_hash: B256::ZERO, +// ..Default::default() +// }; +// env.tx = OpTransaction::Deposit(deposit); + +// let mut ret_gas = Gas::new(90); +// ret_gas.record_refund(20); + +// let gas = +// call_last_frame_return::(env.clone(), InstructionResult::Stop, ret_gas); +// assert_eq!(gas.remaining(), 90); +// assert_eq!(gas.spent(), 10); +// assert_eq!(gas.refunded(), 2); // min(20, 10/5) + +// let gas = call_last_frame_return::(env, InstructionResult::Revert, ret_gas); +// assert_eq!(gas.remaining(), 90); +// assert_eq!(gas.spent(), 10); +// assert_eq!(gas.refunded(), 0); +// } + +// #[test] +// fn test_consume_gas_sys_deposit_tx() { +// let mut env = Envcontext_interface::::default(); +// //env.tx.base.gas_limit = 100; +// //env.tx.source_hash = Some(B256::ZERO); + +// let deposit = TxDeposit { +// gas_limit: 100, +// source_hash: B256::ZERO, +// ..Default::default() +// }; +// env.tx = OpTransaction::Deposit(deposit); + +// let gas = call_last_frame_return::(env, InstructionResult::Stop, Gas::new(90)); +// assert_eq!(gas.remaining(), 0); +// assert_eq!(gas.spent(), 100); +// assert_eq!(gas.refunded(), 0); +// } + +// #[test] +// fn test_commit_mint_value() { +// let caller = Address::ZERO; +// let mut db = InMemoryDB::default(); +// db.insert_account_info( +// caller, +// AccountInfo { +// balance: U256::from(1000), +// ..Default::default() +// }, +// ); + +// let mut context = Context::::new_with_db(db); +// *context.evm.chain.l1_block_info_mut() = Some(L1BlockInfo { +// l1_base_fee: U256::from(1_000), +// l1_fee_overhead: Some(U256::from(1_000)), +// l1_base_fee_scalar: U256::from(1_000), +// ..Default::default() +// }); +// // // Enveloped needs to be some but it will deduce zero fee. +// // context.evm.inner.env.tx.enveloped_tx = Some(bytes!("")); +// // // added mint value is 10. +// // context.evm.inner.env.tx.mint = Some(10); + +// let deposit = TxDeposit { +// gas_limit: 100, +// mint: Some(10), +// source_hash: B256::ZERO, +// ..Default::default() +// }; +// context.evm.inner.env.tx = OpTransaction::Deposit(deposit); + +// deduct_caller::(&mut context).unwrap(); + +// // Check the account balance is updated. +// let account = context +// .evm +// .inner +// .journaled_state +// .load_account(caller, &mut context.evm.inner.db) +// .unwrap(); +// assert_eq!(account.info.balance, U256::from(1010)); +// } + +// #[test] +// fn test_remove_l1_cost_non_deposit() { +// let caller = Address::ZERO; +// let mut db = InMemoryDB::default(); +// db.insert_account_info( +// caller, +// AccountInfo { +// balance: U256::from(1000), +// ..Default::default() +// }, +// ); +// let mut context = Context::::new_with_db(db); +// *context.evm.chain.l1_block_info_mut() = Some(L1BlockInfo { +// l1_base_fee: U256::from(1_000), +// l1_fee_overhead: Some(U256::from(1_000)), +// l1_base_fee_scalar: U256::from(1_000), +// ..Default::default() +// }); +// // // l1block cost is 1048 fee. +// // context.evm.inner.env.tx.enveloped_tx = Some(bytes!("FACADE")); +// // // added mint value is 10. +// // context.evm.inner.env.tx.mint = Some(10); +// // // Putting source_hash to some makes it a deposit transaction. +// // // so enveloped_tx gas cost is ignored. +// // context.evm.inner.env.tx.source_hash = Some(B256::ZERO); + +// let deposit = TxDeposit { +// mint: Some(10), +// source_hash: B256::ZERO, +// ..Default::default() +// }; +// context.evm.inner.env.tx = OpTransaction::Deposit(deposit); + +// deduct_caller::(&mut context).unwrap(); + +// // Check the account balance is updated. +// let account = context +// .evm +// .inner +// .journaled_state +// .load_account(caller, &mut context.evm.inner.db) +// .unwrap(); +// assert_eq!(account.info.balance, U256::from(1010)); +// } + +// #[test] +// fn test_remove_l1_cost() { +// let caller = Address::ZERO; +// let mut db = InMemoryDB::default(); +// db.insert_account_info( +// caller, +// AccountInfo { +// balance: U256::from(1049), +// ..Default::default() +// }, +// ); +// let mut context = Context::::new_with_db(db); +// *context.evm.chain.l1_block_info_mut() = Some(L1BlockInfo { +// l1_base_fee: U256::from(1_000), +// l1_fee_overhead: Some(U256::from(1_000)), +// l1_base_fee_scalar: U256::from(1_000), +// ..Default::default() +// }); +// // l1block cost is 1048 fee. +// context.evm.inner.env.tx = OpTransaction::Base { +// tx: TxEnv::default(), +// enveloped_tx: Some(bytes!("FACADE")), +// }; +// deduct_caller::(&mut context).unwrap(); + +// // Check the account balance is updated. +// let account = context +// .evm +// .inner +// .journaled_state +// .load_account(caller, &mut context.evm.inner.db) +// .unwrap(); +// assert_eq!(account.info.balance, U256::from(1)); +// } + +// #[test] +// fn test_remove_l1_cost_lack_of_funds() { +// let caller = Address::ZERO; +// let mut db = InMemoryDB::default(); +// db.insert_account_info( +// caller, +// AccountInfo { +// balance: U256::from(48), +// ..Default::default() +// }, +// ); +// let mut context = Context::::new_with_db(db); +// *context.evm.chain.l1_block_info_mut() = Some(L1BlockInfo { +// l1_base_fee: U256::from(1_000), +// l1_fee_overhead: Some(U256::from(1_000)), +// l1_base_fee_scalar: U256::from(1_000), +// ..Default::default() +// }); +// // l1block cost is 1048 fee. +// context.evm.inner.env.tx = OpTransaction::Base { +// tx: TxEnv::default(), +// enveloped_tx: Some(bytes!("FACADE")), +// }; + +// assert_eq!( +// deduct_caller::(&mut context), +// Err(EVMError::Transaction( +// InvalidTransaction::LackOfFundForMaxFee { +// fee: Box::new(U256::from(1048)), +// balance: Box::new(U256::from(48)), +// } +// .into(), +// )) +// ); +// } + +// #[test] +// fn test_validate_sys_tx() { +// // mark the tx as a system transaction. +// // Set source hash. +// let tx = TxDeposit { +// is_system_transaction: true, +// ..Default::default() +// }; +// let env = Env::> { +// tx: OpTransaction::Deposit(tx), +// ..Default::default() +// }; + +// assert_eq!( +// validate_env::(&env), +// Err(EVMError::Transaction( +// OpTransactionError::DepositSystemTxPostRegolith +// )) +// ); + +// // Pre-regolith system transactions should be allowed. +// assert!(validate_env::(&env).is_ok()); +// } + +// #[test] +// fn test_validate_deposit_tx() { +// // Set source hash. +// let tx = TxDeposit { +// source_hash: B256::ZERO, +// ..Default::default() +// }; +// let env = Env::> { +// tx: OpTransaction::Deposit(tx), +// ..Default::default() +// }; +// assert!(validate_env::(&env).is_ok()); +// } + +// #[test] +// fn test_validate_tx_against_state_deposit_tx() { +// // Set source hash. +// let tx = TxDeposit { +// source_hash: B256::ZERO, +// ..Default::default() +// }; +// let env = Env::> { +// tx: OpTransaction::Deposit(tx), +// ..Default::default() +// }; + +// // Nonce and balance checks should be skipped for deposit transactions. +// assert!(validate_env::(&env).is_ok()); +// } +// } diff --git a/crates/optimism/src/handler/precompiles.rs b/crates/optimism/src/handler/precompiles.rs new file mode 100644 index 0000000000..de4a018342 --- /dev/null +++ b/crates/optimism/src/handler/precompiles.rs @@ -0,0 +1,121 @@ +use crate::{OpSpec, OpSpecId}; +use once_cell::race::OnceBox; +use precompile::{secp256r1, PrecompileErrors, Precompiles}; +use revm::{ + context::Cfg, context_interface::CfgGetter, handler::EthPrecompileProvider, + handler_interface::PrecompileProvider, specification::hardfork::SpecId, +}; +use std::boxed::Box; + +pub struct OpPrecompileProvider { + precompile_provider: EthPrecompileProvider, +} + +impl Clone for OpPrecompileProvider { + fn clone(&self) -> Self { + Self { + precompile_provider: self.precompile_provider.clone(), + } + } +} + +impl OpPrecompileProvider { + pub fn new(precompiles: &'static Precompiles) -> Self { + Self { + precompile_provider: EthPrecompileProvider { + precompiles, + _phantom: core::marker::PhantomData, + }, + } + } +} + +/// Returns precompiles for Fjor spec. +pub fn fjord() -> &'static Precompiles { + static INSTANCE: OnceBox = OnceBox::new(); + INSTANCE.get_or_init(|| { + let mut precompiles = Precompiles::cancun().clone(); + // EIP-7212: secp256r1 P256verify + precompiles.extend([crate::bn128::pair::GRANITE]); + Box::new(precompiles) + }) +} + +/// Returns precompiles for Granite spec. +pub fn granite() -> &'static Precompiles { + static INSTANCE: OnceBox = OnceBox::new(); + INSTANCE.get_or_init(|| { + let mut precompiles = Precompiles::cancun().clone(); + // Restrict bn256Pairing input size + precompiles.extend([secp256r1::P256VERIFY]); + Box::new(precompiles) + }) +} + +impl PrecompileProvider for OpPrecompileProvider +where + CTX: CfgGetter, + ::Cfg: Cfg, + ERROR: From, +{ + type Context = CTX; + type Error = ERROR; + + #[inline] + fn new(context: &mut Self::Context) -> Self { + let spec = context.cfg().spec(); + match spec { + // no changes + spec @ (OpSpec::Eth( + SpecId::FRONTIER + | SpecId::FRONTIER_THAWING + | SpecId::HOMESTEAD + | SpecId::DAO_FORK + | SpecId::TANGERINE + | SpecId::SPURIOUS_DRAGON + | SpecId::BYZANTIUM + | SpecId::CONSTANTINOPLE + | SpecId::PETERSBURG + | SpecId::ISTANBUL + | SpecId::MUIR_GLACIER + | SpecId::BERLIN + | SpecId::LONDON + | SpecId::ARROW_GLACIER + | SpecId::GRAY_GLACIER + | SpecId::MERGE + | SpecId::SHANGHAI + | SpecId::CANCUN, + ) + | OpSpec::Op( + OpSpecId::BEDROCK | OpSpecId::REGOLITH | OpSpecId::CANYON | OpSpecId::ECOTONE, + )) => Self::new(Precompiles::new(spec.into_eth_spec().into())), + OpSpec::Op(OpSpecId::FJORD) => Self::new(fjord()), + OpSpec::Op(OpSpecId::GRANITE) + | OpSpec::Eth(SpecId::PRAGUE | SpecId::PRAGUE_EOF | SpecId::LATEST) => { + Self::new(granite()) + } + } + } + + #[inline] + fn run( + &mut self, + context: &mut Self::Context, + address: &precompile::Address, + bytes: &precompile::Bytes, + gas_limit: u64, + ) -> Result, Self::Error> { + self.precompile_provider + .run(context, address, bytes, gas_limit) + } + + #[inline] + fn warm_addresses(&self) -> impl Iterator { + self.precompile_provider.warm_addresses() + } + + #[inline] + fn contains(&self, address: &precompile::Address) -> bool { + self.precompile_provider.contains(address) + } +} diff --git a/crates/optimism/src/handler_register.rs b/crates/optimism/src/handler_register.rs deleted file mode 100644 index 95928b5282..0000000000 --- a/crates/optimism/src/handler_register.rs +++ /dev/null @@ -1,785 +0,0 @@ -//! Handler related to Optimism chain - -use crate::{ - optimism_spec_to_generic, - transaction::{ - deposit::DepositTransaction, error::OpTransactionError, OpTransactionType, OpTxTrait, - }, - wiring::{OptimismContextTrait, OptimismWiring}, - OptimismHaltReason, OptimismSpec, OptimismSpecId, -}; -use crate::{BASE_FEE_RECIPIENT, L1_FEE_RECIPIENT}; -use core::ops::Mul; -use revm::{ - database_interface::Database, - handler::{ - mainnet::{self, deduct_caller_inner, validate_block_env, validate_tx_env}, - register::EvmHandler, - }, - interpreter::{return_ok, return_revert, Gas}, - precompile::{secp256r1, PrecompileSpecId}, - primitives::{HashMap, U256}, - state::Account, - transaction::CommonTxFields, - wiring::{ - default::EnvWiring, - result::{ - EVMError, EVMResult, EVMResultGeneric, ExecutionResult, InvalidTransaction, - ResultAndState, - }, - Block, Transaction, - }, - Context, ContextPrecompiles, FrameResult, -}; -use std::sync::Arc; - -pub fn optimism_handle_register(handler: &mut EvmHandler<'_, EvmWiringT>) -where - EvmWiringT: OptimismWiring, -{ - optimism_spec_to_generic!(handler.spec_id, { - // validate environment - handler.validation.env = Arc::new(validate_env::); - // Validate transaction against state. - handler.validation.tx_against_state = - Arc::new(validate_tx_against_state::); - // Load additional precompiles for the given chain spec. - handler.pre_execution.load_precompiles = Arc::new(load_precompiles::); - // load l1 data - handler.pre_execution.load_accounts = Arc::new(load_accounts::); - // An estimated batch cost is charged from the caller and added to L1 Fee Vault. - handler.pre_execution.deduct_caller = Arc::new(deduct_caller::); - // Refund is calculated differently then mainnet. - handler.execution.last_frame_return = Arc::new(last_frame_return::); - handler.post_execution.refund = Arc::new(refund::); - handler.post_execution.reward_beneficiary = - Arc::new(reward_beneficiary::); - // In case of halt of deposit transaction return Error. - handler.post_execution.output = Arc::new(output::); - handler.post_execution.end = Arc::new(end::); - }); -} - -/// Validate environment for the Optimism chain. -pub fn validate_env( - env: &EnvWiring, -) -> EVMResultGeneric<(), EvmWiringT> { - // Do not perform any extra validation for deposit transactions, they are pre-verified on L1. - let tx_type = env.tx.tx_type(); - if tx_type == OpTransactionType::Deposit { - let tx = env.tx.deposit(); - // Do not allow for a system transaction to be processed if Regolith is enabled. - // TODO check if this is correct. - if tx.is_system_transaction() && SPEC::optimism_enabled(OptimismSpecId::REGOLITH) { - return Err(OpTransactionError::DepositSystemTxPostRegolith.into()); - } - return Ok(()); - } - - // Important: validate block before tx. - //validate_block_env::()?; - // Important: validate block before tx as some field are used in transaction validation. - validate_block_env::(&env.block).map_err(EVMError::Header)?; - - // env.validate_tx::() - // .map_err(OptimismInvalidTransaction::Base)?; - - // validate transaction. - validate_tx_env::(&env.tx, &env.block, &env.cfg) - .map_err(OpTransactionError::Base)?; - - Ok(()) -} - -/// Don not perform any extra validation for deposit transactions, they are pre-verified on L1. -pub fn validate_tx_against_state( - context: &mut Context, -) -> EVMResultGeneric<(), EvmWiringT> { - if context.evm.env.tx.tx_type() == OpTransactionType::Deposit { - return Ok(()); - } - mainnet::validate_tx_against_state::(context) -} - -/// Handle output of the transaction -#[inline] -pub fn last_frame_return( - context: &mut Context, - frame_result: &mut FrameResult, -) -> EVMResultGeneric<(), EvmWiringT> { - let env = context.evm.inner.env(); - let is_deposit = env.tx.tx_type() == OpTransactionType::Deposit; - let tx_gas_limit = env.tx.common_fields().gas_limit(); - let is_regolith = SPEC::optimism_enabled(OptimismSpecId::REGOLITH); - - let instruction_result = frame_result.interpreter_result().result; - let gas = frame_result.gas_mut(); - let remaining = gas.remaining(); - let refunded = gas.refunded(); - // Spend the gas limit. Gas is reimbursed when the tx returns successfully. - *gas = Gas::new_spent(tx_gas_limit); - - match instruction_result { - return_ok!() => { - // On Optimism, deposit transactions report gas usage uniquely to other - // transactions due to them being pre-paid on L1. - // - // Hardfork Behavior: - // - Bedrock (success path): - // - Deposit transactions (non-system) report their gas limit as the usage. - // No refunds. - // - Deposit transactions (system) report 0 gas used. No refunds. - // - Regular transactions report gas usage as normal. - // - Regolith (success path): - // - Deposit transactions (all) report their gas used as normal. Refunds - // enabled. - // - Regular transactions report their gas used as normal. - if !is_deposit || is_regolith { - // For regular transactions prior to Regolith and all transactions after - // Regolith, gas is reported as normal. - gas.erase_cost(remaining); - gas.record_refund(refunded); - } else if is_deposit { - let tx = env.tx.deposit(); - if tx.is_system_transaction() { - // System transactions were a special type of deposit transaction in - // the Bedrock hardfork that did not incur any gas costs. - gas.erase_cost(tx_gas_limit); - } - } - } - return_revert!() => { - // On Optimism, deposit transactions report gas usage uniquely to other - // transactions due to them being pre-paid on L1. - // - // Hardfork Behavior: - // - Bedrock (revert path): - // - Deposit transactions (all) report the gas limit as the amount of gas - // used on failure. No refunds. - // - Regular transactions receive a refund on remaining gas as normal. - // - Regolith (revert path): - // - Deposit transactions (all) report the actual gas used as the amount of - // gas used on failure. Refunds on remaining gas enabled. - // - Regular transactions receive a refund on remaining gas as normal. - if !is_deposit || is_regolith { - gas.erase_cost(remaining); - } - } - _ => {} - } - Ok(()) -} - -/// Record Eip-7702 refund and calculate final refund. -#[inline] -pub fn refund( - context: &mut Context, - gas: &mut Gas, - eip7702_refund: i64, -) { - gas.record_refund(eip7702_refund); - - let env = context.evm.inner.env(); - let is_deposit = env.tx.tx_type() == OpTransactionType::Deposit; - let is_regolith = SPEC::optimism_enabled(OptimismSpecId::REGOLITH); - - // Prior to Regolith, deposit transactions did not receive gas refunds. - let is_gas_refund_disabled = env.cfg.is_gas_refund_disabled() || (is_deposit && !is_regolith); - if !is_gas_refund_disabled { - gas.set_final_refund(SPEC::OPTIMISM_SPEC_ID.is_enabled_in(OptimismSpecId::LONDON)); - } -} - -/// Load precompiles for Optimism chain. -#[inline] -pub fn load_precompiles( -) -> ContextPrecompiles { - let mut precompiles = ContextPrecompiles::new(PrecompileSpecId::from_spec_id(SPEC::SPEC_ID)); - - if SPEC::optimism_enabled(OptimismSpecId::FJORD) { - precompiles.extend([ - // EIP-7212: secp256r1 P256verify - secp256r1::P256VERIFY, - ]) - } - - if SPEC::optimism_enabled(OptimismSpecId::GRANITE) { - precompiles.extend([ - // Restrict bn256Pairing input size - crate::bn128::pair::GRANITE, - ]) - } - - precompiles -} - -/// Load account (make them warm) and l1 data from database. -#[inline] -pub fn load_accounts( - context: &mut Context, -) -> EVMResultGeneric<(), EvmWiringT> { - // the L1-cost fee is only computed for Optimism non-deposit transactions. - - if context.evm.env.tx.tx_type() != OpTransactionType::Deposit { - let l1_block_info = - super::L1BlockInfo::try_fetch(&mut context.evm.inner.db, SPEC::OPTIMISM_SPEC_ID) - .map_err(EVMError::Database)?; - - // storage l1 block info for later use. - *context.evm.chain.l1_block_info_mut() = Some(l1_block_info); - } - - mainnet::load_accounts::(context) -} - -/// Deduct max balance from caller -#[inline] -pub fn deduct_caller( - context: &mut Context, -) -> EVMResultGeneric<(), EvmWiringT> { - let caller = context.evm.inner.env.tx.common_fields().caller(); - // load caller's account. - let mut caller_account = context - .evm - .inner - .journaled_state - .load_account(caller, &mut context.evm.inner.db) - .map_err(EVMError::Database)?; - - let is_deposit = context.evm.inner.env.tx.tx_type() == OpTransactionType::Deposit; - - // 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. - if is_deposit { - let tx = context.evm.inner.env.tx.deposit(); - if let Some(mint) = tx.mint() { - caller_account.info.balance += U256::from(mint); - } - } - - // We deduct caller max balance after minting and before deducing the - // l1 cost, max values is already checked in pre_validate but l1 cost wasn't. - deduct_caller_inner::(caller_account.data, &context.evm.inner.env); - - // 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. - if !is_deposit { - // get envelope - let enveloped_tx = context - .evm - .inner - .env - .tx - .enveloped_tx() - .expect("all not deposit tx have enveloped tx"); - - let tx_l1_cost = context - .evm - .inner - .chain - .l1_block_info() - .expect("L1BlockInfo should be loaded") - .calculate_tx_l1_cost(enveloped_tx, SPEC::OPTIMISM_SPEC_ID); - if tx_l1_cost.gt(&caller_account.info.balance) { - return Err(EVMError::Transaction( - InvalidTransaction::LackOfFundForMaxFee { - fee: tx_l1_cost.into(), - balance: caller_account.info.balance.into(), - } - .into(), - )); - } - caller_account.info.balance = caller_account.info.balance.saturating_sub(tx_l1_cost); - } - Ok(()) -} - -/// Reward beneficiary with gas fee. -#[inline] -pub fn reward_beneficiary( - context: &mut Context, - gas: &Gas, -) -> EVMResultGeneric<(), EvmWiringT> { - let is_deposit = context.evm.inner.env.tx.tx_type() == OpTransactionType::Deposit; - - // transfer fee to coinbase/beneficiary. - if !is_deposit { - mainnet::reward_beneficiary::(context, gas)?; - } - - if !is_deposit { - // If the transaction is not a deposit transaction, fees are paid out - // to both the Base Fee Vault as well as the L1 Fee Vault. - let l1_block_info = context - .evm - .chain - .l1_block_info() - .expect("L1BlockInfo should be loaded"); - - let Some(enveloped_tx) = &context.evm.inner.env.tx.enveloped_tx() else { - return Err(EVMError::Custom( - "[OPTIMISM] Failed to load enveloped transaction.".into(), - )); - }; - - let l1_cost = l1_block_info.calculate_tx_l1_cost(enveloped_tx, SPEC::OPTIMISM_SPEC_ID); - - // Send the L1 cost of the transaction to the L1 Fee Vault. - let mut l1_fee_vault_account = context - .evm - .inner - .journaled_state - .load_account(L1_FEE_RECIPIENT, &mut context.evm.inner.db) - .map_err(EVMError::Database)?; - l1_fee_vault_account.mark_touch(); - l1_fee_vault_account.info.balance += l1_cost; - - // Send the base fee of the transaction to the Base Fee Vault. - let mut base_fee_vault_account = context - .evm - .inner - .journaled_state - .load_account(BASE_FEE_RECIPIENT, &mut context.evm.inner.db) - .map_err(EVMError::Database)?; - base_fee_vault_account.mark_touch(); - base_fee_vault_account.info.balance += context - .evm - .inner - .env - .block - .basefee() - .mul(U256::from(gas.spent() - gas.refunded() as u64)); - } - Ok(()) -} - -/// Main return handle, returns the output of the transaction. -#[inline] -pub fn output( - context: &mut Context, - frame_result: FrameResult, -) -> EVMResult { - let result = mainnet::output::(context, frame_result)?; - - if result.result.is_halt() { - // Post-regolith, if the transaction is a deposit transaction and it halts, - // we bubble up to the global return handler. The mint value will be persisted - // and the caller nonce will be incremented there. - let is_deposit = context.evm.inner.env.tx.tx_type() == OpTransactionType::Deposit; - if is_deposit && SPEC::optimism_enabled(OptimismSpecId::REGOLITH) { - return Err(EVMError::Transaction( - OpTransactionError::HaltedDepositPostRegolith, - )); - } - } - Ok(result) -} -/// Optimism end handle changes output if the transaction is a deposit transaction. -/// Deposit transaction can't be reverted and is always successful. -#[inline] -pub fn end( - context: &mut Context, - evm_output: EVMResult, -) -> EVMResult { - let is_deposit = context.evm.inner.env.tx.tx_type() == OpTransactionType::Deposit; - evm_output.or_else(|err| { - if matches!(err, EVMError::Transaction(_)) && is_deposit { - let tx = context.evm.inner.env.tx.deposit(); - // If the transaction is a deposit transaction and it failed - // for any reason, the caller nonce must be bumped, and the - // gas reported must be altered depending on the Hardfork. This is - // also returned as a special Halt variant so that consumers can more - // easily distinguish between a failed deposit and a failed - // normal transaction. - - // Increment sender nonce and account balance for the mint amount. Deposits - // always persist the mint amount, even if the transaction fails. - let account = { - let mut acc = Account::from( - context - .evm - .inner - .db - .basic(tx.caller()) - .unwrap_or_default() - .unwrap_or_default(), - ); - acc.info.nonce = acc.info.nonce.saturating_add(1); - acc.info.balance = acc - .info - .balance - .saturating_add(U256::from(tx.mint().unwrap_or_default())); - acc.mark_touch(); - acc - }; - let state = HashMap::from_iter([(tx.caller(), account)]); - - // The gas used of a failed deposit post-regolith is the gas - // limit of the transaction. pre-regolith, it is the gas limit - // of the transaction for non system transactions and 0 for system - // transactions. - let gas_used = if SPEC::optimism_enabled(OptimismSpecId::REGOLITH) - || !tx.is_system_transaction() - { - tx.gas_limit() - } else { - 0 - }; - - Ok(ResultAndState { - result: ExecutionResult::Halt { - reason: OptimismHaltReason::FailedDeposit, - gas_used, - }, - state, - }) - } else { - Err(err) - } - }) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - transaction::deposit::TxDeposit, wiring::OptimismEvmWiring, BedrockSpec, L1BlockInfo, - LatestSpec, OpTransaction, RegolithSpec, - }; - use database::InMemoryDB; - use revm::{ - database_interface::EmptyDB, - interpreter::{CallOutcome, InstructionResult, InterpreterResult}, - primitives::{bytes, Address, Bytes, B256}, - state::AccountInfo, - wiring::default::{block::BlockEnv, Env, TxEnv}, - }; - use std::boxed::Box; - - type TestEmptyOpWiring = OptimismEvmWiring; - type TestMemOpWiring = OptimismEvmWiring; - - /// Creates frame result. - fn call_last_frame_return( - env: EnvWiring, - instruction_result: InstructionResult, - gas: Gas, - ) -> Gas - where - SPEC: OptimismSpec, - { - let mut ctx = Context::::new_with_db(EmptyDB::default()); - ctx.evm.inner.env = Box::new(env); - let mut first_frame = FrameResult::Call(CallOutcome::new( - InterpreterResult { - result: instruction_result, - output: Bytes::new(), - gas, - }, - 0..0, - )); - last_frame_return::(&mut ctx, &mut first_frame).unwrap(); - refund::(&mut ctx, first_frame.gas_mut(), 0); - *first_frame.gas() - } - - #[test] - fn test_revert_gas() { - let mut env = EnvWiring::::default(); - let tx = TxEnv { - gas_limit: 100, - ..Default::default() - }; - env.tx = OpTransaction::Base { - tx, - enveloped_tx: None, - }; - - let gas = - call_last_frame_return::(env, InstructionResult::Revert, Gas::new(90)); - assert_eq!(gas.remaining(), 90); - assert_eq!(gas.spent(), 10); - assert_eq!(gas.refunded(), 0); - } - - #[test] - fn test_consume_gas() { - let mut env = EnvWiring::::default(); - //env.tx.base.gas_limit = 100; - //env.tx.source_hash = Some(B256::ZERO); - - let deposit = TxDeposit { - gas_limit: 100, - source_hash: B256::ZERO, - ..Default::default() - }; - env.tx = OpTransaction::Deposit(deposit); - - let gas = - call_last_frame_return::(env, InstructionResult::Stop, Gas::new(90)); - assert_eq!(gas.remaining(), 90); - assert_eq!(gas.spent(), 10); - assert_eq!(gas.refunded(), 0); - } - - #[test] - fn test_consume_gas_with_refund() { - let mut env = EnvWiring::::default(); - //env.tx.base.gas_limit = 100; - //env.tx.source_hash = Some(B256::ZERO); - let deposit = TxDeposit { - gas_limit: 100, - source_hash: B256::ZERO, - ..Default::default() - }; - env.tx = OpTransaction::Deposit(deposit); - - let mut ret_gas = Gas::new(90); - ret_gas.record_refund(20); - - let gas = - call_last_frame_return::(env.clone(), InstructionResult::Stop, ret_gas); - assert_eq!(gas.remaining(), 90); - assert_eq!(gas.spent(), 10); - assert_eq!(gas.refunded(), 2); // min(20, 10/5) - - let gas = call_last_frame_return::(env, InstructionResult::Revert, ret_gas); - assert_eq!(gas.remaining(), 90); - assert_eq!(gas.spent(), 10); - assert_eq!(gas.refunded(), 0); - } - - #[test] - fn test_consume_gas_sys_deposit_tx() { - let mut env = EnvWiring::::default(); - //env.tx.base.gas_limit = 100; - //env.tx.source_hash = Some(B256::ZERO); - - let deposit = TxDeposit { - gas_limit: 100, - source_hash: B256::ZERO, - ..Default::default() - }; - env.tx = OpTransaction::Deposit(deposit); - - let gas = call_last_frame_return::(env, InstructionResult::Stop, Gas::new(90)); - assert_eq!(gas.remaining(), 0); - assert_eq!(gas.spent(), 100); - assert_eq!(gas.refunded(), 0); - } - - #[test] - fn test_commit_mint_value() { - let caller = Address::ZERO; - let mut db = InMemoryDB::default(); - db.insert_account_info( - caller, - AccountInfo { - balance: U256::from(1000), - ..Default::default() - }, - ); - - let mut context = Context::::new_with_db(db); - *context.evm.chain.l1_block_info_mut() = Some(L1BlockInfo { - l1_base_fee: U256::from(1_000), - l1_fee_overhead: Some(U256::from(1_000)), - l1_base_fee_scalar: U256::from(1_000), - ..Default::default() - }); - // // Enveloped needs to be some but it will deduce zero fee. - // context.evm.inner.env.tx.enveloped_tx = Some(bytes!("")); - // // added mint value is 10. - // context.evm.inner.env.tx.mint = Some(10); - - let deposit = TxDeposit { - gas_limit: 100, - mint: Some(10), - source_hash: B256::ZERO, - ..Default::default() - }; - context.evm.inner.env.tx = OpTransaction::Deposit(deposit); - - deduct_caller::(&mut context).unwrap(); - - // Check the account balance is updated. - let account = context - .evm - .inner - .journaled_state - .load_account(caller, &mut context.evm.inner.db) - .unwrap(); - assert_eq!(account.info.balance, U256::from(1010)); - } - - #[test] - fn test_remove_l1_cost_non_deposit() { - let caller = Address::ZERO; - let mut db = InMemoryDB::default(); - db.insert_account_info( - caller, - AccountInfo { - balance: U256::from(1000), - ..Default::default() - }, - ); - let mut context = Context::::new_with_db(db); - *context.evm.chain.l1_block_info_mut() = Some(L1BlockInfo { - l1_base_fee: U256::from(1_000), - l1_fee_overhead: Some(U256::from(1_000)), - l1_base_fee_scalar: U256::from(1_000), - ..Default::default() - }); - // // l1block cost is 1048 fee. - // context.evm.inner.env.tx.enveloped_tx = Some(bytes!("FACADE")); - // // added mint value is 10. - // context.evm.inner.env.tx.mint = Some(10); - // // Putting source_hash to some makes it a deposit transaction. - // // so enveloped_tx gas cost is ignored. - // context.evm.inner.env.tx.source_hash = Some(B256::ZERO); - - let deposit = TxDeposit { - mint: Some(10), - source_hash: B256::ZERO, - ..Default::default() - }; - context.evm.inner.env.tx = OpTransaction::Deposit(deposit); - - deduct_caller::(&mut context).unwrap(); - - // Check the account balance is updated. - let account = context - .evm - .inner - .journaled_state - .load_account(caller, &mut context.evm.inner.db) - .unwrap(); - assert_eq!(account.info.balance, U256::from(1010)); - } - - #[test] - fn test_remove_l1_cost() { - let caller = Address::ZERO; - let mut db = InMemoryDB::default(); - db.insert_account_info( - caller, - AccountInfo { - balance: U256::from(1049), - ..Default::default() - }, - ); - let mut context = Context::::new_with_db(db); - *context.evm.chain.l1_block_info_mut() = Some(L1BlockInfo { - l1_base_fee: U256::from(1_000), - l1_fee_overhead: Some(U256::from(1_000)), - l1_base_fee_scalar: U256::from(1_000), - ..Default::default() - }); - // l1block cost is 1048 fee. - context.evm.inner.env.tx = OpTransaction::Base { - tx: TxEnv::default(), - enveloped_tx: Some(bytes!("FACADE")), - }; - deduct_caller::(&mut context).unwrap(); - - // Check the account balance is updated. - let account = context - .evm - .inner - .journaled_state - .load_account(caller, &mut context.evm.inner.db) - .unwrap(); - assert_eq!(account.info.balance, U256::from(1)); - } - - #[test] - fn test_remove_l1_cost_lack_of_funds() { - let caller = Address::ZERO; - let mut db = InMemoryDB::default(); - db.insert_account_info( - caller, - AccountInfo { - balance: U256::from(48), - ..Default::default() - }, - ); - let mut context = Context::::new_with_db(db); - *context.evm.chain.l1_block_info_mut() = Some(L1BlockInfo { - l1_base_fee: U256::from(1_000), - l1_fee_overhead: Some(U256::from(1_000)), - l1_base_fee_scalar: U256::from(1_000), - ..Default::default() - }); - // l1block cost is 1048 fee. - context.evm.inner.env.tx = OpTransaction::Base { - tx: TxEnv::default(), - enveloped_tx: Some(bytes!("FACADE")), - }; - - assert_eq!( - deduct_caller::(&mut context), - Err(EVMError::Transaction( - InvalidTransaction::LackOfFundForMaxFee { - fee: Box::new(U256::from(1048)), - balance: Box::new(U256::from(48)), - } - .into(), - )) - ); - } - - #[test] - fn test_validate_sys_tx() { - // mark the tx as a system transaction. - // Set source hash. - let tx = TxDeposit { - is_system_transaction: true, - ..Default::default() - }; - let env = Env::> { - tx: OpTransaction::Deposit(tx), - ..Default::default() - }; - - assert_eq!( - validate_env::(&env), - Err(EVMError::Transaction( - OpTransactionError::DepositSystemTxPostRegolith - )) - ); - - // Pre-regolith system transactions should be allowed. - assert!(validate_env::(&env).is_ok()); - } - - #[test] - fn test_validate_deposit_tx() { - // Set source hash. - let tx = TxDeposit { - source_hash: B256::ZERO, - ..Default::default() - }; - let env = Env::> { - tx: OpTransaction::Deposit(tx), - ..Default::default() - }; - assert!(validate_env::(&env).is_ok()); - } - - #[test] - fn test_validate_tx_against_state_deposit_tx() { - // Set source hash. - let tx = TxDeposit { - source_hash: B256::ZERO, - ..Default::default() - }; - let env = Env::> { - tx: OpTransaction::Deposit(tx), - ..Default::default() - }; - - // Nonce and balance checks should be skipped for deposit transactions. - assert!(validate_env::(&env).is_ok()); - } -} diff --git a/crates/optimism/src/l1block.rs b/crates/optimism/src/l1block.rs index 5fdc9c794e..13e64942ae 100644 --- a/crates/optimism/src/l1block.rs +++ b/crates/optimism/src/l1block.rs @@ -1,32 +1,34 @@ -use crate::fast_lz::flz_compress_len; +use crate::{fast_lz::flz_compress_len, OpSpecId}; use core::ops::Mul; use revm::{ database_interface::Database, primitives::{address, Address, U256}, + specification::hardfork::SpecId, + Context, }; -use super::OptimismSpecId; +use super::OpSpec; -const ZERO_BYTE_COST: u64 = 4; -const NON_ZERO_BYTE_COST: u64 = 16; +pub const ZERO_BYTE_COST: u64 = 4; +pub const NON_ZERO_BYTE_COST: u64 = 16; /// The two 4-byte Ecotone fee scalar values are packed into the same storage slot as the 8-byte sequence number. /// Byte offset within the storage slot of the 4-byte baseFeeScalar attribute. -const BASE_FEE_SCALAR_OFFSET: usize = 16; +pub const BASE_FEE_SCALAR_OFFSET: usize = 16; /// The two 4-byte Ecotone fee scalar values are packed into the same storage slot as the 8-byte sequence number. /// Byte offset within the storage slot of the 4-byte blobBaseFeeScalar attribute. -const BLOB_BASE_FEE_SCALAR_OFFSET: usize = 20; +pub const BLOB_BASE_FEE_SCALAR_OFFSET: usize = 20; -const L1_BASE_FEE_SLOT: U256 = U256::from_limbs([1u64, 0, 0, 0]); -const L1_OVERHEAD_SLOT: U256 = U256::from_limbs([5u64, 0, 0, 0]); -const L1_SCALAR_SLOT: U256 = U256::from_limbs([6u64, 0, 0, 0]); +pub const L1_BASE_FEE_SLOT: U256 = U256::from_limbs([1u64, 0, 0, 0]); +pub const L1_OVERHEAD_SLOT: U256 = U256::from_limbs([5u64, 0, 0, 0]); +pub const L1_SCALAR_SLOT: U256 = U256::from_limbs([6u64, 0, 0, 0]); /// [ECOTONE_L1_BLOB_BASE_FEE_SLOT] was added in the Ecotone upgrade and stores the L1 blobBaseFee attribute. -const ECOTONE_L1_BLOB_BASE_FEE_SLOT: U256 = U256::from_limbs([7u64, 0, 0, 0]); +pub const ECOTONE_L1_BLOB_BASE_FEE_SLOT: U256 = U256::from_limbs([7u64, 0, 0, 0]); /// As of the ecotone upgrade, this storage slot stores the 32-bit basefeeScalar and blobBaseFeeScalar attributes at /// offsets [BASE_FEE_SCALAR_OFFSET] and [BLOB_BASE_FEE_SCALAR_OFFSET] respectively. -const ECOTONE_L1_FEE_SCALARS_SLOT: U256 = U256::from_limbs([3u64, 0, 0, 0]); +pub const ECOTONE_L1_FEE_SCALARS_SLOT: U256 = U256::from_limbs([3u64, 0, 0, 0]); /// An empty 64-bit set of scalar values. const EMPTY_SCALARS: [u8; 8] = [0u8; 8]; @@ -69,19 +71,16 @@ pub struct L1BlockInfo { impl L1BlockInfo { /// Try to fetch the L1 block info from the database. - pub fn try_fetch( - db: &mut DB, - spec_id: OptimismSpecId, - ) -> Result { + pub fn try_fetch(db: &mut DB, spec_id: OpSpec) -> Result { // Ensure the L1 Block account is loaded into the cache after Ecotone. With EIP-4788, it is no longer the case // that the L1 block account is loaded into the cache prior to the first inquiry for the L1 block info. - if spec_id.is_enabled_in(OptimismSpecId::CANCUN) { + if spec_id.is_enabled_in(SpecId::CANCUN) { let _ = db.basic(L1_BLOCK_CONTRACT)?; } let l1_base_fee = db.storage(L1_BLOCK_CONTRACT, L1_BASE_FEE_SLOT)?; - if !spec_id.is_enabled_in(OptimismSpecId::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)?; @@ -132,8 +131,8 @@ impl L1BlockInfo { /// /// Prior to regolith, an extra 68 non-zero bytes were included in the rollup data costs to /// account for the empty signature. - pub fn data_gas(&self, input: &[u8], spec_id: OptimismSpecId) -> U256 { - if spec_id.is_enabled_in(OptimismSpecId::FJORD) { + pub fn data_gas(&self, input: &[u8], spec_id: OpSpec) -> U256 { + if spec_id.is_enabled_in(OpSpecId::FJORD) { let estimated_size = self.tx_estimated_size_fjord(input); return estimated_size @@ -150,7 +149,7 @@ impl L1BlockInfo { })); // Prior to regolith, an extra 68 non zero bytes were included in the rollup data costs. - if !spec_id.is_enabled_in(OptimismSpecId::REGOLITH) { + if !spec_id.is_enabled_in(OpSpecId::REGOLITH) { rollup_data_gas_cost += U256::from(NON_ZERO_BYTE_COST).mul(U256::from(68)); } @@ -169,16 +168,16 @@ impl L1BlockInfo { .max(U256::from(100_000_000)) } - /// Calculate the gas cost of a transaction based on L1 block data posted on L2, depending on the [OptimismSpecId] passed. - pub fn calculate_tx_l1_cost(&self, input: &[u8], spec_id: OptimismSpecId) -> U256 { + /// Calculate the gas cost of a transaction based on L1 block data posted on L2, depending on the [OpSpec] passed. + pub fn calculate_tx_l1_cost(&self, input: &[u8], spec_id: OpSpec) -> U256 { // If the input is a deposit transaction or empty, the default value is zero. if input.is_empty() || input.first() == Some(&0x7F) { return U256::ZERO; } - if spec_id.is_enabled_in(OptimismSpecId::FJORD) { + if spec_id.is_enabled_in(OpSpecId::FJORD) { self.calculate_tx_l1_cost_fjord(input) - } else if spec_id.is_enabled_in(OptimismSpecId::ECOTONE) { + } else if spec_id.is_enabled_in(OpSpecId::ECOTONE) { self.calculate_tx_l1_cost_ecotone(input, spec_id) } else { self.calculate_tx_l1_cost_bedrock(input, spec_id) @@ -186,7 +185,7 @@ impl L1BlockInfo { } /// Calculate the gas cost of a transaction based on L1 block data posted on L2, pre-Ecotone. - fn calculate_tx_l1_cost_bedrock(&self, input: &[u8], spec_id: OptimismSpecId) -> U256 { + fn calculate_tx_l1_cost_bedrock(&self, input: &[u8], spec_id: OpSpec) -> U256 { let rollup_data_gas_cost = self.data_gas(input, spec_id); rollup_data_gas_cost .saturating_add(self.l1_fee_overhead.unwrap_or_default()) @@ -197,7 +196,7 @@ impl L1BlockInfo { /// Calculate the gas cost of a transaction based on L1 block data posted on L2, post-Ecotone. /// - /// [OptimismSpecId::ECOTONE] L1 cost function: + /// [OpSpecId::ECOTONE] L1 cost function: /// `(calldataGas/16)*(l1BaseFee*16*l1BaseFeeScalar + l1BlobBaseFee*l1BlobBaseFeeScalar)/1e6` /// /// We divide "calldataGas" by 16 to change from units of calldata gas to "estimated # of bytes when compressed". @@ -205,7 +204,7 @@ impl L1BlockInfo { /// /// Function is actually computed as follows for better precision under integer arithmetic: /// `calldataGas*(l1BaseFee*16*l1BaseFeeScalar + l1BlobBaseFee*l1BlobBaseFeeScalar)/16e6` - fn calculate_tx_l1_cost_ecotone(&self, input: &[u8], spec_id: OptimismSpecId) -> U256 { + fn calculate_tx_l1_cost_ecotone(&self, input: &[u8], spec_id: OpSpec) -> U256 { // There is an edgecase where, for the very first Ecotone block (unless it is activated at Genesis), we must // use the Bedrock cost function. To determine if this is the case, we can check if the Ecotone parameters are // unset. @@ -223,7 +222,7 @@ impl L1BlockInfo { /// Calculate the gas cost of a transaction based on L1 block data posted on L2, post-Fjord. /// - /// [OptimismSpecId::FJORD] L1 cost function: + /// [OpSpecId::FJORD] L1 cost function: /// `estimatedSize*(baseFeeScalar*l1BaseFee*16 + blobFeeScalar*l1BlobBaseFee)/1e12` fn calculate_tx_l1_cost_fjord(&self, input: &[u8]) -> U256 { let l1_fee_scaled = self.calculate_l1_fee_scaled_ecotone(); @@ -249,6 +248,23 @@ impl L1BlockInfo { } } +pub trait L1BlockInfoGetter { + fn l1_block_info(&self) -> &L1BlockInfo; + fn l1_block_info_mut(&mut self) -> &mut L1BlockInfo; +} + +impl L1BlockInfoGetter + for Context +{ + fn l1_block_info(&self) -> &L1BlockInfo { + &self.chain + } + + fn l1_block_info_mut(&mut self) -> &mut L1BlockInfo { + &mut self.chain + } +} + #[cfg(test)] mod tests { use super::*; @@ -270,17 +286,17 @@ mod tests { // gas cost = 3 non-zero bytes * NON_ZERO_BYTE_COST + NON_ZERO_BYTE_COST * 68 // gas cost = 3 * 16 + 68 * 16 = 1136 let input = bytes!("FACADE"); - let bedrock_data_gas = l1_block_info.data_gas(&input, OptimismSpecId::BEDROCK); + let bedrock_data_gas = l1_block_info.data_gas(&input, OpSpecId::BEDROCK.into()); assert_eq!(bedrock_data_gas, U256::from(1136)); // Regolith has no added 68 non zero bytes // gas cost = 3 * 16 = 48 - let regolith_data_gas = l1_block_info.data_gas(&input, OptimismSpecId::REGOLITH); + let regolith_data_gas = l1_block_info.data_gas(&input, OpSpecId::REGOLITH.into()); assert_eq!(regolith_data_gas, U256::from(48)); // Fjord has a minimum compressed size of 100 bytes // gas cost = 100 * 16 = 1600 - let fjord_data_gas = l1_block_info.data_gas(&input, OptimismSpecId::FJORD); + let fjord_data_gas = l1_block_info.data_gas(&input, OpSpecId::FJORD.into()); assert_eq!(fjord_data_gas, U256::from(1600)); } @@ -300,17 +316,17 @@ mod tests { // gas cost = 3 non-zero * NON_ZERO_BYTE_COST + 2 * ZERO_BYTE_COST + NON_ZERO_BYTE_COST * 68 // gas cost = 3 * 16 + 2 * 4 + 68 * 16 = 1144 let input = bytes!("FA00CA00DE"); - let bedrock_data_gas = l1_block_info.data_gas(&input, OptimismSpecId::BEDROCK); + let bedrock_data_gas = l1_block_info.data_gas(&input, OpSpecId::BEDROCK.into()); assert_eq!(bedrock_data_gas, U256::from(1144)); // Regolith has no added 68 non zero bytes // gas cost = 3 * 16 + 2 * 4 = 56 - let regolith_data_gas = l1_block_info.data_gas(&input, OptimismSpecId::REGOLITH); + let regolith_data_gas = l1_block_info.data_gas(&input, OpSpecId::REGOLITH.into()); assert_eq!(regolith_data_gas, U256::from(56)); // Fjord has a minimum compressed size of 100 bytes // gas cost = 100 * 16 = 1600 - let fjord_data_gas = l1_block_info.data_gas(&input, OptimismSpecId::FJORD); + let fjord_data_gas = l1_block_info.data_gas(&input, OpSpecId::FJORD.into()); assert_eq!(fjord_data_gas, U256::from(1600)); } @@ -324,17 +340,17 @@ mod tests { }; let input = bytes!("FACADE"); - let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OptimismSpecId::REGOLITH); + let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::REGOLITH.into()); assert_eq!(gas_cost, U256::from(1048)); // Zero rollup data gas cost should result in zero let input = bytes!(""); - let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OptimismSpecId::REGOLITH); + let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::REGOLITH.into()); assert_eq!(gas_cost, U256::ZERO); // Deposit transactions with the EIP-2718 type of 0x7F should result in zero let input = bytes!("7FFACADE"); - let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OptimismSpecId::REGOLITH); + let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::REGOLITH.into()); assert_eq!(gas_cost, U256::ZERO); } @@ -353,23 +369,23 @@ mod tests { // = (16 * 3) * (1000 * 16 * 1000 + 1000 * 1000) / (16 * 1e6) // = 51 let input = bytes!("FACADE"); - let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OptimismSpecId::ECOTONE); + let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::ECOTONE.into()); assert_eq!(gas_cost, U256::from(51)); // Zero rollup data gas cost should result in zero let input = bytes!(""); - let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OptimismSpecId::ECOTONE); + let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::ECOTONE.into()); assert_eq!(gas_cost, U256::ZERO); // Deposit transactions with the EIP-2718 type of 0x7F should result in zero let input = bytes!("7FFACADE"); - let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OptimismSpecId::ECOTONE); + let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::ECOTONE.into()); assert_eq!(gas_cost, U256::ZERO); // If the scalars are empty, the bedrock cost function should be used. l1_block_info.empty_scalars = true; let input = bytes!("FACADE"); - let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OptimismSpecId::ECOTONE); + let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::ECOTONE.into()); assert_eq!(gas_cost, U256::from(1048)); } @@ -408,11 +424,11 @@ mod tests { // test - let gas_used = l1_block_info.data_gas(TX, OptimismSpecId::ECOTONE); + let gas_used = l1_block_info.data_gas(TX, OpSpecId::ECOTONE.into()); assert_eq!(gas_used, expected_l1_gas_used); - let l1_fee = l1_block_info.calculate_tx_l1_cost_ecotone(TX, OptimismSpecId::ECOTONE); + let l1_fee = l1_block_info.calculate_tx_l1_cost_ecotone(TX, OpSpecId::ECOTONE.into()); assert_eq!(l1_fee, expected_l1_fee) } @@ -438,7 +454,7 @@ mod tests { // l1Cost = estimatedSize * l1FeeScaled / 1e12 // = 100e6 * 17 / 1e6 // = 1700 - let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OptimismSpecId::FJORD); + let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::FJORD.into()); assert_eq!(gas_cost, U256::from(1700)); // fastLzSize = 202 @@ -449,17 +465,17 @@ mod tests { // l1Cost = estimatedSize * l1FeeScaled / 1e12 // = 126387400 * 17 / 1e6 // = 2148 - let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OptimismSpecId::FJORD); + let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::FJORD.into()); assert_eq!(gas_cost, U256::from(2148)); // Zero rollup data gas cost should result in zero let input = bytes!(""); - let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OptimismSpecId::FJORD); + let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::FJORD.into()); assert_eq!(gas_cost, U256::ZERO); // Deposit transactions with the EIP-2718 type of 0x7F should result in zero let input = bytes!("7FFACADE"); - let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OptimismSpecId::FJORD); + let gas_cost = l1_block_info.calculate_tx_l1_cost(&input, OpSpecId::FJORD.into()); assert_eq!(gas_cost, U256::ZERO); } @@ -490,7 +506,7 @@ mod tests { // test - let data_gas = l1_block_info.data_gas(TX, OptimismSpecId::FJORD); + let data_gas = l1_block_info.data_gas(TX, OpSpecId::FJORD.into()); assert_eq!(data_gas, expected_data_gas); diff --git a/crates/optimism/src/lib.rs b/crates/optimism/src/lib.rs index faf0d1edf3..fbf11e0da1 100644 --- a/crates/optimism/src/lib.rs +++ b/crates/optimism/src/lib.rs @@ -6,20 +6,17 @@ extern crate alloc as std; pub mod bn128; +pub mod evm; pub mod fast_lz; -pub mod handler_register; +pub mod handler; pub mod l1block; pub mod result; pub mod spec; pub mod transaction; -pub mod wiring; -pub use handler_register::{ - deduct_caller, end, last_frame_return, load_accounts, load_precompiles, - optimism_handle_register, output, refund, reward_beneficiary, validate_env, - validate_tx_against_state, +pub use l1block::{ + L1BlockInfo, L1BlockInfoGetter, BASE_FEE_RECIPIENT, L1_BLOCK_CONTRACT, L1_FEE_RECIPIENT, }; -pub use l1block::{L1BlockInfo, BASE_FEE_RECIPIENT, L1_BLOCK_CONTRACT, L1_FEE_RECIPIENT}; pub use result::OptimismHaltReason; pub use spec::*; pub use transaction::{error::OpTransactionError, OpTransaction, OpTransactionType}; diff --git a/crates/optimism/src/result.rs b/crates/optimism/src/result.rs index 7b3103f47c..90f526e468 100644 --- a/crates/optimism/src/result.rs +++ b/crates/optimism/src/result.rs @@ -1,4 +1,4 @@ -use revm::wiring::result::HaltReason; +use revm::context_interface::result::HaltReason; #[derive(Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] diff --git a/crates/optimism/src/spec.rs b/crates/optimism/src/spec.rs index 6e05af241b..82530d3855 100644 --- a/crates/optimism/src/spec.rs +++ b/crates/optimism/src/spec.rs @@ -1,141 +1,84 @@ -use revm::{ - precompile::PrecompileSpecId, - specification::hardfork::{Spec, SpecId}, -}; +use revm::specification::hardfork::SpecId; -/// Specification IDs for the optimism blockchain. #[repr(u8)] -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, enumn::N)] +#[derive(Clone, Copy, Debug, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[allow(non_camel_case_types)] -pub enum OptimismSpecId { - FRONTIER = 0, - FRONTIER_THAWING = 1, - HOMESTEAD = 2, - DAO_FORK = 3, - TANGERINE = 4, - SPURIOUS_DRAGON = 5, - BYZANTIUM = 6, - CONSTANTINOPLE = 7, - PETERSBURG = 8, - ISTANBUL = 9, - MUIR_GLACIER = 10, - BERLIN = 11, - LONDON = 12, - ARROW_GLACIER = 13, - GRAY_GLACIER = 14, - MERGE = 15, - BEDROCK = 16, - REGOLITH = 17, - SHANGHAI = 18, - CANYON = 19, - CANCUN = 20, - ECOTONE = 21, - FJORD = 22, - GRANITE = 23, - PRAGUE = 24, - PRAGUE_EOF = 25, - #[default] - LATEST = u8::MAX, +pub enum OpSpec { + Eth(SpecId), + Op(OpSpecId), } -impl OptimismSpecId { - /// Returns the `OptimismSpecId` for the given `u8`. - #[inline] - pub fn try_from_u8(spec_id: u8) -> Option { - Self::n(spec_id) - } +#[repr(u8)] +#[derive(Clone, Copy, Debug, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[allow(non_camel_case_types)] +pub enum OpSpecId { + BEDROCK = 100, + REGOLITH, + CANYON, + ECOTONE, + FJORD, + GRANITE, +} - /// Returns `true` if the given specification ID is enabled in this spec. - #[inline] - pub const fn is_enabled_in(self, other: Self) -> bool { - Self::enabled(self, other) +impl OpSpecId { + /// Converts the `OpSpec` into a `SpecId`. + pub const fn into_eth_spec(self) -> SpecId { + match self { + Self::BEDROCK | Self::REGOLITH => SpecId::MERGE, + Self::CANYON => SpecId::SHANGHAI, + Self::ECOTONE | Self::FJORD | Self::GRANITE => SpecId::CANCUN, + } } - /// Returns `true` if the given specification ID is enabled in this spec. - #[inline] - pub const fn enabled(our: Self, other: Self) -> bool { - our as u8 >= other as u8 + pub const fn is_enabled_in(self, other: OpSpecId) -> bool { + self as u8 <= other as u8 } +} - /// Converts the `OptimismSpecId` into a `SpecId`. - const fn into_eth_spec_id(self) -> SpecId { - match self { - OptimismSpecId::FRONTIER => SpecId::FRONTIER, - OptimismSpecId::FRONTIER_THAWING => SpecId::FRONTIER_THAWING, - OptimismSpecId::HOMESTEAD => SpecId::HOMESTEAD, - OptimismSpecId::DAO_FORK => SpecId::DAO_FORK, - OptimismSpecId::TANGERINE => SpecId::TANGERINE, - OptimismSpecId::SPURIOUS_DRAGON => SpecId::SPURIOUS_DRAGON, - OptimismSpecId::BYZANTIUM => SpecId::BYZANTIUM, - OptimismSpecId::CONSTANTINOPLE => SpecId::CONSTANTINOPLE, - OptimismSpecId::PETERSBURG => SpecId::PETERSBURG, - OptimismSpecId::ISTANBUL => SpecId::ISTANBUL, - OptimismSpecId::MUIR_GLACIER => SpecId::MUIR_GLACIER, - OptimismSpecId::BERLIN => SpecId::BERLIN, - OptimismSpecId::LONDON => SpecId::LONDON, - OptimismSpecId::ARROW_GLACIER => SpecId::ARROW_GLACIER, - OptimismSpecId::GRAY_GLACIER => SpecId::GRAY_GLACIER, - OptimismSpecId::MERGE | OptimismSpecId::BEDROCK | OptimismSpecId::REGOLITH => { - SpecId::MERGE - } - OptimismSpecId::SHANGHAI | OptimismSpecId::CANYON => SpecId::SHANGHAI, - OptimismSpecId::CANCUN - | OptimismSpecId::ECOTONE - | OptimismSpecId::FJORD - | OptimismSpecId::GRANITE => SpecId::CANCUN, - OptimismSpecId::PRAGUE => SpecId::PRAGUE, - OptimismSpecId::PRAGUE_EOF => SpecId::PRAGUE_EOF, - OptimismSpecId::LATEST => SpecId::LATEST, - } +impl From for OpSpec { + fn from(spec: OpSpecId) -> Self { + OpSpec::Op(spec) } } -impl From for SpecId { - fn from(value: OptimismSpecId) -> Self { - value.into_eth_spec_id() +impl From for OpSpec { + fn from(spec: SpecId) -> Self { + OpSpec::Eth(spec) } } -impl From for OptimismSpecId { - fn from(value: SpecId) -> Self { - match value { - SpecId::FRONTIER => Self::FRONTIER, - SpecId::FRONTIER_THAWING => Self::FRONTIER_THAWING, - SpecId::HOMESTEAD => Self::HOMESTEAD, - SpecId::DAO_FORK => Self::DAO_FORK, - SpecId::TANGERINE => Self::TANGERINE, - SpecId::SPURIOUS_DRAGON => Self::SPURIOUS_DRAGON, - SpecId::BYZANTIUM => Self::BYZANTIUM, - SpecId::CONSTANTINOPLE => Self::CONSTANTINOPLE, - SpecId::PETERSBURG => Self::PETERSBURG, - SpecId::ISTANBUL => Self::ISTANBUL, - SpecId::MUIR_GLACIER => Self::MUIR_GLACIER, - SpecId::BERLIN => Self::BERLIN, - SpecId::LONDON => Self::LONDON, - SpecId::ARROW_GLACIER => Self::ARROW_GLACIER, - SpecId::GRAY_GLACIER => Self::GRAY_GLACIER, - SpecId::MERGE => Self::MERGE, - SpecId::SHANGHAI => Self::SHANGHAI, - SpecId::CANCUN => Self::CANCUN, - SpecId::PRAGUE => Self::PRAGUE, - SpecId::PRAGUE_EOF => Self::PRAGUE_EOF, - SpecId::LATEST => Self::LATEST, +impl TryFrom<&str> for OpSpecId { + type Error = (); + + fn try_from(name: &str) -> Result { + match name { + name::BEDROCK => Ok(OpSpecId::BEDROCK), + name::REGOLITH => Ok(OpSpecId::REGOLITH), + name::CANYON => Ok(OpSpecId::CANYON), + name::ECOTONE => Ok(OpSpecId::ECOTONE), + name::FJORD => Ok(OpSpecId::FJORD), + name::GRANITE => Ok(OpSpecId::GRANITE), + _ => Err(()), } } } -impl From for PrecompileSpecId { - fn from(value: OptimismSpecId) -> Self { - PrecompileSpecId::from_spec_id(value.into_eth_spec_id()) +impl From for &'static str { + fn from(spec_id: OpSpecId) -> Self { + match spec_id { + OpSpecId::BEDROCK => name::BEDROCK, + OpSpecId::REGOLITH => name::REGOLITH, + OpSpecId::CANYON => name::CANYON, + OpSpecId::ECOTONE => name::ECOTONE, + OpSpecId::FJORD => name::FJORD, + OpSpecId::GRANITE => name::GRANITE, + } } } /// String identifiers for Optimism hardforks. -pub mod id { - // Re-export the Ethereum hardforks. - pub use revm::specification::hardfork::id::*; - +pub mod name { pub const BEDROCK: &str = "Bedrock"; pub const REGOLITH: &str = "Regolith"; pub const CANYON: &str = "Canyon"; @@ -144,659 +87,106 @@ pub mod id { pub const GRANITE: &str = "Granite"; } -impl From<&str> for OptimismSpecId { - fn from(name: &str) -> Self { - match name { - id::FRONTIER => Self::FRONTIER, - id::FRONTIER_THAWING => Self::FRONTIER_THAWING, - id::HOMESTEAD => Self::HOMESTEAD, - id::DAO_FORK => Self::DAO_FORK, - id::TANGERINE => Self::TANGERINE, - id::SPURIOUS_DRAGON => Self::SPURIOUS_DRAGON, - id::BYZANTIUM => Self::BYZANTIUM, - id::CONSTANTINOPLE => Self::CONSTANTINOPLE, - id::PETERSBURG => Self::PETERSBURG, - id::ISTANBUL => Self::ISTANBUL, - id::MUIR_GLACIER => Self::MUIR_GLACIER, - id::BERLIN => Self::BERLIN, - id::LONDON => Self::LONDON, - id::ARROW_GLACIER => Self::ARROW_GLACIER, - id::GRAY_GLACIER => Self::GRAY_GLACIER, - id::MERGE => Self::MERGE, - id::SHANGHAI => Self::SHANGHAI, - id::CANCUN => Self::CANCUN, - id::PRAGUE => Self::PRAGUE, - id::PRAGUE_EOF => Self::PRAGUE_EOF, - id::BEDROCK => Self::BEDROCK, - id::REGOLITH => Self::REGOLITH, - id::CANYON => Self::CANYON, - id::ECOTONE => Self::ECOTONE, - id::FJORD => Self::FJORD, - id::LATEST => Self::LATEST, - _ => Self::LATEST, +impl OpSpec { + /// Returns `true` if the given specification ID is enabled in this spec. + #[inline] + pub fn is_enabled_in(self, other: impl Into) -> bool { + match (self, other.into()) { + (OpSpec::Eth(this), OpSpec::Eth(other)) => other as u8 <= this as u8, + (OpSpec::Op(this), OpSpec::Op(other)) => other as u8 <= this as u8, + (OpSpec::Eth(this), OpSpec::Op(other)) => other.into_eth_spec() as u8 <= this as u8, + (OpSpec::Op(this), OpSpec::Eth(other)) => other as u8 <= this.into_eth_spec() as u8, } } -} -impl From for &'static str { - fn from(value: OptimismSpecId) -> Self { - match value { - OptimismSpecId::FRONTIER - | OptimismSpecId::FRONTIER_THAWING - | OptimismSpecId::HOMESTEAD - | OptimismSpecId::DAO_FORK - | OptimismSpecId::TANGERINE - | OptimismSpecId::SPURIOUS_DRAGON - | OptimismSpecId::BYZANTIUM - | OptimismSpecId::CONSTANTINOPLE - | OptimismSpecId::PETERSBURG - | OptimismSpecId::ISTANBUL - | OptimismSpecId::MUIR_GLACIER - | OptimismSpecId::BERLIN - | OptimismSpecId::LONDON - | OptimismSpecId::ARROW_GLACIER - | OptimismSpecId::GRAY_GLACIER - | OptimismSpecId::MERGE - | OptimismSpecId::SHANGHAI - | OptimismSpecId::CANCUN - | OptimismSpecId::PRAGUE - | OptimismSpecId::PRAGUE_EOF => value.into_eth_spec_id().into(), - OptimismSpecId::BEDROCK => id::BEDROCK, - OptimismSpecId::REGOLITH => id::REGOLITH, - OptimismSpecId::CANYON => id::CANYON, - OptimismSpecId::ECOTONE => id::ECOTONE, - OptimismSpecId::FJORD => id::FJORD, - OptimismSpecId::GRANITE => id::GRANITE, - OptimismSpecId::LATEST => id::LATEST, + /// Converts the `OpSpec` into a `SpecId`. + pub const fn into_eth_spec(self) -> SpecId { + match self { + OpSpec::Eth(spec) => spec, + OpSpec::Op(spec) => spec.into_eth_spec(), } } } -pub trait OptimismSpec: Spec + Sized + 'static { - /// The specification ID for optimism. - const OPTIMISM_SPEC_ID: OptimismSpecId; - - /// Returns whether the provided `OptimismSpec` is enabled by this spec. - #[inline] - fn optimism_enabled(spec_id: OptimismSpecId) -> bool { - OptimismSpecId::enabled(Self::OPTIMISM_SPEC_ID, spec_id) - } -} - -macro_rules! spec { - ($spec_id:ident, $spec_name:ident) => { - #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] - pub struct $spec_name; - - impl OptimismSpec for $spec_name { - const OPTIMISM_SPEC_ID: OptimismSpecId = OptimismSpecId::$spec_id; +impl From<&str> for OpSpec { + fn from(name: &str) -> Self { + let eth = SpecId::from(name); + if eth != SpecId::LATEST { + return Self::Eth(eth); } - - impl Spec for $spec_name { - const SPEC_ID: SpecId = $spec_name::OPTIMISM_SPEC_ID.into_eth_spec_id(); + match OpSpecId::try_from(name) { + Ok(op) => Self::Op(op), + Err(_) => Self::Eth(SpecId::LATEST), } - }; + } } -spec!(FRONTIER, FrontierSpec); -// FRONTIER_THAWING no EVM spec change -spec!(HOMESTEAD, HomesteadSpec); -// DAO_FORK no EVM spec change -spec!(TANGERINE, TangerineSpec); -spec!(SPURIOUS_DRAGON, SpuriousDragonSpec); -spec!(BYZANTIUM, ByzantiumSpec); -// CONSTANTINOPLE was overridden with PETERSBURG -spec!(PETERSBURG, PetersburgSpec); -spec!(ISTANBUL, IstanbulSpec); -// MUIR_GLACIER no EVM spec change -spec!(BERLIN, BerlinSpec); -spec!(LONDON, LondonSpec); -// ARROW_GLACIER no EVM spec change -// GRAY_GLACIER no EVM spec change -spec!(MERGE, MergeSpec); -spec!(SHANGHAI, ShanghaiSpec); -spec!(CANCUN, CancunSpec); -spec!(PRAGUE, PragueSpec); -spec!(PRAGUE_EOF, PragueEofSpec); - -spec!(LATEST, LatestSpec); - -// Optimism Hardforks -spec!(BEDROCK, BedrockSpec); -spec!(REGOLITH, RegolithSpec); -spec!(CANYON, CanyonSpec); -spec!(ECOTONE, EcotoneSpec); -spec!(FJORD, FjordSpec); -spec!(GRANITE, GraniteSpec); - -#[macro_export] -macro_rules! optimism_spec_to_generic { - ($spec_id:expr, $e:expr) => {{ - // We are transitioning from var to generic spec. - match $spec_id { - $crate::OptimismSpecId::FRONTIER | $crate::OptimismSpecId::FRONTIER_THAWING => { - use $crate::FrontierSpec as SPEC; - $e - } - $crate::OptimismSpecId::HOMESTEAD | $crate::OptimismSpecId::DAO_FORK => { - use $crate::HomesteadSpec as SPEC; - $e - } - $crate::OptimismSpecId::TANGERINE => { - use $crate::TangerineSpec as SPEC; - $e - } - $crate::OptimismSpecId::SPURIOUS_DRAGON => { - use $crate::SpuriousDragonSpec as SPEC; - $e - } - $crate::OptimismSpecId::BYZANTIUM => { - use $crate::ByzantiumSpec as SPEC; - $e - } - $crate::OptimismSpecId::PETERSBURG | $crate::OptimismSpecId::CONSTANTINOPLE => { - use $crate::PetersburgSpec as SPEC; - $e - } - $crate::OptimismSpecId::ISTANBUL | $crate::OptimismSpecId::MUIR_GLACIER => { - use $crate::IstanbulSpec as SPEC; - $e - } - $crate::OptimismSpecId::BERLIN => { - use $crate::BerlinSpec as SPEC; - $e - } - $crate::OptimismSpecId::LONDON - | $crate::OptimismSpecId::ARROW_GLACIER - | $crate::OptimismSpecId::GRAY_GLACIER => { - use $crate::LondonSpec as SPEC; - $e - } - $crate::OptimismSpecId::MERGE => { - use $crate::MergeSpec as SPEC; - $e - } - $crate::OptimismSpecId::SHANGHAI => { - use $crate::ShanghaiSpec as SPEC; - $e - } - $crate::OptimismSpecId::CANCUN => { - use $crate::CancunSpec as SPEC; - $e - } - $crate::OptimismSpecId::LATEST => { - use $crate::LatestSpec as SPEC; - $e - } - $crate::OptimismSpecId::PRAGUE => { - use $crate::PragueSpec as SPEC; - $e - } - $crate::OptimismSpecId::PRAGUE_EOF => { - use $crate::PragueEofSpec as SPEC; - $e - } - $crate::OptimismSpecId::BEDROCK => { - use $crate::BedrockSpec as SPEC; - $e - } - $crate::OptimismSpecId::REGOLITH => { - use $crate::RegolithSpec as SPEC; - $e - } - $crate::OptimismSpecId::CANYON => { - use $crate::CanyonSpec as SPEC; - $e - } - $crate::OptimismSpecId::GRANITE => { - use $crate::GraniteSpec as SPEC; - $e - } - $crate::OptimismSpecId::ECOTONE => { - use $crate::EcotoneSpec as SPEC; - $e - } - $crate::OptimismSpecId::FJORD => { - use $crate::FjordSpec as SPEC; - $e - } +impl From for &'static str { + fn from(value: OpSpec) -> Self { + match value { + OpSpec::Eth(eth) => eth.into(), + OpSpec::Op(op) => op.into(), } - }}; + } } #[cfg(test)] mod tests { use super::*; - #[test] - fn optimism_spec_to_generic() { - optimism_spec_to_generic!( - OptimismSpecId::FRONTIER, - assert_eq!(SPEC::SPEC_ID, SpecId::FRONTIER) - ); - optimism_spec_to_generic!( - OptimismSpecId::FRONTIER_THAWING, - assert_eq!(SPEC::SPEC_ID, SpecId::FRONTIER) - ); - optimism_spec_to_generic!( - OptimismSpecId::HOMESTEAD, - assert_eq!(SPEC::SPEC_ID, SpecId::HOMESTEAD) - ); - optimism_spec_to_generic!( - OptimismSpecId::DAO_FORK, - assert_eq!(SPEC::SPEC_ID, SpecId::HOMESTEAD) - ); - optimism_spec_to_generic!( - OptimismSpecId::TANGERINE, - assert_eq!(SPEC::SPEC_ID, SpecId::TANGERINE) - ); - optimism_spec_to_generic!( - OptimismSpecId::SPURIOUS_DRAGON, - assert_eq!(SPEC::SPEC_ID, SpecId::SPURIOUS_DRAGON) - ); - optimism_spec_to_generic!( - OptimismSpecId::BYZANTIUM, - assert_eq!(SPEC::SPEC_ID, SpecId::BYZANTIUM) - ); - optimism_spec_to_generic!( - OptimismSpecId::CONSTANTINOPLE, - assert_eq!(SPEC::SPEC_ID, SpecId::PETERSBURG) - ); - optimism_spec_to_generic!( - OptimismSpecId::PETERSBURG, - assert_eq!(SPEC::SPEC_ID, SpecId::PETERSBURG) - ); - optimism_spec_to_generic!( - OptimismSpecId::ISTANBUL, - assert_eq!(SPEC::SPEC_ID, SpecId::ISTANBUL) - ); - optimism_spec_to_generic!( - OptimismSpecId::MUIR_GLACIER, - assert_eq!(SPEC::SPEC_ID, SpecId::ISTANBUL) - ); - optimism_spec_to_generic!( - OptimismSpecId::BERLIN, - assert_eq!(SPEC::SPEC_ID, SpecId::BERLIN) - ); - optimism_spec_to_generic!( - OptimismSpecId::LONDON, - assert_eq!(SPEC::SPEC_ID, SpecId::LONDON) - ); - optimism_spec_to_generic!( - OptimismSpecId::ARROW_GLACIER, - assert_eq!(SPEC::SPEC_ID, SpecId::LONDON) - ); - optimism_spec_to_generic!( - OptimismSpecId::GRAY_GLACIER, - assert_eq!(SPEC::SPEC_ID, SpecId::LONDON) - ); - optimism_spec_to_generic!( - OptimismSpecId::MERGE, - assert_eq!(SPEC::SPEC_ID, SpecId::MERGE) - ); - optimism_spec_to_generic!( - OptimismSpecId::BEDROCK, - assert_eq!(SPEC::SPEC_ID, SpecId::MERGE) - ); - optimism_spec_to_generic!( - OptimismSpecId::REGOLITH, - assert_eq!(SPEC::SPEC_ID, SpecId::MERGE) - ); - optimism_spec_to_generic!( - OptimismSpecId::SHANGHAI, - assert_eq!(SPEC::SPEC_ID, SpecId::SHANGHAI) - ); - optimism_spec_to_generic!( - OptimismSpecId::CANYON, - assert_eq!(SPEC::SPEC_ID, SpecId::SHANGHAI) - ); - optimism_spec_to_generic!( - OptimismSpecId::CANCUN, - assert_eq!(SPEC::SPEC_ID, SpecId::CANCUN) - ); - optimism_spec_to_generic!( - OptimismSpecId::ECOTONE, - assert_eq!(SPEC::SPEC_ID, SpecId::CANCUN) - ); - optimism_spec_to_generic!( - OptimismSpecId::FJORD, - assert_eq!(SPEC::SPEC_ID, SpecId::CANCUN) - ); - optimism_spec_to_generic!( - OptimismSpecId::PRAGUE, - assert_eq!(SPEC::SPEC_ID, SpecId::PRAGUE) - ); - optimism_spec_to_generic!( - OptimismSpecId::LATEST, - assert_eq!(SPEC::SPEC_ID, SpecId::LATEST) - ); - optimism_spec_to_generic!( - OptimismSpecId::FRONTIER, - assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::FRONTIER) - ); - optimism_spec_to_generic!( - OptimismSpecId::FRONTIER_THAWING, - assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::FRONTIER) - ); - optimism_spec_to_generic!( - OptimismSpecId::HOMESTEAD, - assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::HOMESTEAD) - ); - optimism_spec_to_generic!( - OptimismSpecId::DAO_FORK, - assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::HOMESTEAD) - ); - optimism_spec_to_generic!( - OptimismSpecId::TANGERINE, - assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::TANGERINE) - ); - optimism_spec_to_generic!( - OptimismSpecId::SPURIOUS_DRAGON, - assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::SPURIOUS_DRAGON) - ); - optimism_spec_to_generic!( - OptimismSpecId::BYZANTIUM, - assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::BYZANTIUM) - ); - optimism_spec_to_generic!( - OptimismSpecId::CONSTANTINOPLE, - assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::PETERSBURG) - ); - optimism_spec_to_generic!( - OptimismSpecId::PETERSBURG, - assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::PETERSBURG) - ); - optimism_spec_to_generic!( - OptimismSpecId::ISTANBUL, - assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::ISTANBUL) - ); - optimism_spec_to_generic!( - OptimismSpecId::MUIR_GLACIER, - assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::ISTANBUL) - ); - optimism_spec_to_generic!( - OptimismSpecId::BERLIN, - assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::BERLIN) - ); - optimism_spec_to_generic!( - OptimismSpecId::LONDON, - assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::LONDON) - ); - optimism_spec_to_generic!( - OptimismSpecId::ARROW_GLACIER, - assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::LONDON) - ); - optimism_spec_to_generic!( - OptimismSpecId::GRAY_GLACIER, - assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::LONDON) - ); - optimism_spec_to_generic!( - OptimismSpecId::MERGE, - assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::MERGE) - ); - optimism_spec_to_generic!( - OptimismSpecId::BEDROCK, - assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::BEDROCK) - ); - optimism_spec_to_generic!( - OptimismSpecId::REGOLITH, - assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::REGOLITH) - ); - optimism_spec_to_generic!( - OptimismSpecId::SHANGHAI, - assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::SHANGHAI) - ); - optimism_spec_to_generic!( - OptimismSpecId::CANYON, - assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::CANYON) - ); - optimism_spec_to_generic!( - OptimismSpecId::CANCUN, - assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::CANCUN) - ); - optimism_spec_to_generic!( - OptimismSpecId::ECOTONE, - assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::ECOTONE) - ); - optimism_spec_to_generic!( - OptimismSpecId::FJORD, - assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::FJORD) - ); - optimism_spec_to_generic!( - OptimismSpecId::GRANITE, - assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::GRANITE) - ); - optimism_spec_to_generic!( - OptimismSpecId::PRAGUE, - assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::PRAGUE) - ); - optimism_spec_to_generic!( - OptimismSpecId::PRAGUE_EOF, - assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::PRAGUE_EOF) - ); - optimism_spec_to_generic!( - OptimismSpecId::LATEST, - assert_eq!(SPEC::OPTIMISM_SPEC_ID, OptimismSpecId::LATEST) - ); - } - #[test] fn test_bedrock_post_merge_hardforks() { - assert!(BedrockSpec::optimism_enabled(OptimismSpecId::MERGE)); - assert!(!BedrockSpec::optimism_enabled(OptimismSpecId::SHANGHAI)); - assert!(!BedrockSpec::optimism_enabled(OptimismSpecId::CANCUN)); - assert!(!BedrockSpec::optimism_enabled(OptimismSpecId::LATEST)); - assert!(BedrockSpec::optimism_enabled(OptimismSpecId::BEDROCK)); - assert!(!BedrockSpec::optimism_enabled(OptimismSpecId::REGOLITH)); + assert!(OpSpec::Op(OpSpecId::BEDROCK).is_enabled_in(SpecId::MERGE)); + assert!(!OpSpec::Op(OpSpecId::BEDROCK).is_enabled_in(SpecId::SHANGHAI)); + assert!(!OpSpec::Op(OpSpecId::BEDROCK).is_enabled_in(SpecId::CANCUN)); + assert!(!OpSpec::Op(OpSpecId::BEDROCK).is_enabled_in(SpecId::LATEST)); + assert!(OpSpec::Op(OpSpecId::BEDROCK).is_enabled_in(OpSpecId::BEDROCK)); + assert!(!OpSpec::Op(OpSpecId::BEDROCK).is_enabled_in(OpSpecId::REGOLITH)); } #[test] fn test_regolith_post_merge_hardforks() { - assert!(RegolithSpec::optimism_enabled(OptimismSpecId::MERGE)); - assert!(!RegolithSpec::optimism_enabled(OptimismSpecId::SHANGHAI)); - assert!(!RegolithSpec::optimism_enabled(OptimismSpecId::CANCUN)); - assert!(!RegolithSpec::optimism_enabled(OptimismSpecId::LATEST)); - assert!(RegolithSpec::optimism_enabled(OptimismSpecId::BEDROCK)); - assert!(RegolithSpec::optimism_enabled(OptimismSpecId::REGOLITH)); - } - - #[test] - fn test_bedrock_post_merge_hardforks_spec_id() { - assert!(OptimismSpecId::enabled( - OptimismSpecId::BEDROCK, - OptimismSpecId::MERGE - )); - assert!(!OptimismSpecId::enabled( - OptimismSpecId::BEDROCK, - OptimismSpecId::SHANGHAI - )); - assert!(!OptimismSpecId::enabled( - OptimismSpecId::BEDROCK, - OptimismSpecId::CANCUN - )); - assert!(!OptimismSpecId::enabled( - OptimismSpecId::BEDROCK, - OptimismSpecId::LATEST - )); - assert!(OptimismSpecId::enabled( - OptimismSpecId::BEDROCK, - OptimismSpecId::BEDROCK - )); - assert!(!OptimismSpecId::enabled( - OptimismSpecId::BEDROCK, - OptimismSpecId::REGOLITH - )); - } - - #[test] - fn test_regolith_post_merge_hardforks_spec_id() { - assert!(OptimismSpecId::enabled( - OptimismSpecId::REGOLITH, - OptimismSpecId::MERGE - )); - assert!(!OptimismSpecId::enabled( - OptimismSpecId::REGOLITH, - OptimismSpecId::SHANGHAI - )); - assert!(!OptimismSpecId::enabled( - OptimismSpecId::REGOLITH, - OptimismSpecId::CANCUN - )); - assert!(!OptimismSpecId::enabled( - OptimismSpecId::REGOLITH, - OptimismSpecId::LATEST - )); - assert!(OptimismSpecId::enabled( - OptimismSpecId::REGOLITH, - OptimismSpecId::BEDROCK - )); - assert!(OptimismSpecId::enabled( - OptimismSpecId::REGOLITH, - OptimismSpecId::REGOLITH - )); + assert!(OpSpec::Op(OpSpecId::REGOLITH).is_enabled_in(SpecId::MERGE)); + assert!(!OpSpec::Op(OpSpecId::REGOLITH).is_enabled_in(SpecId::SHANGHAI)); + assert!(!OpSpec::Op(OpSpecId::REGOLITH).is_enabled_in(SpecId::CANCUN)); + assert!(!OpSpec::Op(OpSpecId::REGOLITH).is_enabled_in(SpecId::LATEST)); + assert!(OpSpec::Op(OpSpecId::REGOLITH).is_enabled_in(OpSpecId::BEDROCK)); + assert!(OpSpec::Op(OpSpecId::REGOLITH).is_enabled_in(OpSpecId::REGOLITH)); } #[test] fn test_canyon_post_merge_hardforks() { - assert!(CanyonSpec::optimism_enabled(OptimismSpecId::MERGE)); - assert!(CanyonSpec::optimism_enabled(OptimismSpecId::SHANGHAI)); - assert!(!CanyonSpec::optimism_enabled(OptimismSpecId::CANCUN)); - assert!(!CanyonSpec::optimism_enabled(OptimismSpecId::LATEST)); - assert!(CanyonSpec::optimism_enabled(OptimismSpecId::BEDROCK)); - assert!(CanyonSpec::optimism_enabled(OptimismSpecId::REGOLITH)); - assert!(CanyonSpec::optimism_enabled(OptimismSpecId::CANYON)); - } - - #[test] - fn test_canyon_post_merge_hardforks_spec_id() { - assert!(OptimismSpecId::enabled( - OptimismSpecId::CANYON, - OptimismSpecId::MERGE - )); - assert!(OptimismSpecId::enabled( - OptimismSpecId::CANYON, - OptimismSpecId::SHANGHAI - )); - assert!(!OptimismSpecId::enabled( - OptimismSpecId::CANYON, - OptimismSpecId::CANCUN - )); - assert!(!OptimismSpecId::enabled( - OptimismSpecId::CANYON, - OptimismSpecId::LATEST - )); - assert!(OptimismSpecId::enabled( - OptimismSpecId::CANYON, - OptimismSpecId::BEDROCK - )); - assert!(OptimismSpecId::enabled( - OptimismSpecId::CANYON, - OptimismSpecId::REGOLITH - )); - assert!(OptimismSpecId::enabled( - OptimismSpecId::CANYON, - OptimismSpecId::CANYON - )); + assert!(OpSpec::Op(OpSpecId::CANYON).is_enabled_in(SpecId::MERGE)); + assert!(OpSpec::Op(OpSpecId::CANYON).is_enabled_in(SpecId::SHANGHAI)); + assert!(!OpSpec::Op(OpSpecId::CANYON).is_enabled_in(SpecId::CANCUN)); + assert!(!OpSpec::Op(OpSpecId::CANYON).is_enabled_in(SpecId::LATEST)); + assert!(OpSpec::Op(OpSpecId::CANYON).is_enabled_in(OpSpecId::BEDROCK)); + assert!(OpSpec::Op(OpSpecId::CANYON).is_enabled_in(OpSpecId::REGOLITH)); + assert!(OpSpec::Op(OpSpecId::CANYON).is_enabled_in(OpSpecId::CANYON)); } #[test] fn test_ecotone_post_merge_hardforks() { - assert!(EcotoneSpec::optimism_enabled(OptimismSpecId::MERGE)); - assert!(EcotoneSpec::optimism_enabled(OptimismSpecId::SHANGHAI)); - assert!(EcotoneSpec::optimism_enabled(OptimismSpecId::CANCUN)); - assert!(!EcotoneSpec::optimism_enabled(OptimismSpecId::LATEST)); - assert!(EcotoneSpec::optimism_enabled(OptimismSpecId::BEDROCK)); - assert!(EcotoneSpec::optimism_enabled(OptimismSpecId::REGOLITH)); - assert!(EcotoneSpec::optimism_enabled(OptimismSpecId::CANYON)); - assert!(EcotoneSpec::optimism_enabled(OptimismSpecId::ECOTONE)); - } - - #[test] - fn test_ecotone_post_merge_hardforks_spec_id() { - assert!(OptimismSpecId::enabled( - OptimismSpecId::ECOTONE, - OptimismSpecId::MERGE - )); - assert!(OptimismSpecId::enabled( - OptimismSpecId::ECOTONE, - OptimismSpecId::SHANGHAI - )); - assert!(OptimismSpecId::enabled( - OptimismSpecId::ECOTONE, - OptimismSpecId::CANCUN - )); - assert!(!OptimismSpecId::enabled( - OptimismSpecId::ECOTONE, - OptimismSpecId::LATEST - )); - assert!(OptimismSpecId::enabled( - OptimismSpecId::ECOTONE, - OptimismSpecId::BEDROCK - )); - assert!(OptimismSpecId::enabled( - OptimismSpecId::ECOTONE, - OptimismSpecId::REGOLITH - )); - assert!(OptimismSpecId::enabled( - OptimismSpecId::ECOTONE, - OptimismSpecId::CANYON - )); - assert!(OptimismSpecId::enabled( - OptimismSpecId::ECOTONE, - OptimismSpecId::ECOTONE - )); + assert!(OpSpec::Op(OpSpecId::ECOTONE).is_enabled_in(SpecId::MERGE)); + assert!(OpSpec::Op(OpSpecId::ECOTONE).is_enabled_in(SpecId::SHANGHAI)); + assert!(OpSpec::Op(OpSpecId::ECOTONE).is_enabled_in(SpecId::CANCUN)); + assert!(!OpSpec::Op(OpSpecId::ECOTONE).is_enabled_in(SpecId::LATEST)); + assert!(OpSpec::Op(OpSpecId::ECOTONE).is_enabled_in(OpSpecId::BEDROCK)); + assert!(OpSpec::Op(OpSpecId::ECOTONE).is_enabled_in(OpSpecId::REGOLITH)); + assert!(OpSpec::Op(OpSpecId::ECOTONE).is_enabled_in(OpSpecId::CANYON)); + assert!(OpSpec::Op(OpSpecId::ECOTONE).is_enabled_in(OpSpecId::ECOTONE)); } #[test] fn test_fjord_post_merge_hardforks() { - assert!(FjordSpec::optimism_enabled(OptimismSpecId::MERGE)); - assert!(FjordSpec::optimism_enabled(OptimismSpecId::SHANGHAI)); - assert!(FjordSpec::optimism_enabled(OptimismSpecId::CANCUN)); - assert!(!FjordSpec::optimism_enabled(OptimismSpecId::LATEST)); - assert!(FjordSpec::optimism_enabled(OptimismSpecId::BEDROCK)); - assert!(FjordSpec::optimism_enabled(OptimismSpecId::REGOLITH)); - assert!(FjordSpec::optimism_enabled(OptimismSpecId::CANYON)); - assert!(FjordSpec::optimism_enabled(OptimismSpecId::ECOTONE)); - assert!(FjordSpec::optimism_enabled(OptimismSpecId::FJORD)); - } - - #[test] - fn test_fjord_post_merge_hardforks_spec_id() { - assert!(OptimismSpecId::enabled( - OptimismSpecId::FJORD, - OptimismSpecId::MERGE - )); - assert!(OptimismSpecId::enabled( - OptimismSpecId::FJORD, - OptimismSpecId::SHANGHAI - )); - assert!(OptimismSpecId::enabled( - OptimismSpecId::FJORD, - OptimismSpecId::CANCUN - )); - assert!(!OptimismSpecId::enabled( - OptimismSpecId::FJORD, - OptimismSpecId::LATEST - )); - assert!(OptimismSpecId::enabled( - OptimismSpecId::FJORD, - OptimismSpecId::BEDROCK - )); - assert!(OptimismSpecId::enabled( - OptimismSpecId::FJORD, - OptimismSpecId::REGOLITH - )); - assert!(OptimismSpecId::enabled( - OptimismSpecId::FJORD, - OptimismSpecId::CANYON - )); - assert!(OptimismSpecId::enabled( - OptimismSpecId::FJORD, - OptimismSpecId::ECOTONE - )); - assert!(OptimismSpecId::enabled( - OptimismSpecId::FJORD, - OptimismSpecId::FJORD - )); + assert!(OpSpec::Op(OpSpecId::FJORD).is_enabled_in(SpecId::MERGE)); + assert!(OpSpec::Op(OpSpecId::FJORD).is_enabled_in(SpecId::SHANGHAI)); + assert!(OpSpec::Op(OpSpecId::FJORD).is_enabled_in(SpecId::CANCUN)); + assert!(!OpSpec::Op(OpSpecId::FJORD).is_enabled_in(SpecId::LATEST)); + assert!(OpSpec::Op(OpSpecId::FJORD).is_enabled_in(OpSpecId::BEDROCK)); + assert!(OpSpec::Op(OpSpecId::FJORD).is_enabled_in(OpSpecId::REGOLITH)); + assert!(OpSpec::Op(OpSpecId::FJORD).is_enabled_in(OpSpecId::CANYON)); + assert!(OpSpec::Op(OpSpecId::FJORD).is_enabled_in(OpSpecId::ECOTONE)); + assert!(OpSpec::Op(OpSpecId::FJORD).is_enabled_in(OpSpecId::FJORD)); } } diff --git a/crates/optimism/src/transaction/abstraction.rs b/crates/optimism/src/transaction/abstraction.rs index b41997db92..f12d4f632f 100644 --- a/crates/optimism/src/transaction/abstraction.rs +++ b/crates/optimism/src/transaction/abstraction.rs @@ -1,9 +1,13 @@ use super::deposit::{DepositTransaction, TxDeposit}; use crate::OpTransactionError; use revm::{ + context::TxEnv, + context_interface::{ + transaction::{CommonTxFields, Transaction, TransactionType}, + TransactionGetter, + }, primitives::Bytes, - transaction::{CommonTxFields, Transaction, TransactionType}, - wiring::default::TxEnv, + Context, Database, }; pub trait OpTxTrait: Transaction { @@ -14,6 +18,22 @@ pub trait OpTxTrait: Transaction { fn enveloped_tx(&self) -> Option<&Bytes>; } +pub trait OpTxGetter: TransactionGetter { + type OpTransaction: OpTxTrait; + + fn op_tx(&self) -> &Self::OpTransaction; +} + +impl OpTxGetter + for Context, CFG, DB, CHAIN> +{ + type OpTransaction = OpTransaction; + + fn op_tx(&self) -> &Self::OpTransaction { + &self.tx + } +} + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum OpTransactionType { diff --git a/crates/optimism/src/transaction/deposit.rs b/crates/optimism/src/transaction/deposit.rs index 41c71ce395..72ce082346 100644 --- a/crates/optimism/src/transaction/deposit.rs +++ b/crates/optimism/src/transaction/deposit.rs @@ -1,6 +1,6 @@ use revm::{ + context_interface::transaction::CommonTxFields, primitives::{Address, Bytes, TxKind, B256, U256}, - transaction::CommonTxFields, }; pub trait DepositTransaction: CommonTxFields { diff --git a/crates/optimism/src/transaction/error.rs b/crates/optimism/src/transaction/error.rs index 6cd8144bb4..0b3cd09187 100644 --- a/crates/optimism/src/transaction/error.rs +++ b/crates/optimism/src/transaction/error.rs @@ -1,7 +1,7 @@ use core::fmt::Display; -use revm::{ +use revm::context_interface::{ + result::{EVMError, InvalidTransaction}, transaction::TransactionError, - wiring::result::{EVMError, InvalidTransaction}, }; /// Optimism transaction validation error. diff --git a/crates/optimism/src/wiring.rs b/crates/optimism/src/wiring.rs deleted file mode 100644 index da19e42dd6..0000000000 --- a/crates/optimism/src/wiring.rs +++ /dev/null @@ -1,92 +0,0 @@ -use crate::{ - optimism_handle_register, - transaction::{OpTransaction, OpTransactionType, OpTxTrait}, - L1BlockInfo, OpTransactionError, OptimismHaltReason, OptimismSpecId, -}; -use core::marker::PhantomData; -use revm::{ - database_interface::Database, - handler::register::HandleRegisters, - wiring::default::{block::BlockEnv, TxEnv}, - wiring::EvmWiring, - EvmHandler, -}; - -pub trait OptimismContextTrait { - /// A reference to the cached L1 block info. - fn l1_block_info(&self) -> Option<&L1BlockInfo>; - - /// A mutable reference to the cached L1 block info. - fn l1_block_info_mut(&mut self) -> &mut Option; -} - -/// Trait for an Optimism chain spec. -pub trait OptimismWiring: - revm::EvmWiring< - ChainContext: OptimismContextTrait, - Hardfork = OptimismSpecId, - HaltReason = OptimismHaltReason, - Transaction: OpTxTrait< - TransactionType = OpTransactionType, - TransactionError = OpTransactionError, - >, -> -{ -} - -impl OptimismWiring for EvmWiringT where - EvmWiringT: revm::EvmWiring< - ChainContext: OptimismContextTrait, - Hardfork = OptimismSpecId, - HaltReason = OptimismHaltReason, - Transaction: OpTxTrait< - TransactionType = OpTransactionType, - TransactionError = OpTransactionError, - >, - > -{ -} - -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct OptimismEvmWiring { - _phantom: PhantomData<(DB, EXT)>, -} - -impl EvmWiring for OptimismEvmWiring { - type Block = BlockEnv; - type Database = DB; - type ChainContext = Context; - type ExternalContext = EXT; - type Hardfork = OptimismSpecId; - type HaltReason = OptimismHaltReason; - type Transaction = OpTransaction; -} - -impl revm::EvmWiring for OptimismEvmWiring { - fn handler<'evm>(hardfork: Self::Hardfork) -> EvmHandler<'evm, Self> - where - DB: Database, - { - let mut handler = EvmHandler::mainnet_with_spec(hardfork); - - handler.append_handler_register(HandleRegisters::Plain(optimism_handle_register::)); - - handler - } -} - -/// Context for the Optimism chain. -#[derive(Clone, Default, Debug, PartialEq, Eq)] -pub struct Context { - l1_block_info: Option, -} - -impl OptimismContextTrait for Context { - fn l1_block_info(&self) -> Option<&L1BlockInfo> { - self.l1_block_info.as_ref() - } - - fn l1_block_info_mut(&mut self) -> &mut Option { - &mut self.l1_block_info - } -} diff --git a/crates/precompile/Cargo.toml b/crates/precompile/Cargo.toml index fa4691dffd..e2445b2a03 100644 --- a/crates/precompile/Cargo.toml +++ b/crates/precompile/Cargo.toml @@ -24,7 +24,7 @@ all = "warn" [dependencies] # revm primitives.workspace = true -wiring.workspace = true +context-interface.workspace = true specification.workspace = true # static precompile sets. @@ -67,7 +67,6 @@ p256 = { version = "0.13.2", optional = true, default-features = false, features # utils cfg-if = { version = "1.0", default-features = false } -dyn-clone = "1.0" [dev-dependencies] criterion = "0.5" @@ -98,11 +97,11 @@ secp256r1 = ["dep:p256"] # These libraries may not work on all no_std platforms as they depend on C. # Enables the KZG point evaluation precompile. -c-kzg = ["dep:c-kzg", "wiring/c-kzg"] +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", "wiring/kzg-rs"] +kzg-rs = ["dep:kzg-rs"] -portable = ["c-kzg", "c-kzg?/portable"] +portable = ["c-kzg"] # Use `secp256k1` as a faster alternative to `k256`. # The problem that `secp256k1` has is it fails to build for `wasm` target on Windows and Mac as it is c lib. diff --git a/crates/precompile/benches/bench.rs b/crates/precompile/benches/bench.rs index 07c603c2c0..38513a68fd 100644 --- a/crates/precompile/benches/bench.rs +++ b/crates/precompile/benches/bench.rs @@ -13,7 +13,6 @@ use revm_precompile::{ use secp256k1::{Message, SecretKey, SECP256K1}; use sha2::{Digest, Sha256}; use specification::eip4844::VERSIONED_HASH_VERSION_KZG; -use wiring::default::CfgEnv; /// Benchmarks different cryptography-related precompiles. pub fn benchmark_crypto_precompiles(c: &mut Criterion) { @@ -108,8 +107,7 @@ pub fn benchmark_crypto_precompiles(c: &mut Criterion) { let kzg_input = [versioned_hash, z, y, commitment, proof].concat().into(); let gas = 50000; - let env = CfgEnv::default(); - let output = run(&kzg_input, gas, &env).unwrap(); + let output = run(&kzg_input, gas).unwrap(); println!("gas used by kzg precompile: {:?}", output.gas_used); group.bench_function(group_name("ecrecover precompile"), |b| { @@ -141,7 +139,7 @@ pub fn benchmark_crypto_precompiles(c: &mut Criterion) { group.bench_function(group_name("kzg precompile"), |b| { b.iter(|| { - run(&kzg_input, gas, &env).unwrap(); + run(&kzg_input, gas).unwrap(); black_box(()) }) }); diff --git a/crates/precompile/src/blake2.rs b/crates/precompile/src/blake2.rs index bf22b019a7..16b5bce77b 100644 --- a/crates/precompile/src/blake2.rs +++ b/crates/precompile/src/blake2.rs @@ -1,13 +1,10 @@ -use crate::{ - Precompile, PrecompileError, PrecompileOutput, PrecompileResult, PrecompileWithAddress, -}; +use crate::{PrecompileError, PrecompileOutput, PrecompileResult, PrecompileWithAddress}; use primitives::Bytes; const F_ROUND: u64 = 1; const INPUT_LENGTH: usize = 213; -pub const FUN: PrecompileWithAddress = - PrecompileWithAddress(crate::u64_to_address(9), Precompile::Standard(run)); +pub const FUN: PrecompileWithAddress = PrecompileWithAddress(crate::u64_to_address(9), run); /// reference: /// input format: diff --git a/crates/precompile/src/bls12_381/g1_add.rs b/crates/precompile/src/bls12_381/g1_add.rs index f1b4d628c1..d746cc0d78 100644 --- a/crates/precompile/src/bls12_381/g1_add.rs +++ b/crates/precompile/src/bls12_381/g1_add.rs @@ -1,6 +1,6 @@ use super::g1::{encode_g1_point, extract_g1_input, G1_INPUT_ITEM_LENGTH}; use crate::{u64_to_address, PrecompileWithAddress}; -use crate::{Precompile, PrecompileError, PrecompileOutput, PrecompileResult}; +use crate::{PrecompileError, PrecompileOutput, PrecompileResult}; use blst::{ blst_p1, blst_p1_add_or_double_affine, blst_p1_affine, blst_p1_from_affine, blst_p1_to_affine, }; @@ -8,7 +8,7 @@ use primitives::Bytes; /// [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537#specification) BLS12_G1ADD precompile. pub const PRECOMPILE: PrecompileWithAddress = - PrecompileWithAddress(u64_to_address(ADDRESS), Precompile::Standard(g1_add)); + PrecompileWithAddress(u64_to_address(ADDRESS), g1_add); /// BLS12_G1ADD precompile address. pub const ADDRESS: u64 = 0x0b; /// Base gas fee for BLS12-381 g1_add operation. diff --git a/crates/precompile/src/bls12_381/g1_msm.rs b/crates/precompile/src/bls12_381/g1_msm.rs index 0d1d4cdadb..4573394bd6 100644 --- a/crates/precompile/src/bls12_381/g1_msm.rs +++ b/crates/precompile/src/bls12_381/g1_msm.rs @@ -5,13 +5,13 @@ use super::{ utils::{extract_scalar_input, NBITS, SCALAR_LENGTH}, }; use crate::{u64_to_address, PrecompileWithAddress}; -use crate::{Precompile, PrecompileError, PrecompileOutput, PrecompileResult}; +use crate::{PrecompileError, PrecompileOutput, PrecompileResult}; use blst::{blst_p1, blst_p1_affine, blst_p1_from_affine, blst_p1_to_affine, p1_affines}; use primitives::Bytes; /// [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537#specification) BLS12_G1MSM precompile. pub const PRECOMPILE: PrecompileWithAddress = - PrecompileWithAddress(u64_to_address(ADDRESS), Precompile::Standard(g1_msm)); + PrecompileWithAddress(u64_to_address(ADDRESS), g1_msm); /// BLS12_G1MSM precompile address. pub const ADDRESS: u64 = 0x0d; diff --git a/crates/precompile/src/bls12_381/g1_mul.rs b/crates/precompile/src/bls12_381/g1_mul.rs index fbc3b28013..2458de7c37 100644 --- a/crates/precompile/src/bls12_381/g1_mul.rs +++ b/crates/precompile/src/bls12_381/g1_mul.rs @@ -3,13 +3,13 @@ use super::{ utils::{extract_scalar_input, NBITS}, }; use crate::{u64_to_address, PrecompileWithAddress}; -use crate::{Precompile, PrecompileError, PrecompileOutput, PrecompileResult}; +use crate::{PrecompileError, PrecompileOutput, PrecompileResult}; use blst::{blst_p1, blst_p1_affine, blst_p1_from_affine, blst_p1_mult, blst_p1_to_affine}; use primitives::Bytes; /// [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537#specification) BLS12_G1MUL precompile. pub const PRECOMPILE: PrecompileWithAddress = - PrecompileWithAddress(u64_to_address(ADDRESS), Precompile::Standard(g1_mul)); + PrecompileWithAddress(u64_to_address(ADDRESS), g1_mul); /// BLS12_G1MUL precompile address. pub const ADDRESS: u64 = 0x0c; /// Base gas fee for BLS12-381 g1_mul operation. diff --git a/crates/precompile/src/bls12_381/g2_add.rs b/crates/precompile/src/bls12_381/g2_add.rs index 6fd03df38b..f98f7345b7 100644 --- a/crates/precompile/src/bls12_381/g2_add.rs +++ b/crates/precompile/src/bls12_381/g2_add.rs @@ -1,6 +1,6 @@ use super::g2::{encode_g2_point, extract_g2_input, G2_INPUT_ITEM_LENGTH}; use crate::{u64_to_address, PrecompileWithAddress}; -use crate::{Precompile, PrecompileError, PrecompileOutput, PrecompileResult}; +use crate::{PrecompileError, PrecompileOutput, PrecompileResult}; use blst::{ blst_p2, blst_p2_add_or_double_affine, blst_p2_affine, blst_p2_from_affine, blst_p2_to_affine, }; @@ -8,7 +8,7 @@ use primitives::Bytes; /// [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537#specification) BLS12_G2ADD precompile. pub const PRECOMPILE: PrecompileWithAddress = - PrecompileWithAddress(u64_to_address(ADDRESS), Precompile::Standard(g2_add)); + PrecompileWithAddress(u64_to_address(ADDRESS), g2_add); /// BLS12_G2ADD precompile address. pub const ADDRESS: u64 = 0x0e; /// Base gas fee for BLS12-381 g2_add operation. diff --git a/crates/precompile/src/bls12_381/g2_msm.rs b/crates/precompile/src/bls12_381/g2_msm.rs index b28be86543..675bf540f4 100644 --- a/crates/precompile/src/bls12_381/g2_msm.rs +++ b/crates/precompile/src/bls12_381/g2_msm.rs @@ -5,13 +5,13 @@ use super::{ utils::{extract_scalar_input, NBITS, SCALAR_LENGTH}, }; use crate::{u64_to_address, PrecompileWithAddress}; -use crate::{Precompile, PrecompileError, PrecompileOutput, PrecompileResult}; +use crate::{PrecompileError, PrecompileOutput, PrecompileResult}; use blst::{blst_p2, blst_p2_affine, blst_p2_from_affine, blst_p2_to_affine, p2_affines}; use primitives::Bytes; /// [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537#specification) BLS12_G2MSM precompile. pub const PRECOMPILE: PrecompileWithAddress = - PrecompileWithAddress(u64_to_address(ADDRESS), Precompile::Standard(g2_msm)); + PrecompileWithAddress(u64_to_address(ADDRESS), g2_msm); /// BLS12_G2MSM precompile address. pub const ADDRESS: u64 = 0x10; diff --git a/crates/precompile/src/bls12_381/g2_mul.rs b/crates/precompile/src/bls12_381/g2_mul.rs index e64af370a5..b6a5fdf4b2 100644 --- a/crates/precompile/src/bls12_381/g2_mul.rs +++ b/crates/precompile/src/bls12_381/g2_mul.rs @@ -3,13 +3,13 @@ use super::{ utils::{extract_scalar_input, NBITS}, }; use crate::{u64_to_address, PrecompileWithAddress}; -use crate::{Precompile, PrecompileError, PrecompileOutput, PrecompileResult}; +use crate::{PrecompileError, PrecompileOutput, PrecompileResult}; use blst::{blst_p2, blst_p2_affine, blst_p2_from_affine, blst_p2_mult, blst_p2_to_affine}; use primitives::Bytes; /// [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537#specification) BLS12_G2MUL precompile. pub const PRECOMPILE: PrecompileWithAddress = - PrecompileWithAddress(u64_to_address(ADDRESS), Precompile::Standard(g2_mul)); + PrecompileWithAddress(u64_to_address(ADDRESS), g2_mul); /// BLS12_G2MUL precompile address. pub const ADDRESS: u64 = 0x0f; /// Base gas fee for BLS12-381 g2_mul operation. 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 8abdcfd925..803cd25d74 100644 --- a/crates/precompile/src/bls12_381/map_fp2_to_g2.rs +++ b/crates/precompile/src/bls12_381/map_fp2_to_g2.rs @@ -4,13 +4,13 @@ use super::{ utils::{remove_padding, PADDED_FP2_LENGTH, PADDED_FP_LENGTH}, }; use crate::{u64_to_address, PrecompileWithAddress}; -use crate::{Precompile, PrecompileError, PrecompileOutput, PrecompileResult}; +use crate::{PrecompileError, PrecompileOutput, PrecompileResult}; use blst::{blst_map_to_g2, blst_p2, blst_p2_affine, blst_p2_to_affine}; use primitives::Bytes; /// [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537#specification) BLS12_MAP_FP2_TO_G2 precompile. pub const PRECOMPILE: PrecompileWithAddress = - PrecompileWithAddress(u64_to_address(ADDRESS), Precompile::Standard(map_fp2_to_g2)); + PrecompileWithAddress(u64_to_address(ADDRESS), map_fp2_to_g2); /// BLS12_MAP_FP2_TO_G2 precompile address. pub const ADDRESS: u64 = 0x13; 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 330d399622..7bc66c829f 100644 --- a/crates/precompile/src/bls12_381/map_fp_to_g1.rs +++ b/crates/precompile/src/bls12_381/map_fp_to_g1.rs @@ -3,13 +3,13 @@ use super::{ utils::{fp_from_bendian, remove_padding, PADDED_FP_LENGTH}, }; use crate::{u64_to_address, PrecompileWithAddress}; -use crate::{Precompile, PrecompileError, PrecompileOutput, PrecompileResult}; +use crate::{PrecompileError, PrecompileOutput, PrecompileResult}; use blst::{blst_map_to_g1, blst_p1, blst_p1_affine, blst_p1_to_affine}; use primitives::Bytes; /// [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537#specification) BLS12_MAP_FP_TO_G1 precompile. pub const PRECOMPILE: PrecompileWithAddress = - PrecompileWithAddress(u64_to_address(ADDRESS), Precompile::Standard(map_fp_to_g1)); + PrecompileWithAddress(u64_to_address(ADDRESS), map_fp_to_g1); /// BLS12_MAP_FP_TO_G1 precompile address. pub const ADDRESS: u64 = 0x12; diff --git a/crates/precompile/src/bls12_381/pairing.rs b/crates/precompile/src/bls12_381/pairing.rs index a54af3dc76..becdd23009 100644 --- a/crates/precompile/src/bls12_381/pairing.rs +++ b/crates/precompile/src/bls12_381/pairing.rs @@ -3,15 +3,14 @@ use super::{ g2::{extract_g2_input, G2_INPUT_ITEM_LENGTH}, }; use crate::{ - u64_to_address, Precompile, PrecompileError, PrecompileOutput, PrecompileResult, - PrecompileWithAddress, + u64_to_address, PrecompileError, PrecompileOutput, PrecompileResult, PrecompileWithAddress, }; use blst::{blst_final_exp, blst_fp12, blst_fp12_is_one, blst_fp12_mul, blst_miller_loop}; use primitives::{Bytes, B256}; /// [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537#specification) BLS12_PAIRING precompile. pub const PRECOMPILE: PrecompileWithAddress = - PrecompileWithAddress(u64_to_address(ADDRESS), Precompile::Standard(pairing)); + PrecompileWithAddress(u64_to_address(ADDRESS), pairing); /// BLS12_PAIRING precompile address. pub const ADDRESS: u64 = 0x11; diff --git a/crates/precompile/src/bn128.rs b/crates/precompile/src/bn128.rs index c98ada1d2c..b6622f3ffb 100644 --- a/crates/precompile/src/bn128.rs +++ b/crates/precompile/src/bn128.rs @@ -1,7 +1,6 @@ use crate::{ utilities::{bool_to_bytes32, right_pad}, - Address, Precompile, PrecompileError, PrecompileOutput, PrecompileResult, - PrecompileWithAddress, + Address, PrecompileError, PrecompileOutput, PrecompileResult, PrecompileWithAddress, }; use bn::{AffineG1, AffineG2, Fq, Fq2, Group, Gt, G1, G2}; use std::vec::Vec; @@ -12,16 +11,16 @@ pub mod add { const ADDRESS: Address = crate::u64_to_address(6); pub const ISTANBUL_ADD_GAS_COST: u64 = 150; - pub const ISTANBUL: PrecompileWithAddress = PrecompileWithAddress( - ADDRESS, - Precompile::Standard(|input, gas_limit| run_add(input, ISTANBUL_ADD_GAS_COST, gas_limit)), - ); + pub const ISTANBUL: PrecompileWithAddress = + PrecompileWithAddress(ADDRESS, |input, gas_limit| { + run_add(input, ISTANBUL_ADD_GAS_COST, gas_limit) + }); pub const BYZANTIUM_ADD_GAS_COST: u64 = 500; - pub const BYZANTIUM: PrecompileWithAddress = PrecompileWithAddress( - ADDRESS, - Precompile::Standard(|input, gas_limit| run_add(input, BYZANTIUM_ADD_GAS_COST, gas_limit)), - ); + pub const BYZANTIUM: PrecompileWithAddress = + PrecompileWithAddress(ADDRESS, |input, gas_limit| { + run_add(input, BYZANTIUM_ADD_GAS_COST, gas_limit) + }); } pub mod mul { @@ -30,16 +29,16 @@ pub mod mul { const ADDRESS: Address = crate::u64_to_address(7); pub const ISTANBUL_MUL_GAS_COST: u64 = 6_000; - pub const ISTANBUL: PrecompileWithAddress = PrecompileWithAddress( - ADDRESS, - Precompile::Standard(|input, gas_limit| run_mul(input, ISTANBUL_MUL_GAS_COST, gas_limit)), - ); + pub const ISTANBUL: PrecompileWithAddress = + PrecompileWithAddress(ADDRESS, |input, gas_limit| { + run_mul(input, ISTANBUL_MUL_GAS_COST, gas_limit) + }); pub const BYZANTIUM_MUL_GAS_COST: u64 = 40_000; - pub const BYZANTIUM: PrecompileWithAddress = PrecompileWithAddress( - ADDRESS, - Precompile::Standard(|input, gas_limit| run_mul(input, BYZANTIUM_MUL_GAS_COST, gas_limit)), - ); + pub const BYZANTIUM: PrecompileWithAddress = + PrecompileWithAddress(ADDRESS, |input, gas_limit| { + run_mul(input, BYZANTIUM_MUL_GAS_COST, gas_limit) + }); } pub mod pair { @@ -49,31 +48,27 @@ pub mod pair { pub const ISTANBUL_PAIR_PER_POINT: u64 = 34_000; pub const ISTANBUL_PAIR_BASE: u64 = 45_000; - pub const ISTANBUL: PrecompileWithAddress = PrecompileWithAddress( - ADDRESS, - Precompile::Standard(|input, gas_limit| { + pub const ISTANBUL: PrecompileWithAddress = + PrecompileWithAddress(ADDRESS, |input, gas_limit| { run_pair( input, ISTANBUL_PAIR_PER_POINT, ISTANBUL_PAIR_BASE, gas_limit, ) - }), - ); + }); pub const BYZANTIUM_PAIR_PER_POINT: u64 = 80_000; pub const BYZANTIUM_PAIR_BASE: u64 = 100_000; - pub const BYZANTIUM: PrecompileWithAddress = PrecompileWithAddress( - ADDRESS, - Precompile::Standard(|input, gas_limit| { + pub const BYZANTIUM: PrecompileWithAddress = + PrecompileWithAddress(ADDRESS, |input, gas_limit| { run_pair( input, BYZANTIUM_PAIR_PER_POINT, BYZANTIUM_PAIR_BASE, gas_limit, ) - }), - ); + }); } /// Input length for the add operation. diff --git a/crates/precompile/src/fatal_precompile.rs b/crates/precompile/src/fatal_precompile.rs deleted file mode 100644 index 67780caec6..0000000000 --- a/crates/precompile/src/fatal_precompile.rs +++ /dev/null @@ -1,36 +0,0 @@ -use crate::primitives::{Address, Bytes}; -use crate::{ - Precompile, PrecompileErrors, PrecompileResult, PrecompileWithAddress, StatefulPrecompile, -}; -use std::{string::String, sync::Arc}; -use wiring::default::CfgEnv; - -/// Disable kzg precompile. This will return Fatal error on precompile call -pub fn fatal_precompile(address: Address, msg: String) -> PrecompileWithAddress { - PrecompileWithAddress(address, FatalPrecompile::new_precompile(msg)) -} - -/// Fatal precompile that returns Fatal error on precompile call -pub struct FatalPrecompile { - msg: String, -} - -impl FatalPrecompile { - /// Create a new fatal precompile - pub fn new(msg: String) -> Self { - Self { msg } - } - - /// Create a new stateful fatal precompile - pub fn new_precompile(msg: String) -> Precompile { - Precompile::Stateful(Arc::new(Self::new(msg))) - } -} - -impl StatefulPrecompile for FatalPrecompile { - fn call(&self, _: &Bytes, _: u64, _: &CfgEnv) -> PrecompileResult { - Err(PrecompileErrors::Fatal { - msg: self.msg.clone(), - }) - } -} diff --git a/crates/precompile/src/hash.rs b/crates/precompile/src/hash.rs index cad78606d6..2e16f1c4b0 100644 --- a/crates/precompile/src/hash.rs +++ b/crates/precompile/src/hash.rs @@ -1,17 +1,13 @@ use super::calc_linear_cost_u32; -use crate::{ - Precompile, PrecompileError, PrecompileOutput, PrecompileResult, PrecompileWithAddress, -}; +use crate::{PrecompileError, PrecompileOutput, PrecompileResult, PrecompileWithAddress}; use primitives::Bytes; use sha2::Digest; pub const SHA256: PrecompileWithAddress = - PrecompileWithAddress(crate::u64_to_address(2), Precompile::Standard(sha256_run)); + PrecompileWithAddress(crate::u64_to_address(2), sha256_run); -pub const RIPEMD160: PrecompileWithAddress = PrecompileWithAddress( - crate::u64_to_address(3), - Precompile::Standard(ripemd160_run), -); +pub const RIPEMD160: PrecompileWithAddress = + PrecompileWithAddress(crate::u64_to_address(3), ripemd160_run); /// Computes the SHA-256 hash of the input data. /// diff --git a/crates/precompile/src/identity.rs b/crates/precompile/src/identity.rs index 420813a0ab..a4b5d2a9c8 100644 --- a/crates/precompile/src/identity.rs +++ b/crates/precompile/src/identity.rs @@ -1,11 +1,9 @@ use super::calc_linear_cost_u32; -use crate::{ - Precompile, PrecompileError, PrecompileOutput, PrecompileResult, PrecompileWithAddress, -}; +use crate::{PrecompileError, PrecompileOutput, PrecompileResult, PrecompileWithAddress}; use primitives::Bytes; pub const FUN: PrecompileWithAddress = - PrecompileWithAddress(crate::u64_to_address(4), Precompile::Standard(identity_run)); + PrecompileWithAddress(crate::u64_to_address(4), identity_run); /// The base cost of the operation. pub const IDENTITY_BASE: u64 = 15; diff --git a/crates/precompile/src/interface.rs b/crates/precompile/src/interface.rs index c73e89280e..1a418b76d5 100644 --- a/crates/precompile/src/interface.rs +++ b/crates/precompile/src/interface.rs @@ -1,8 +1,7 @@ +use context_interface::result::EVMError; use core::fmt; -use dyn_clone::DynClone; use primitives::Bytes; -use std::{boxed::Box, string::String, sync::Arc}; -use wiring::default::CfgEnv; +use std::string::{String, ToString}; /// A precompile operation result. /// @@ -25,114 +24,7 @@ impl PrecompileOutput { } } -pub type StandardPrecompileFn = fn(&Bytes, u64) -> PrecompileResult; -pub type EnvPrecompileFn = fn(&Bytes, u64, env: &CfgEnv) -> PrecompileResult; - -/// Stateful precompile trait. It is used to create -/// a arc precompile Precompile::Stateful. -pub trait StatefulPrecompile: Sync + Send { - fn call(&self, bytes: &Bytes, gas_limit: u64, env: &CfgEnv) -> PrecompileResult; -} - -/// Mutable stateful precompile trait. It is used to create -/// a boxed precompile in Precompile::StatefulMut. -pub trait StatefulPrecompileMut: DynClone + Send + Sync { - fn call_mut(&mut self, bytes: &Bytes, gas_limit: u64, env: &CfgEnv) -> PrecompileResult; -} - -dyn_clone::clone_trait_object!(StatefulPrecompileMut); - -/// Arc over stateful precompile. -pub type StatefulPrecompileArc = Arc; - -/// Box over mutable stateful precompile -pub type StatefulPrecompileBox = Box; - -/// Precompile and its handlers. -#[derive(Clone)] -pub enum Precompile { - /// Standard simple precompile that takes input and gas limit. - Standard(StandardPrecompileFn), - /// Similar to Standard but takes reference to [`CfgEnv`]. - Env(EnvPrecompileFn), - /// Stateful precompile that is Arc over [`StatefulPrecompile`] trait. - /// It takes a reference to input, gas limit and [`CfgEnv`]. - Stateful(StatefulPrecompileArc), - /// Mutable stateful precompile that is Box over [`StatefulPrecompileMut`] trait. - /// It takes a reference to input, gas limit and [`CfgEnv`]. - StatefulMut(StatefulPrecompileBox), -} - -impl From for Precompile { - fn from(p: StandardPrecompileFn) -> Self { - Precompile::Standard(p) - } -} - -impl From for Precompile { - fn from(p: EnvPrecompileFn) -> Self { - Precompile::Env(p) - } -} - -impl From for Precompile { - fn from(p: StatefulPrecompileArc) -> Self { - Precompile::Stateful(p) - } -} - -impl From for Precompile { - fn from(p: StatefulPrecompileBox) -> Self { - Precompile::StatefulMut(p) - } -} - -impl fmt::Debug for Precompile { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Precompile::Standard(_) => f.write_str("Standard"), - Precompile::Env(_) => f.write_str("Env"), - Precompile::Stateful(_) => f.write_str("Stateful"), - Precompile::StatefulMut(_) => f.write_str("StatefulMut"), - } - } -} - -impl Precompile { - /// Create a new stateful precompile. - pub fn new_stateful(p: P) -> Self { - Self::Stateful(Arc::new(p)) - } - - /// Create a new mutable stateful precompile. - pub fn new_stateful_mut(p: P) -> Self { - Self::StatefulMut(Box::new(p)) - } - - /// Call the precompile with the given input and gas limit and return the result. - pub fn call(&mut self, bytes: &Bytes, gas_limit: u64, env: &CfgEnv) -> PrecompileResult { - match *self { - Precompile::Standard(p) => p(bytes, gas_limit), - Precompile::Env(p) => p(bytes, gas_limit, env), - Precompile::Stateful(ref p) => p.call(bytes, gas_limit, env), - Precompile::StatefulMut(ref mut p) => p.call_mut(bytes, gas_limit, env), - } - } - - /// Call the precompile with the given input and gas limit and return the result. - /// - /// Returns an error if the precompile is mutable. - pub fn call_ref(&self, bytes: &Bytes, gas_limit: u64, env: &CfgEnv) -> PrecompileResult { - match *self { - Precompile::Standard(p) => p(bytes, gas_limit), - Precompile::Env(p) => p(bytes, gas_limit, env), - Precompile::Stateful(ref p) => p.call(bytes, gas_limit, env), - Precompile::StatefulMut(_) => Err(PrecompileErrors::Fatal { - msg: "call_ref on mutable stateful precompile".into(), - }), - } - } -} +pub type PrecompileFn = fn(&Bytes, u64) -> PrecompileResult; /// Precompile errors. #[derive(Clone, Debug, PartialEq, Eq, Hash)] @@ -141,6 +33,12 @@ pub enum PrecompileErrors { Fatal { msg: String }, } +impl From for EVMError { + fn from(value: PrecompileErrors) -> Self { + Self::Precompile(value.to_string()) + } +} + impl core::error::Error for PrecompileErrors {} impl fmt::Display for PrecompileErrors { @@ -218,33 +116,3 @@ impl fmt::Display for PrecompileError { f.write_str(s) } } - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn stateful_precompile_mut() { - #[derive(Default, Clone)] - struct MyPrecompile {} - - impl StatefulPrecompileMut for MyPrecompile { - fn call_mut( - &mut self, - _bytes: &Bytes, - _gas_limit: u64, - _env: &CfgEnv, - ) -> PrecompileResult { - Err(PrecompileError::OutOfGas.into()) - } - } - - let mut p = Precompile::new_stateful_mut(MyPrecompile::default()); - match &mut p { - Precompile::StatefulMut(p) => { - let _ = p.call_mut(&Bytes::new(), 0, &CfgEnv::default()); - } - _ => panic!("not a state"), - } - } -} diff --git a/crates/precompile/src/kzg_point_evaluation.rs b/crates/precompile/src/kzg_point_evaluation.rs index 37eea7061d..0f11062c0d 100644 --- a/crates/precompile/src/kzg_point_evaluation.rs +++ b/crates/precompile/src/kzg_point_evaluation.rs @@ -1,19 +1,15 @@ -use crate::{ - Address, Precompile, PrecompileError, PrecompileOutput, PrecompileResult, PrecompileWithAddress, -}; +use crate::{Address, PrecompileError, PrecompileOutput, PrecompileResult, PrecompileWithAddress}; cfg_if::cfg_if! { if #[cfg(feature = "c-kzg")] { - use c_kzg::{Bytes32, Bytes48, KzgProof, KzgSettings}; + use c_kzg::{Bytes32, Bytes48, KzgProof}; } else if #[cfg(feature = "kzg-rs")] { - use kzg_rs::{Bytes32, Bytes48, KzgProof, KzgSettings}; + use kzg_rs::{Bytes32, Bytes48, KzgProof}; } } use primitives::{hex_literal::hex, Bytes}; use sha2::{Digest, Sha256}; -use wiring::default::CfgEnv; -pub const POINT_EVALUATION: PrecompileWithAddress = - PrecompileWithAddress(ADDRESS, Precompile::Env(run)); +pub const POINT_EVALUATION: PrecompileWithAddress = PrecompileWithAddress(ADDRESS, run); pub const ADDRESS: Address = crate::u64_to_address(0x0A); pub const GAS_COST: u64 = 50_000; @@ -33,7 +29,7 @@ pub const RETURN_VALUE: &[u8; 64] = &hex!( /// | versioned_hash | z | y | commitment | proof | /// | 32 | 32 | 32 | 48 | 48 | /// with z and y being padded 32 byte big endian values -pub fn run(input: &Bytes, gas_limit: u64, cfg: &CfgEnv) -> PrecompileResult { +pub fn run(input: &Bytes, gas_limit: u64) -> PrecompileResult { if gas_limit < GAS_COST { return Err(PrecompileError::OutOfGas.into()); } @@ -55,7 +51,7 @@ pub fn run(input: &Bytes, gas_limit: u64, cfg: &CfgEnv) -> PrecompileResult { let z = as_bytes32(&input[32..64]); let y = as_bytes32(&input[64..96]); let proof = as_bytes48(&input[144..192]); - if !verify_kzg_proof(commitment, z, y, proof, cfg.kzg_settings.get()) { + if !verify_kzg_proof(commitment, z, y, proof) { return Err(PrecompileError::BlobVerifyKzgProofFailed.into()); } @@ -72,13 +68,15 @@ pub fn kzg_to_versioned_hash(commitment: &[u8]) -> [u8; 32] { } #[inline] -pub fn verify_kzg_proof( - commitment: &Bytes48, - z: &Bytes32, - y: &Bytes32, - proof: &Bytes48, - kzg_settings: &KzgSettings, -) -> bool { +pub fn verify_kzg_proof(commitment: &Bytes48, z: &Bytes32, y: &Bytes32, proof: &Bytes48) -> bool { + cfg_if::cfg_if! { + if #[cfg(feature = "c-kzg")] { + let kzg_settings = c_kzg::ethereum_kzg_settings(); + } else if #[cfg(feature = "kzg-rs")] { + let env = kzg_rs::EnvKzgSettings::default(); + let kzg_settings = env.get(); + } + } KzgProof::verify_kzg_proof(commitment, z, y, proof, kzg_settings).unwrap_or(false) } @@ -121,8 +119,7 @@ mod tests { let expected_output = hex!("000000000000000000000000000000000000000000000000000000000000100073eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001"); let gas = 50000; - let env = CfgEnv::default(); - let output = run(&input.into(), gas, &env).unwrap(); + let output = run(&input.into(), gas).unwrap(); assert_eq!(output.gas_used, gas); assert_eq!(output.bytes[..], expected_output); } diff --git a/crates/precompile/src/lib.rs b/crates/precompile/src/lib.rs index bc0551dd6e..f069f5a9cb 100644 --- a/crates/precompile/src/lib.rs +++ b/crates/precompile/src/lib.rs @@ -12,7 +12,6 @@ pub mod blake2; #[cfg(feature = "blst")] pub mod bls12_381; pub mod bn128; -pub mod fatal_precompile; pub mod hash; pub mod identity; pub mod interface; @@ -24,8 +23,6 @@ pub mod secp256k1; pub mod secp256r1; pub mod utilities; -pub use fatal_precompile::fatal_precompile; - pub use interface::*; #[cfg(all(feature = "c-kzg", feature = "kzg-rs"))] // silence kzg-rs lint as c-kzg will be used as default if both are enabled. @@ -37,16 +34,17 @@ pub use primitives; use cfg_if::cfg_if; use core::hash::Hash; use once_cell::race::OnceBox; +use specification::hardfork::SpecId; use std::{boxed::Box, vec::Vec}; pub fn calc_linear_cost_u32(len: usize, base: u64, word: u64) -> u64 { - (len as u64 + 32 - 1) / 32 * word + base + (len as u64).div_ceil(32) * word + base } #[derive(Clone, Default, Debug)] pub struct Precompiles { /// Precompiles. - inner: HashMap, + inner: HashMap, /// Addresses of precompile. addresses: HashSet
, } @@ -81,7 +79,7 @@ impl Precompiles { } /// Returns inner HashMap of precompiles. - pub fn inner(&self) -> &HashMap { + pub fn inner(&self) -> &HashMap { &self.inner } @@ -147,11 +145,11 @@ impl Precompiles { if #[cfg(any(feature = "c-kzg", feature = "kzg-rs"))] { let precompile = kzg_point_evaluation::POINT_EVALUATION.clone(); } else { - // TODO move constants to separate file. - let precompile = fatal_precompile(u64_to_address(0x0A), "c-kzg feature is not enabled".into()); + let precompile = PrecompileWithAddress(u64_to_address(0x0A), |_,_| Err(PrecompileErrors::Fatal { msg: "c-kzg feature is not enabled".into()})); } } + precompiles.extend([ precompile, ]); @@ -203,13 +201,13 @@ impl Precompiles { /// Returns the precompile for the given address. #[inline] - pub fn get(&self, address: &Address) -> Option<&Precompile> { + pub fn get(&self, address: &Address) -> Option<&PrecompileFn> { self.inner.get(address) } /// Returns the precompile for the given address. #[inline] - pub fn get_mut(&mut self, address: &Address) -> Option<&mut Precompile> { + pub fn get_mut(&mut self, address: &Address) -> Option<&mut PrecompileFn> { self.inner.get_mut(address) } @@ -233,22 +231,22 @@ impl Precompiles { /// Other precompiles with overwrite existing precompiles. #[inline] pub fn extend(&mut self, other: impl IntoIterator) { - let items = other.into_iter().collect::>(); + let items: Vec = other.into_iter().collect::>(); self.addresses.extend(items.iter().map(|p| *p.address())); - self.inner.extend(items.into_iter().map(Into::into)); + self.inner.extend(items.into_iter().map(|p| (p.0, p.1))); } } #[derive(Clone, Debug)] -pub struct PrecompileWithAddress(pub Address, pub Precompile); +pub struct PrecompileWithAddress(pub Address, pub PrecompileFn); -impl From<(Address, Precompile)> for PrecompileWithAddress { - fn from(value: (Address, Precompile)) -> Self { +impl From<(Address, PrecompileFn)> for PrecompileWithAddress { + fn from(value: (Address, PrecompileFn)) -> Self { PrecompileWithAddress(value.0, value.1) } } -impl From for (Address, Precompile) { +impl From for (Address, PrecompileFn) { fn from(value: PrecompileWithAddress) -> Self { (value.0, value.1) } @@ -263,7 +261,7 @@ impl PrecompileWithAddress { /// Returns reference of precompile. #[inline] - pub fn precompile(&self) -> &Precompile { + pub fn precompile(&self) -> &PrecompileFn { &self.1 } } @@ -279,8 +277,14 @@ pub enum PrecompileSpecId { LATEST, } +impl From for PrecompileSpecId { + fn from(spec_id: SpecId) -> Self { + Self::from_spec_id(spec_id) + } +} + impl PrecompileSpecId { - /// Returns the appropriate precompile Spec for the primitive [SpecId](specification::hardfork::SpecId) + /// Returns the appropriate precompile Spec for the primitive [SpecId]. pub const fn from_spec_id(spec_id: specification::hardfork::SpecId) -> Self { use specification::hardfork::SpecId::*; match spec_id { diff --git a/crates/precompile/src/modexp.rs b/crates/precompile/src/modexp.rs index ac8aea29d1..42468052f7 100644 --- a/crates/precompile/src/modexp.rs +++ b/crates/precompile/src/modexp.rs @@ -1,19 +1,17 @@ use crate::{ primitives::U256, utilities::{left_pad, left_pad_vec, right_pad_vec, right_pad_with_offset}, - Precompile, PrecompileError, PrecompileOutput, PrecompileResult, PrecompileWithAddress, + PrecompileError, PrecompileOutput, PrecompileResult, PrecompileWithAddress, }; use aurora_engine_modexp::modexp; use core::cmp::{max, min}; use primitives::Bytes; -pub const BYZANTIUM: PrecompileWithAddress = PrecompileWithAddress( - crate::u64_to_address(5), - Precompile::Standard(byzantium_run), -); +pub const BYZANTIUM: PrecompileWithAddress = + PrecompileWithAddress(crate::u64_to_address(5), byzantium_run); pub const BERLIN: PrecompileWithAddress = - PrecompileWithAddress(crate::u64_to_address(5), Precompile::Standard(berlin_run)); + PrecompileWithAddress(crate::u64_to_address(5), berlin_run); /// See: /// See: diff --git a/crates/precompile/src/secp256k1.rs b/crates/precompile/src/secp256k1.rs index 864b43eaf3..e300f87b4d 100644 --- a/crates/precompile/src/secp256k1.rs +++ b/crates/precompile/src/secp256k1.rs @@ -1,13 +1,11 @@ use crate::{ - utilities::right_pad, Precompile, PrecompileError, PrecompileOutput, PrecompileResult, + utilities::right_pad, PrecompileError, PrecompileOutput, PrecompileResult, PrecompileWithAddress, }; use primitives::{alloy_primitives::B512, Bytes, B256}; -pub const ECRECOVER: PrecompileWithAddress = PrecompileWithAddress( - crate::u64_to_address(1), - Precompile::Standard(ec_recover_run), -); +pub const ECRECOVER: PrecompileWithAddress = + PrecompileWithAddress(crate::u64_to_address(1), ec_recover_run); pub use self::secp256k1::ecrecover; diff --git a/crates/precompile/src/secp256r1.rs b/crates/precompile/src/secp256r1.rs index 16aaebec31..6f6bb84f96 100644 --- a/crates/precompile/src/secp256r1.rs +++ b/crates/precompile/src/secp256r1.rs @@ -7,8 +7,7 @@ //! P256 elliptic curve. The [`P256VERIFY`] const represents the implementation of this precompile, //! with the address that it is currently deployed at. use crate::{ - u64_to_address, Precompile, PrecompileError, PrecompileOutput, PrecompileResult, - PrecompileWithAddress, + u64_to_address, PrecompileError, PrecompileOutput, PrecompileResult, PrecompileWithAddress, }; use p256::ecdsa::{signature::hazmat::PrehashVerifier, Signature, VerifyingKey}; use primitives::{Bytes, B256}; @@ -23,7 +22,7 @@ pub fn precompiles() -> impl Iterator { /// [EIP-7212](https://eips.ethereum.org/EIPS/eip-7212#specification) secp256r1 precompile. pub const P256VERIFY: PrecompileWithAddress = - PrecompileWithAddress(u64_to_address(0x100), Precompile::Standard(p256_verify)); + PrecompileWithAddress(u64_to_address(0x100), p256_verify); /// secp256r1 precompile logic. It takes the input bytes sent to the precompile /// and the gas limit. The output represents the result of verifying the diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index 7c832a4e32..9098136484 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -25,24 +25,22 @@ all = "warn" # revm interpreter.workspace = true precompile.workspace = true -wiring.workspace = true primitives.workspace = true database-interface.workspace = true state.workspace = true specification.workspace = true bytecode.workspace = true -database = { workspace = true, optional = true } -transaction.workspace = true - -# misc -derive-where = { version = "1.2.7", default-features = false } -dyn-clone = "1.0" +context.workspace = true +context-interface.workspace = true +handler.workspace = true +handler-interface.workspace = true # Optional -serde = { version = "1.0", default-features = false, features = [ - "derive", - "rc", -], optional = true } +# TODO check if needed. +# serde = { version = "1.0", default-features = false, features = [ +# "derive", +# "rc", +# ], optional = true } [dev-dependencies] database.workspace = true @@ -60,19 +58,21 @@ alloy-provider = "0.4.2" [features] default = ["std", "c-kzg", "secp256k1", "portable", "blst"] -std = ["serde?/std", "interpreter/std", "precompile/std"] -hashbrown = ["interpreter/hashbrown", "precompile/hashbrown"] -serde = [ - "dep:serde", - "interpreter/serde", - "database-interface/serde", - "primitives/serde", +std = [ + "interpreter/std", + "precompile/std", + "handler/std", + "handler-interface/std", + "context/std", + "context-interface/std", ] +hashbrown = ["interpreter/hashbrown", "precompile/hashbrown"] +serde = ["interpreter/serde", "database-interface/serde", "primitives/serde"] arbitrary = ["primitives/arbitrary"] asm-keccak = ["primitives/asm-keccak"] -portable = ["wiring/portable"] +portable = ["precompile/portable"] -test-utils = ["database"] +test-utils = [] dev = [ "memory_limit", @@ -82,12 +82,12 @@ dev = [ "optional_gas_refund", "optional_no_base_fee", ] -memory_limit = ["wiring/memory_limit", "interpreter/memory_limit"] -optional_balance_check = ["wiring/optional_balance_check"] -optional_block_gas_limit = ["wiring/optional_block_gas_limit"] -optional_eip3607 = ["wiring/optional_eip3607"] -optional_gas_refund = ["wiring/optional_gas_refund"] -optional_no_base_fee = ["wiring/optional_no_base_fee"] +memory_limit = ["context-interface/memory_limit", "interpreter/memory_limit"] +optional_balance_check = ["context-interface/optional_balance_check"] +optional_block_gas_limit = ["context-interface/optional_block_gas_limit"] +optional_eip3607 = ["context-interface/optional_eip3607"] +optional_gas_refund = ["context-interface/optional_gas_refund"] +optional_no_base_fee = ["context-interface/optional_no_base_fee"] # See comments in `precompile` secp256k1 = ["precompile/secp256k1"] diff --git a/crates/revm/benches/bench.rs b/crates/revm/benches/bench.rs deleted file mode 100644 index 411e7df610..0000000000 --- a/crates/revm/benches/bench.rs +++ /dev/null @@ -1,140 +0,0 @@ -use criterion::{ - criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, -}; -use database::BenchmarkDB; -use interpreter::{table::make_instruction_table, SharedMemory, EMPTY_SHARED_MEMORY}; -use revm::{ - bytecode::Bytecode, - interpreter::{Contract, DummyHost, Interpreter}, - primitives::{address, bytes, hex, Bytes, TxKind, U256}, - specification::hardfork::BerlinSpec, - wiring::EthereumWiring, - Evm, -}; -use std::time::Duration; - -fn analysis(c: &mut Criterion) { - let evm = Evm::>::builder() - .modify_tx_env(|tx| { - tx.caller = address!("0000000000000000000000000000000000000002"); - tx.transact_to = TxKind::Call(address!("0000000000000000000000000000000000000000")); - // evm.env.tx.data = bytes!("30627b7c"); - tx.data = bytes!("8035F0CE"); - }) - .build(); - - let contract_data: Bytes = hex::decode(ANALYSIS).unwrap().into(); - - let mut g = c.benchmark_group("analysis"); - g.noise_threshold(0.03) - .warm_up_time(Duration::from_secs(3)) - .measurement_time(Duration::from_secs(10)) - .sample_size(10); - - let raw = Bytecode::new_raw(contract_data.clone()); - let mut evm = evm.modify().with_db(BenchmarkDB::new_bytecode(raw)).build(); - bench_transact(&mut g, &mut evm); - - let analysed = Bytecode::new_raw(contract_data).into_analyzed(); - let mut evm = evm - .modify() - .with_db(BenchmarkDB::new_bytecode(analysed)) - .build(); - bench_transact(&mut g, &mut evm); - - g.finish(); -} - -fn snailtracer(c: &mut Criterion) { - let mut evm = Evm::>::builder() - .with_db(BenchmarkDB::new_bytecode(bytecode(SNAILTRACER))) - .modify_tx_env(|tx| { - tx.caller = address!("1000000000000000000000000000000000000000"); - tx.transact_to = TxKind::Call(address!("0000000000000000000000000000000000000000")); - tx.data = bytes!("30627b7c"); - }) - .build(); - - let mut g = c.benchmark_group("snailtracer"); - g.noise_threshold(0.03) - .warm_up_time(Duration::from_secs(3)) - .measurement_time(Duration::from_secs(10)) - .sample_size(10); - bench_transact(&mut g, &mut evm); - bench_eval(&mut g, &mut evm); - g.finish(); -} - -fn transfer(c: &mut Criterion) { - let mut evm = Evm::>::builder() - .with_db(BenchmarkDB::new_bytecode(Bytecode::new())) - .modify_tx_env(|tx| { - tx.caller = address!("0000000000000000000000000000000000000001"); - tx.transact_to = TxKind::Call(address!("0000000000000000000000000000000000000000")); - tx.value = U256::from(10); - }) - .build(); - - let mut g = c.benchmark_group("transfer"); - g.noise_threshold(0.03).warm_up_time(Duration::from_secs(1)); - bench_transact(&mut g, &mut evm); - g.finish(); -} - -fn bench_transact( - g: &mut BenchmarkGroup<'_, WallTime>, - evm: &mut Evm<'_, EthereumWiring>, -) { - let state = match evm.context.evm.db.0 { - Bytecode::LegacyRaw(_) => "raw", - Bytecode::LegacyAnalyzed(_) => "analysed", - Bytecode::Eof(_) => "eof", - Bytecode::Eip7702(_) => panic!("Delegated account not supported"), - }; - let id = format!("transact/{state}"); - g.bench_function(id, |b| b.iter(|| evm.transact().unwrap())); -} - -fn bench_eval( - g: &mut BenchmarkGroup<'_, WallTime>, - evm: &mut Evm<'static, EthereumWiring>, -) { - g.bench_function("eval", |b| { - let contract = Contract { - input: evm.context.evm.env.tx.data.clone(), - bytecode: evm.context.evm.db.0.clone().into_analyzed(), - ..Default::default() - }; - let mut shared_memory = SharedMemory::new(); - let mut host = DummyHost::new(*evm.context.evm.env.clone()); - let instruction_table = - make_instruction_table::>, BerlinSpec>(); - b.iter(move || { - // replace memory with empty memory to use it inside interpreter. - // Later return memory back. - let temp = core::mem::replace(&mut shared_memory, EMPTY_SHARED_MEMORY); - let mut interpreter = Interpreter::new(contract.clone(), u64::MAX, false); - let res = interpreter.run(temp, &instruction_table, &mut host); - shared_memory = interpreter.take_memory(); - host.clear(); - res - }) - }); -} - -fn bytecode(s: &str) -> Bytecode { - Bytecode::new_raw(hex::decode(s).unwrap().into()).into_analyzed() -} - -#[rustfmt::skip] -criterion_group!( - benches, - analysis, - snailtracer, - transfer, -); -criterion_main!(benches); - -const ANALYSIS: &str = "6060604052341561000f57600080fd5b604051610dd1380380610dd18339810160405280805190602001909190805182019190602001805190602001909190805182019190505083600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508360008190555082600390805190602001906100a79291906100e3565b5081600460006101000a81548160ff021916908360ff16021790555080600590805190602001906100d99291906100e3565b5050505050610188565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061012457805160ff1916838001178555610152565b82800160010185558215610152579182015b82811115610151578251825591602001919060010190610136565b5b50905061015f9190610163565b5090565b61018591905b80821115610181576000816000905550600101610169565b5090565b90565b610c3a806101976000396000f3006060604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b4578063095ea7b31461014257806318160ddd1461019c57806323b872dd146101c557806327e235e31461023e578063313ce5671461028b5780635c658165146102ba57806370a082311461032657806395d89b4114610373578063a9059cbb14610401578063dd62ed3e1461045b575b600080fd5b34156100bf57600080fd5b6100c76104c7565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101075780820151818401526020810190506100ec565b50505050905090810190601f1680156101345780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561014d57600080fd5b610182600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610565565b604051808215151515815260200191505060405180910390f35b34156101a757600080fd5b6101af610657565b6040518082815260200191505060405180910390f35b34156101d057600080fd5b610224600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061065d565b604051808215151515815260200191505060405180910390f35b341561024957600080fd5b610275600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506108f7565b6040518082815260200191505060405180910390f35b341561029657600080fd5b61029e61090f565b604051808260ff1660ff16815260200191505060405180910390f35b34156102c557600080fd5b610310600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610922565b6040518082815260200191505060405180910390f35b341561033157600080fd5b61035d600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610947565b6040518082815260200191505060405180910390f35b341561037e57600080fd5b610386610990565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103c65780820151818401526020810190506103ab565b50505050905090810190601f1680156103f35780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561040c57600080fd5b610441600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610a2e565b604051808215151515815260200191505060405180910390f35b341561046657600080fd5b6104b1600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610b87565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561055d5780601f106105325761010080835404028352916020019161055d565b820191906000526020600020905b81548152906001019060200180831161054057829003601f168201915b505050505081565b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60005481565b600080600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015801561072e5750828110155b151561073957600080fd5b82600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156108865782600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a360019150509392505050565b60016020528060005260406000206000915090505481565b600460009054906101000a900460ff1681565b6002602052816000526040600020602052806000526040600020600091509150505481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a265780601f106109fb57610100808354040283529160200191610a26565b820191906000526020600020905b815481529060010190602001808311610a0957829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610a7e57600080fd5b81600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a36001905092915050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050929150505600a165627a7a72305820df254047bc8f2904ad3e966b6db116d703bebd40efadadb5e738c836ffc8f58a0029"; - -const SNAILTRACER: &str = "608060405234801561001057600080fd5b506004361061004c5760003560e01c806330627b7c1461005157806375ac892a14610085578063784f13661461011d578063c294360114610146575b600080fd5b610059610163565b604080516001600160f81b03199485168152928416602084015292168183015290519081900360600190f35b6100a86004803603604081101561009b57600080fd5b50803590602001356102d1565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100e25781810151838201526020016100ca565b50505050905090810190601f16801561010f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6100596004803603606081101561013357600080fd5b508035906020810135906040013561055b565b6100a86004803603602081101561015c57600080fd5b5035610590565b6000806000610176610400610300610834565b60405180606001604052806001546000546207d5dc028161019357fe5b058152600060208083018290526040928301919091528251600b81905583820151600c81905593830151600d819055835160608082018652928152808401959095528484015282519081018352600654815260075491810191909152600854918101919091526102259161021c916102139161020e91612ef7565b612f64565b6207d5dc612feb565b620f424061301e565b8051600e556020810151600f55604001516010556102416142dd565b61025a816102556102006101806008613064565b613212565b90506102708161025561014561021c6008613064565b905061028481610255610258806008613064565b905061029a8161025561020a61020c6008613064565b90506102a781600461301e565b90506102b1613250565b8051602082015160409092015160f891821b9692821b9550901b92509050565b606060005b6000548112156104c95760006102ed828686613064565b90506002816000015160f81b90808054603f811680603e811461032a576002830184556001831661031c578192505b600160028404019350610342565b600084815260209081902060ff198516905560419094555b505050600190038154600116156103685790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816020015160f81b90808054603f811680603e81146103c557600283018455600183166103b7578192505b6001600284040193506103dd565b600084815260209081902060ff198516905560419094555b505050600190038154600116156104035790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816040015160f81b90808054603f811680603e81146104605760028301845560018316610452578192505b600160028404019350610478565b600084815260209081902060ff198516905560419094555b5050506001900381546001161561049e5790600052602060002090602091828204019190065b815460ff601f929092036101000a9182021916600160f81b90930402919091179055506001016102d6565b506002805460408051602060018416156101000260001901909316849004601f8101849004840282018401909252818152929183018282801561054d5780601f106105225761010080835404028352916020019161054d565b820191906000526020600020905b81548152906001019060200180831161053057829003601f168201915b505050505090505b92915050565b60008060008061056c878787613064565b8051602082015160409092015160f891821b9a92821b9950901b9650945050505050565b600154606090600019015b600081126107a35760005b6000548112156107995760006105bd828487613064565b90506002816000015160f81b90808054603f811680603e81146105fa57600283018455600183166105ec578192505b600160028404019350610612565b600084815260209081902060ff198516905560419094555b505050600190038154600116156106385790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816020015160f81b90808054603f811680603e81146106955760028301845560018316610687578192505b6001600284040193506106ad565b600084815260209081902060ff198516905560419094555b505050600190038154600116156106d35790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816040015160f81b90808054603f811680603e81146107305760028301845560018316610722578192505b600160028404019350610748565b600084815260209081902060ff198516905560419094555b5050506001900381546001161561076e5790600052602060002090602091828204019190065b815460ff601f929092036101000a9182021916600160f81b90930402919091179055506001016105a6565b506000190161059b565b506002805460408051602060018416156101000260001901909316849004601f810184900484028201840190925281815292918301828280156108275780601f106107fc57610100808354040283529160200191610827565b820191906000526020600020905b81548152906001019060200180831161080a57829003601f168201915b505050505090505b919050565b8160008190555080600181905550604051806080016040528060405180606001604052806302faf08081526020016303197500815260200163119e7f8081525081526020016108a460405180606001604052806000815260200161a673198152602001620f423f19815250612f64565b815260006020808301829052604092830182905283518051600355808201516004558301516005558381015180516006559081015160075582015160085582820151600955606092830151600a805460ff1916911515919091179055815192830190915260015490548291906207d5dc028161091c57fe5b058152600060208083018290526040928301919091528251600b81905583820151600c81905593830151600d819055835160608082018652928152808401959095528484015282519081018352600654815260075491810191909152600854918101919091526109979161021c916102139161020e91612ef7565b8051600e55602080820151600f55604091820151601055815160a08101835264174876e8008152825160608082018552641748862a40825263026e8f00828501526304dd1e008286015282840191825284518082018652600080825281860181905281870181905284870191825286518084018852620b71b081526203d09081880181905281890152928501928352608085018181526011805460018082018355919093528651600b9093027f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c688101938455955180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c69880155808901517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6a8801558901517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6b870155925180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6c870155808801517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6d8701558801517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6e860155925180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6f860155958601517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c7085015594909501517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c71830155517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c72909101805492949192909160ff1990911690836002811115610c1057fe5b0217905550505060116040518060a0016040528064174876e8008152602001604051806060016040528064174290493f19815260200163026e8f0081526020016304dd1e008152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806203d09081526020016203d0908152602001620b71b0815250815260200160006002811115610cb657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610d5857fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200163026e8f00815260200164174876e800815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b0815250815260200160006002811115610dfd57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610e9f57fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200163026e8f00815260200164173e54e97f1981525081526020016040518060600160405280600081526020016000815260200160008152508152602001604051806060016040528060008152602001600081526020016000815250815260200160006002811115610f3f57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610fe157fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200164174876e80081526020016304dd1e00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b081525081526020016000600281111561108657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff199092169190849081111561112857fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200164174399c9ff1981526020016304dd1e00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b08152508152602001600060028111156111ce57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff199092169190849081111561127057fe5b0217905550505060116040518060a0016040528062fbc5208152602001604051806060016040528063019bfcc0815260200162fbc52081526020016302cd29c0815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561131157fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff19909216919084908111156113b357fe5b0217905550505060116040518060a001604052806323c34600815260200160405180606001604052806302faf080815260200163289c455081526020016304dd1e008152508152602001604051806060016040528062b71b00815260200162b71b00815260200162b71b00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016000600281111561145657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff19909216919084908111156114f857fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561160c57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff19909216919084908111156116fd57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561180e57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff19909216919084908111156118ff57fe5b0217905550505060126040518060e001604052806040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611a1357fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611b0457fe5b0217905550505060126040518060e001604052806040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611c1557fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611d0657fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611e1a57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611f0b57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a6081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561201c57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561210d57fe5b0217905550505060126040518060e001604052806040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a6081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561222157fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561231257fe5b0217905550505060126040518060e001604052806040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561242357fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561251457fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a6081525081526020016040518060600160405280630555a9608152602001630188c2e081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561262857fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561271957fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561282d57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561291e57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115612a3257fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115612b2357fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a081525081526020016040518060600160405280630555a960815260200163016a8c8081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115612c3757fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115612d2857fe5b0217905550505060005b601254811015612ef257600060128281548110612d4b57fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff90911690811115612e6c57fe5b6002811115612e7757fe5b815250509050612eac61020e612e95836020015184600001516132cd565b612ea7846040015185600001516132cd565b612ef7565b60128381548110612eb957fe5b60009182526020918290208351600960139093029091019182015590820151600a820155604090910151600b9091015550600101612d32565b505050565b612eff6142dd565b604051806060016040528083602001518560400151028460400151866020015102038152602001836040015185600001510284600001518660400151020381526020018360000151856020015102846020015186600001510203815250905092915050565b612f6c6142dd565b604082015160208301518351600092612f9292918002918002919091019080020161330c565b90506040518060600160405280828560000151620f42400281612fb157fe5b058152602001828560200151620f42400281612fc957fe5b058152602001828560400151620f42400281612fe157fe5b0590529392505050565b612ff36142dd565b5060408051606081018252835183028152602080850151840290820152928101519091029082015290565b6130266142dd565b60405180606001604052808385600001518161303e57fe5b0581526020018385602001518161305157fe5b05815260200183856040015181612fe157fe5b61306c6142dd565b6000546013805463ffffffff1916918502860163ffffffff169190911790556130936142dd565b905060005b828112156131f157600061317261314c61021c613115600b60405180606001604052908160008201548152602001600182015481526020016002820154815250506207a1206000546207a1206130ec613343565b63ffffffff16816130f957fe5b0663ffffffff168d620f424002018161310e57fe5b0503612feb565b60408051606081018252600e548152600f5460208201526010549181019190915260015461025591906207a12090816130ec613343565b604080516060810182526006548152600754602082015260085491810191909152613212565b6040805160e081019091526003546080820190815260045460a083015260055460c083015291925060009181906131ae9061025586608c612feb565b81526020016131bc84612f64565b815260006020820181905260409091015290506131e5846102556131df8461336c565b8861301e565b93505050600101613098565b5061320861021c61320183613753565b60ff612feb565b90505b9392505050565b61321a6142dd565b50604080516060810182528251845101815260208084015181860151019082015291810151928101519092019181019190915290565b60008080556001819055613266906002906142fe565b60006003819055600481905560058190556006819055600781905560088190556009819055600a805460ff19169055600b819055600c819055600d819055600e819055600f81905560108190556132bf90601190614345565b6132cb60126000614366565b565b6132d56142dd565b5060408051606081018252825184510381526020808401518186015103908201528282015184830151039181019190915292915050565b80600260018201055b8181121561333d5780915060028182858161332c57fe5b05018161333557fe5b059050613315565b50919050565b6013805463ffffffff19811663ffffffff9182166341c64e6d0261303901821617918290551690565b6133746142dd565b600a826040015113156133a657604051806060016040528060008152602001600081526020016000815250905061082f565b60008060006133b48561379f565b91945092509050826133e857604051806060016040528060008152602001600081526020016000815250935050505061082f565b6133f0614387565b6133f86143c7565b6134006142dd565b6134086142dd565b600086600181111561341657fe5b1415613505576011858154811061342957fe5b60009182526020918290206040805160a081018252600b90930290910180548352815160608082018452600183015482526002808401548388015260038401548386015285870192909252835180820185526004840154815260058401548188015260068401548186015285850152835180820185526007840154815260088401549681019690965260098301549386019390935291830193909352600a830154919291608084019160ff909116908111156134e157fe5b60028111156134ec57fe5b8152505093508360600151915083604001519050613653565b6012858154811061351257fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff9091169081111561363357fe5b600281111561363e57fe5b8152505092508260a001519150826080015190505b6040820151600190811215613669575060408201515b808360200151131561367c575060208201515b808360400151131561368f575060408201515b60408a01805160010190819052600512156136f75780620f42406136b1613343565b63ffffffff16816136be57fe5b0663ffffffff1612156136e8576136e16136db84620f4240612feb565b8261301e565b92506136f7565b50965061082f95505050505050565b6136ff6142dd565b600088600181111561370d57fe5b14156137255761371e8b878b613a57565b9050613733565b6137308b868b613aec565b90505b6137448361025561021c8785613baa565b9b9a5050505050505050505050565b61375b6142dd565b60405180606001604052806137738460000151613be8565b81526020016137858460200151613be8565b81526020016137978460400151613be8565b905292915050565b60008080808080805b6011548110156138c2576000613890601183815481106137c457fe5b60009182526020918290206040805160a081018252600b90930290910180548352815160608082018452600183015482526002808401548388015260038401548386015285870192909252835180820185526004840154815260058401548188015260068401548186015285850152835180820185526007840154815260088401549681019690965260098301549386019390935291830193909352600a830154919291608084019160ff9091169081111561387c57fe5b600281111561388757fe5b9052508a613c13565b90506000811380156138a957508415806138a957508481125b156138b957809450600093508192505b506001016137a8565b5060005b601254811015613a49576000613a17601283815481106138e257fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff90911690811115613a0357fe5b6002811115613a0e57fe5b9052508a613cbb565b9050600081138015613a305750841580613a3057508481125b15613a4057809450600193508192505b506001016138c6565b509196909550909350915050565b613a5f6142dd565b6000613a7a856000015161025561021c886020015187612feb565b90506000613a8f61020e8387602001516132cd565b9050600085608001516002811115613aa357fe5b1415613ae1576000613ab9828860200151613e0c565b12613acd57613aca81600019612feb565b90505b613ad8868383613e31565b9250505061320b565b613ad8868383613fc1565b613af46142dd565b6000613b0f856000015161025561021c886020015187612feb565b6060860151909150620a2c2a9015613b2757506216e3605b6000620f4240613b3f87606001518960200151613e0c565b81613b4657fe5b05905060008112613b55576000035b64e8d4a5100081800281038380020281900590036000811215613b8c57613b8188858960600151613fc1565b94505050505061320b565b613b9e88858960600151868686614039565b98975050505050505050565b613bb26142dd565b50604080516060810182528251845102815260208084015181860151029082015291810151928101519092029181019190915290565b600080821215613bfa5750600061082f565b620f4240821315613c0f5750620f424061082f565b5090565b600080613c28846020015184600001516132cd565b90506000620f4240613c3e838660200151613e0c565b81613c4557fe5b865191900591506000908002613c5b8480613e0c565b838402030190506000811215613c775760009350505050610555565b613c808161330c565b90506103e88183031315613c9957900391506105559050565b6103e88183011315613caf570191506105559050565b50600095945050505050565b600080613cd0846020015185600001516132cd565b90506000613ce6856040015186600001516132cd565b90506000613cf8856020015183612ef7565b90506000620f4240613d0a8584613e0c565b81613d1157fe5b0590506103e71981138015613d2757506103e881125b15613d39576000945050505050610555565b85518751600091613d49916132cd565b9050600082613d588386613e0c565b81613d5f57fe5b0590506000811280613d735750620f424081135b15613d875760009650505050505050610555565b6000613d938388612ef7565b9050600084613da68b6020015184613e0c565b81613dad57fe5b0590506000811280613dc35750620f4240818401135b15613dd957600098505050505050505050610555565b600085613de68985613e0c565b81613ded57fe5b0590506103e88112156137445760009950505050505050505050610555565b6040808201519083015160208084015190850151845186510291020191020192915050565b613e396142dd565b6000620f424080613e48613343565b63ffffffff1681613e5557fe5b0663ffffffff16625fdfb00281613e6857fe5b0590506000620f4240613e79613343565b63ffffffff1681613e8657fe5b0663ffffffff1690506000613e9a8261330c565b6103e8029050613ea86142dd565b620186a0613eb98760000151614216565b1315613ee657604051806060016040528060008152602001620f4240815260200160008152509050613f09565b6040518060600160405280620f4240815260200160008152602001600081525090505b613f1661020e8288612ef7565b90506000613f2761020e8884612ef7565b9050613f7f61020e613f64613f5285620f424088613f448c61422e565b0281613f4c57fe5b05612feb565b61025585620f424089613f448d61424e565b6102558a613f7689620f42400361330c565b6103e802612feb565b9150613fb460405180608001604052808a81526020018481526020018b6040015181526020018b60600151151581525061336c565b9998505050505050505050565b613fc96142dd565b6000613ffb61020e8660200151613ff686620f4240613fec898c60200151613e0c565b60020281613f4c57fe5b6132cd565b90506140306040518060800160405280868152602001838152602001876040015181526020018760600151151581525061336c565b95945050505050565b6140416142dd565b60608701516000199015614053575060015b600061408961020e61021c61406c8c602001518a612feb565b613ff68b6140798a61330c565b620f42408c8e0205018802612feb565b60608a0151909150620f42408601906140ba57620f42406140aa838a613e0c565b816140b157fe5b05620f42400390505b60408a0151619c406c0c9f2c9cd04674edea40000000620ea6008480028502850285020205019060021261415e5761412a61411f60405180608001604052808d81526020018681526020018e6040015181526020018e6060015115151581525061336c565b82620f424003612feb565b92506141448361025561413e8e8e8e613fc1565b84612feb565b925061415383620f424061301e565b94505050505061420c565b600281056203d09001620f4240614173613343565b63ffffffff168161418057fe5b0663ffffffff1612156141b2576141536141a461419e8d8d8d613fc1565b83612feb565b600283056203d0900161301e565b6142056141f76141ec60405180608001604052808e81526020018781526020018f6040015181526020018f6060015115151581525061336c565b83620f424003612feb565b60028305620b71b00361301e565b9450505050505b9695505050505050565b60008082131561422757508061082f565b5060000390565b60008061423a8361424e565b905061320b81820264e8d4a510000361330c565b60005b600082121561426757625fdfb082019150614251565b5b625fdfb0821261427f57625fdfb082039150614268565b6001828160025b818313156142d457818385028161429957fe5b0585019450620f4240808788860202816142af57fe5b05816142b757fe5b600095909503940592506001810181029190910290600201614286565b50505050919050565b60405180606001604052806000815260200160008152602001600081525090565b50805460018160011615610100020316600290046000825580601f106143245750614342565b601f0160209004906000526020600020908101906143429190614401565b50565b50805460008255600b02906000526020600020908101906143429190614416565b50805460008255601302906000526020600020908101906143429190614475565b6040518060a00160405280600081526020016143a16142dd565b81526020016143ae6142dd565b81526020016143bb6142dd565b81526020016000905290565b6040518060e001604052806143da6142dd565b81526020016143e76142dd565b81526020016143f46142dd565b81526020016143a16142dd565b5b80821115613c0f5760008155600101614402565b5b80821115613c0f57600080825560018201819055600282018190556003820181905560048201819055600582018190556006820181905560078201819055600882018190556009820155600a8101805460ff19169055600b01614417565b5b80821115613c0f576000808255600182018190556002820181905560038201819055600482018190556005820181905560068201819055600782018190556008820181905560098201819055600a8201819055600b8201819055600c8201819055600d8201819055600e8201819055600f820181905560108201819055601182015560128101805460ff1916905560130161447656fea2646970667358221220037024f5647853879c58fbcc61ac3616455f6f731cc6e84f91eb5a3b4e06c00464736f6c63430007060033"; diff --git a/crates/revm/src/builder.rs b/crates/revm/src/builder.rs deleted file mode 100644 index 10a572ee7e..0000000000 --- a/crates/revm/src/builder.rs +++ /dev/null @@ -1,694 +0,0 @@ -use crate::{handler::register, Context, Evm, EvmContext, EvmWiring, Handler}; -use core::marker::PhantomData; -use database_interface::EmptyDB; -use std::boxed::Box; -use transaction::Transaction; -use wiring::{ - default::{CfgEnv, EnvWiring}, - result::InvalidTransaction, - EthereumWiring, -}; - -/// Evm Builder allows building or modifying EVM. -/// Note that some of the methods that changes underlying structures -/// will reset the registered handler to default mainnet. -pub struct EvmBuilder<'a, BuilderStage, EvmWiringT: EvmWiring> { - database: Option, - external_context: Option, - env: Option>>, - /// Handler that will be used by EVM. It contains handle registers - handler: Handler<'a, EvmWiringT, Context>, - /// Phantom data to mark the stage of the builder. - phantom: PhantomData, -} - -/// First stage of the builder allows setting generic variables. -/// Generic variables are database and external context. -pub struct SetGenericStage; - -/// Second stage of the builder allows appending handler registers. -/// Requires the database and external context to be set. -pub struct HandlerStage; - -impl<'a> Default for EvmBuilder<'a, SetGenericStage, EthereumWiring> { - fn default() -> Self { - Self::new() - } -} - -impl<'a, EvmWiringT: EvmWiring> EvmBuilder<'a, SetGenericStage, EvmWiringT> -where - EvmWiringT::Transaction: Default, - EvmWiringT::Block: Default, -{ - /// Sets the [`EvmWiring`] that will be used by [`Evm`]. - pub fn new() -> EvmBuilder<'a, SetGenericStage, EvmWiringT> { - EvmBuilder { - database: None, - external_context: None, - env: Some(Box::new(EnvWiring::::default())), - handler: EvmWiringT::handler::<'a>(EvmWiringT::Hardfork::default()), - phantom: PhantomData, - } - } -} - -impl<'a, EvmWiringT: EvmWiring> EvmBuilder<'a, SetGenericStage, EvmWiringT> { - pub fn new_with( - database: EvmWiringT::Database, - external_context: EvmWiringT::ExternalContext, - env: Box>, - handler: Handler<'a, EvmWiringT, Context>, - ) -> EvmBuilder<'a, SetGenericStage, EvmWiringT> { - EvmBuilder { - database: Some(database), - external_context: Some(external_context), - env: Some(env), - handler, - phantom: PhantomData, - } - } - - pub fn with_wiring( - self, - ) -> EvmBuilder<'a, SetGenericStage, NewEvmWiringT> - where - NewEvmWiringT::Transaction: Default, - NewEvmWiringT::Block: Default, - { - EvmBuilder { - database: None, - external_context: None, - env: Some(Box::new(EnvWiring::::default())), - handler: NewEvmWiringT::handler::<'a>(NewEvmWiringT::Hardfork::default()), - phantom: PhantomData, - } - } - - pub fn reset_handler_with_external_context< - NewEvmWiringT: EvmWiring< - Database = EvmWiringT::Database, - Block = EvmWiringT::Block, - Transaction = EvmWiringT::Transaction, - Hardfork = EvmWiringT::Hardfork, - HaltReason = EvmWiringT::HaltReason, - >, - >( - self, - ) -> EvmBuilder<'a, SetGenericStage, NewEvmWiringT> { - EvmBuilder { - database: self.database, - external_context: None, - env: self.env, - // Handler that will be used by EVM. It contains handle registers - handler: NewEvmWiringT::handler::<'a>(NewEvmWiringT::Hardfork::default()), - phantom: PhantomData, - } - } - - pub fn reset_new_database< - NewEvmWiringT: EvmWiring< - ExternalContext = EvmWiringT::ExternalContext, - Block = EvmWiringT::Block, - Transaction = EvmWiringT::Transaction, - Hardfork = EvmWiringT::Hardfork, - HaltReason = EvmWiringT::HaltReason, - >, - >( - self, - ) -> EvmBuilder<'a, SetGenericStage, NewEvmWiringT> { - EvmBuilder { - database: None, - external_context: self.external_context, - env: self.env, - // Handler that will be used by EVM. It contains handle registers - handler: NewEvmWiringT::handler::<'a>(NewEvmWiringT::Hardfork::default()), - phantom: PhantomData, - } - } -} - -impl<'a, EvmWiringT> EvmBuilder<'a, SetGenericStage, EvmWiringT> -where - EvmWiringT: EvmWiring>>, -{ - /// Creates the default [EvmWiring]::[crate::Database] that will be used by [`Evm`]. - pub fn with_default_db(mut self) -> EvmBuilder<'a, SetGenericStage, EvmWiringT> - where - EvmWiringT::Database: Default, - { - self.database = Some(EvmWiringT::Database::default()); - self - } - - pub fn with_default_ext_ctx(mut self) -> EvmBuilder<'a, SetGenericStage, EvmWiringT> - where - EvmWiringT::ExternalContext: Default, - { - self.external_context = Some(EvmWiringT::ExternalContext::default()); - self - } - - /// Sets the [`crate::Database`] that will be used by [`Evm`]. - pub fn with_db( - mut self, - db: EvmWiringT::Database, - ) -> EvmBuilder<'a, SetGenericStage, EvmWiringT> { - self.database = Some(db); - self - } - - /// Sets the external context that will be used by [`Evm`]. - pub fn with_external_context( - mut self, - external_context: EvmWiringT::ExternalContext, - ) -> EvmBuilder<'a, SetGenericStage, EvmWiringT> { - self.external_context = Some(external_context); - self - } - - // /// Sets Builder with [`EnvWithEvmWiring`]. - // pub fn with_env_with_handler_cfg( - // mut self, - // env_with_handler_cfg: EnvWithEvmWiring, - // ) -> EvmBuilder<'a, HandlerStage, EvmWiringT> { - // let EnvWithEvmWiring { env, spec_id } = env_with_handler_cfg; - // self.context.evm.env = env; - // EvmBuilder { - // context: self.context, - // handler: EvmWiringT::handler::<'a, EXT, DB>(spec_id), - // phantom: PhantomData, - // } - // } - - // /// Sets Builder with [`ContextWithEvmWiring`]. - // pub fn with_context_with_handler_cfg( - // self, - // context_with_handler_cfg: ContextWithEvmWiring, - // ) -> EvmBuilder<'a, HandlerStage, EvmWiringT, OEXT, ODB> { - // EvmBuilder { - // context: context_with_handler_cfg.context, - // handler: EvmWiringT::handler::<'a, OEXT, ODB>(context_with_handler_cfg.spec_id), - // phantom: PhantomData, - // } - // } - - // /// Sets Builder with [`CfgEnvWithEvmWiring`]. - // pub fn with_cfg_env_with_handler_cfg( - // mut self, - // cfg_env_and_spec_id: CfgEnvWithEvmWiring, - // ) -> EvmBuilder<'a, HandlerStage, EvmWiringT> { - // self.context.evm.env.cfg = cfg_env_and_spec_id.cfg_env; - - // EvmBuilder { - // context: self.context, - // handler: EvmWiringT::handler::<'a>(cfg_env_and_spec_id.spec_id), - // phantom: PhantomData, - // } - // } -} - -impl<'a, EvmWiringT: EvmWiring> EvmBuilder<'a, HandlerStage, EvmWiringT> { - // /// Creates new builder from Evm, Evm is consumed and all field are moved to Builder. - // /// It will preserve set handler and context. - // /// - // /// Builder is in HandlerStage and both database and external are set. - // pub fn new(evm: Evm<'a, EvmWiringT>) -> Self { - // Self { - // context: evm.context, - // handler: evm.handler, - // phantom: PhantomData, - // } - // } - // } - - // impl<'a, EvmWiringT: EvmWiring> EvmBuilder<'a, HandlerStage, EvmWiringT> - // where - // EvmWiringT: - // EvmWiring>>, - // { - // /// Sets the [`EmptyDB`] and resets the [`Handler`] to default mainnet. - // pub fn reset_handler_with_empty_db(self) -> EvmBuilder<'a, HandlerStage, EvmWiringT> { - // EvmBuilder { - // context: Context::new( - // self.context.evm.with_db(EmptyDB::default()), - // self.context.external, - // ), - // handler: EvmWiringT::handler::<'a>(self.handler.spec_id()), - // phantom: PhantomData, - // } - // } - - // /// Sets the [`Database`] that will be used by [`Evm`] - // /// and resets the [`Handler`] to default mainnet. - // pub fn reset_handler_with_db( - // self, - // db: ODB, - // ) -> EvmBuilder<'a, SetGenericStage, EvmWiringT, EXT, ODB> { - // EvmBuilder { - // context: Context::new(self.context.evm.with_db(db), self.context.external), - // handler: EvmWiringT::handler::<'a, EXT, ODB>(self.handler.spec_id()), - // phantom: PhantomData, - // } - // } - - // /// Resets [`Handler`] and sets the [`DatabaseRef`] that will be used by [`Evm`] - // /// and resets the [`Handler`] to default mainnet. - // pub fn reset_handler_with_ref_db( - // self, - // db: ODB, - // ) -> EvmBuilder<'a, SetGenericStage, EvmWiringT, EXT, WrapDatabaseRef> { - // EvmBuilder { - // context: Context::new( - // self.context.evm.with_db(WrapDatabaseRef(db)), - // self.context.external, - // ), - // handler: EvmWiringT::handler::<'a, EXT, WrapDatabaseRef>(self.handler.spec_id()), - // phantom: PhantomData, - // } - // } - - // /// Resets [`Handler`] and sets new `ExternalContext` type. - // /// and resets the [`Handler`] to default mainnet. - // pub fn reset_handler_with_external_context( - // self, - // external: OEXT, - // ) -> EvmBuilder<'a, SetGenericStage, EvmWiringT, OEXT, DB> { - // EvmBuilder { - // context: Context::new(self.context.evm, external), - // handler: EvmWiringT::handler::<'a, OEXT, DB>(self.handler.spec_id()), - // phantom: PhantomData, - // } - // } -} - -impl<'a, BuilderStage, EvmWiringT: EvmWiring> EvmBuilder<'a, BuilderStage, EvmWiringT> { - /// This modifies the [EvmBuilder] to make it easy to construct an [`Evm`] with a _specific_ - /// handler. - /// - /// # Example - /// ```rust - /// use revm::{EvmBuilder, EvmHandler}; - /// use wiring::EthereumWiring; - /// use database_interface::EmptyDB; - /// use specification::hardfork::{SpecId,CancunSpec}; - /// - /// let builder = EvmBuilder::default().with_default_db().with_default_ext_ctx(); - /// - /// // get the desired handler - /// let mainnet = EvmHandler::<'_, EthereumWiring>::mainnet_with_spec(SpecId::CANCUN); - /// let builder = builder.with_handler(mainnet); - /// - /// // build the EVM - /// let evm = builder.build(); - /// ``` - pub fn with_handler( - mut self, - handler: Handler<'a, EvmWiringT, Context>, - ) -> EvmBuilder<'a, BuilderStage, EvmWiringT> { - self.handler = handler; - self - } - - /// Builds the [`Evm`]. - pub fn build(self) -> Evm<'a, EvmWiringT> { - Evm::new( - Context::new( - EvmContext::new_with_env(self.database.unwrap(), self.env.unwrap()), - self.external_context.unwrap(), - ), - self.handler, - ) - } - - /// Register Handler that modifies the behavior of EVM. - /// Check [`Handler`] for more information. - /// - /// When called, EvmBuilder will transition from SetGenericStage to HandlerStage. - pub fn append_handler_register( - mut self, - handle_register: register::HandleRegister, - ) -> EvmBuilder<'a, BuilderStage, EvmWiringT> { - self.handler - .append_handler_register(register::HandleRegisters::Plain(handle_register)); - self - } - - /// Register Handler that modifies the behavior of EVM. - /// Check [`Handler`] for more information. - /// - /// When called, EvmBuilder will transition from SetGenericStage to HandlerStage. - pub fn append_handler_register_box( - mut self, - handle_register: register::HandleRegisterBox<'a, EvmWiringT>, - ) -> EvmBuilder<'a, BuilderStage, EvmWiringT> { - self.handler - .append_handler_register(register::HandleRegisters::Box(handle_register)); - self - } - - /// Allows modification of Evm Database. - pub fn modify_db(mut self, f: impl FnOnce(&mut EvmWiringT::Database)) -> Self { - f(self.database.as_mut().unwrap()); - self - } - - /// Allows modification of external context. - pub fn modify_external_context( - mut self, - f: impl FnOnce(&mut EvmWiringT::ExternalContext), - ) -> Self { - f(self.external_context.as_mut().unwrap()); - self - } - - /// Allows modification of Evm Environment. - pub fn modify_env(mut self, f: impl FnOnce(&mut Box>)) -> Self { - f(self.env.as_mut().unwrap()); - self - } - - /// Sets Evm Environment. - pub fn with_env(mut self, env: Box>) -> Self { - self.env = Some(env); - self - } - - /// Allows modification of Evm's Transaction Environment. - pub fn modify_tx_env(mut self, f: impl FnOnce(&mut EvmWiringT::Transaction)) -> Self { - f(&mut self.env.as_mut().unwrap().tx); - self - } - - /// Sets Evm's Transaction Environment. - pub fn with_tx_env(mut self, tx_env: EvmWiringT::Transaction) -> Self { - self.env.as_mut().unwrap().tx = tx_env; - self - } - - /// Allows modification of Evm's Block Environment. - pub fn modify_block_env(mut self, f: impl FnOnce(&mut EvmWiringT::Block)) -> Self { - f(&mut self.env.as_mut().unwrap().block); - self - } - - /// Sets Evm's Block Environment. - pub fn with_block_env(mut self, block_env: EvmWiringT::Block) -> Self { - self.env.as_mut().unwrap().block = block_env; - self - } - - /// Allows modification of Evm's Config Environment. - pub fn modify_cfg_env(mut self, f: impl FnOnce(&mut CfgEnv)) -> Self { - f(&mut self.env.as_mut().unwrap().cfg); - self - } -} - -impl<'a, BuilderStage, EvmWiringT> EvmBuilder<'a, BuilderStage, EvmWiringT> -where - EvmWiringT: EvmWiring, -{ - /// Clears Block environment of EVM. - pub fn with_clear_block_env(mut self) -> Self { - self.env.as_mut().unwrap().block = EvmWiringT::Block::default(); - self - } -} - -impl<'a, BuilderStage, EvmWiringT> EvmBuilder<'a, BuilderStage, EvmWiringT> -where - EvmWiringT: EvmWiring, -{ - /// Clears Transaction environment of EVM. - pub fn with_clear_tx_env(mut self) -> Self { - self.env.as_mut().unwrap().tx = EvmWiringT::Transaction::default(); - self - } -} - -impl<'a, BuilderStage, EvmWiringT> EvmBuilder<'a, BuilderStage, EvmWiringT> -where - EvmWiringT: EvmWiring, -{ - /// Clears Environment of EVM. - pub fn with_clear_env(mut self) -> Self { - self.env.as_mut().unwrap().clear(); - self - } -} - -impl<'a, BuilderStage, EvmWiringT: EvmWiring> EvmBuilder<'a, BuilderStage, EvmWiringT> -where - EvmWiringT: EvmWiring>>, -{ - /// Sets specification Id , that will mark the version of EVM. - /// It represent the hard fork of ethereum. - /// - /// # Note - /// - /// When changed it will reapply all handle registers, this can be - /// expensive operation depending on registers. - pub fn with_spec_id(mut self, spec_id: EvmWiringT::Hardfork) -> Self { - self.handler.modify_spec_id(spec_id); - self - } - - /// Resets [`Handler`] to default mainnet. - pub fn reset_handler(mut self) -> Self { - self.handler = EvmWiringT::handler::<'a>(self.handler.spec_id()); - self - } -} - -#[cfg(test)] -mod test { - extern crate alloc; - - use crate::{Context, Evm}; - use alloc::{boxed::Box, rc::Rc}; - use bytecode::Bytecode; - use core::cell::RefCell; - use database::InMemoryDB; - use interpreter::Interpreter; - use primitives::{address, TxKind, U256}; - use state::AccountInfo; - use wiring::EthereumWiring; - - /// Custom evm context - #[derive(Default, Clone, Debug)] - pub(crate) struct CustomContext { - pub(crate) inner: Rc>, - } - - #[test] - fn simple_add_stateful_instruction() { - let code = Bytecode::new_raw([0xED, 0x00].into()); - let code_hash = code.hash_slow(); - let to_addr = address!("ffffffffffffffffffffffffffffffffffffffff"); - - // initialize the custom context and make sure it's zero - let custom_context = CustomContext::default(); - assert_eq!(*custom_context.inner.borrow(), 0); - - let to_capture = custom_context.clone(); - let mut evm = Evm::>::builder() - .with_default_db() - .with_default_ext_ctx() - .modify_db(|db| { - db.insert_account_info( - to_addr, - AccountInfo::new(U256::from(1_000_000), 0, code_hash, code), - ) - }) - .modify_tx_env(|tx| { - tx.transact_to = TxKind::Call(to_addr); - tx.gas_limit = 100_000; - }) - // we need to use handle register box to capture the custom context in the handle - // register - .append_handler_register_box(Box::new(move |handler| { - let custom_context = to_capture.clone(); - - // we need to use a box to capture the custom context in the instruction - let custom_instruction = - Box::new(move |_interp: &mut Interpreter, _host: &mut Context<_>| { - // modify the value - let mut inner = custom_context.inner.borrow_mut(); - *inner += 1; - }); - - // need to ensure the instruction table is a boxed instruction table so that we - // can insert the custom instruction as a boxed instruction - handler - .instruction_table - .insert_boxed(0xED, custom_instruction); - })) - .build(); - - let _result_and_state = evm.transact().unwrap(); - - // ensure the custom context was modified - assert_eq!(*custom_context.inner.borrow(), 1); - } - - // #[test] - // fn simple_add_instruction() { - // const CUSTOM_INSTRUCTION_COST: u64 = 133; - // const INITIAL_TX_GAS: u64 = 21000; - // const EXPECTED_RESULT_GAS: u64 = INITIAL_TX_GAS + CUSTOM_INSTRUCTION_COST; - - // fn custom_instruction(interp: &mut Interpreter, _host: &mut impl Host) { - // // just spend some gas - // gas!(interp, CUSTOM_INSTRUCTION_COST); - // } - - // let code = Bytecode::new_raw([0xED, 0x00].into()); - // let code_hash = code.hash_slow(); - // let to_addr = address!("ffffffffffffffffffffffffffffffffffffffff"); - - // let mut evm = Evm::builder() - // .with_wiring::>() - // .with_db(InMemoryDB::default()) - // .modify_db(|db| { - // db.insert_account_info(to_addr, AccountInfo::new(U256::ZERO, 0, code_hash, code)) - // }) - // .modify_tx_env(|tx| { - // let transact_to = &mut tx.transact_to; - - // *transact_to = TxKind::Call(to_addr) - // }) - // .append_handler_register(|handler| { - // handler.instruction_table.insert(0xED, custom_instruction) - // }) - // .build(); - - // let result_and_state = evm.transact().unwrap(); - // assert_eq!(result_and_state.result.gas_used(), EXPECTED_RESULT_GAS); - // } - - // #[test] - // fn simple_build() { - // // build without external with latest spec - // Evm::builder().with_chain_spec::().build(); - // // build with empty db - // Evm::builder() - // .with_chain_spec::() - // .with_empty_db() - // .build(); - // // build with_db - // Evm::builder() - // .with_chain_spec::() - // .with_db(EmptyDB::default()) - // .build(); - // // build with empty external - // Evm::builder() - // .with_chain_spec::() - // .with_empty_db() - // .build(); - // // build with some external - // Evm::builder() - // .with_chain_spec::() - // .with_empty_db() - // .with_external_context(()) - // .build(); - // // build with spec - // Evm::builder() - // .with_empty_db() - // .with_spec_id(SpecId::HOMESTEAD) - // .build(); - - // // with with Env change in multiple places - // Evm::builder() - // .with_chain_spec::() - // .with_empty_db() - // .modify_tx_env(|tx| tx.gas_limit = 10) - // .build(); - // Evm::builder() - // .with_chain_spec::() - // .modify_tx_env(|tx| tx.gas_limit = 10) - // .build(); - // Evm::builder() - // .with_chain_spec::() - // .with_empty_db() - // .modify_tx_env(|tx| tx.gas_limit = 10) - // .build(); - // Evm::builder() - // .with_chain_spec::() - // .with_empty_db() - // .modify_tx_env(|tx| tx.gas_limit = 10) - // .build(); - - // // with inspector handle - // Evm::builder() - // .with_chain_spec::() - // .with_empty_db() - // .with_external_context(NoOpInspector) - // .append_handler_register(inspector_handle_register) - // .build(); - - // // create the builder - // let evm = Evm::builder() - // .with_db(EmptyDB::default()) - // .with_chain_spec::() - // .with_external_context(NoOpInspector) - // .append_handler_register(inspector_handle_register) - // // this would not compile - // // .with_db(..) - // .build(); - - // let Context { external: _, .. } = evm.into_context(); - // } - - // #[test] - // fn build_modify_build() { - // // build evm - // let evm = Evm::builder() - // .with_empty_db() - // .with_spec_id(SpecId::HOMESTEAD) - // .build(); - - // // modify evm - // let evm = evm.modify().with_spec_id(SpecId::FRONTIER).build(); - // let _ = evm - // .modify() - // .modify_tx_env(|tx| tx.chain_id = Some(2)) - // .build(); - // } - - // #[test] - // fn build_custom_precompile() { - // struct CustomPrecompile; - - // impl ContextStatefulPrecompile for CustomPrecompile { - // fn call( - // &self, - // _input: &Bytes, - // _gas_limit: u64, - // _context: &mut InnerEvmContext, - // ) -> PrecompileResult { - // Ok(PrecompileOutput::new(10, Bytes::new())) - // } - // } - - // let spec_id = crate::primitives::SpecId::HOMESTEAD; - - // let mut evm = Evm::builder() - // .with_chain_spec::() - // .with_spec_id(spec_id) - // .append_handler_register(|handler| { - // let precompiles = handler.pre_execution.load_precompiles(); - // handler.pre_execution.load_precompiles = Arc::new(move || { - // let mut precompiles = precompiles.clone(); - // precompiles.extend([( - // Address::ZERO, - // ContextPrecompile::ContextStateful(Arc::new(CustomPrecompile)), - // )]); - // precompiles - // }); - // }) - // .build(); - - // evm.transact().unwrap(); - // } -} diff --git a/crates/revm/src/context.rs b/crates/revm/src/context.rs deleted file mode 100644 index ea6da4bc1a..0000000000 --- a/crates/revm/src/context.rs +++ /dev/null @@ -1,188 +0,0 @@ -mod context_precompiles; -pub(crate) mod evm_context; -mod inner_evm_context; - -pub use context_precompiles::{ - ContextPrecompile, ContextPrecompiles, ContextStatefulPrecompile, ContextStatefulPrecompileArc, - ContextStatefulPrecompileBox, ContextStatefulPrecompileMut, -}; -use derive_where::derive_where; -pub use evm_context::EvmContext; -pub use inner_evm_context::InnerEvmContext; - -use crate::EvmWiring; -use database_interface::{Database, EmptyDB}; -use interpreter::{ - as_u64_saturated, AccountLoad, Eip7702CodeLoad, Host, SStoreResult, SelfDestructResult, - StateLoad, -}; -use primitives::{Address, Bytes, Log, B256, BLOCK_HASH_HISTORY, U256}; -use std::boxed::Box; -use wiring::{default::EnvWiring, Block, EthereumWiring}; - -/// Main Context structure that contains both EvmContext and External context. -#[derive_where(Clone; EvmWiringT::Block, EvmWiringT::ChainContext, EvmWiringT::Transaction, EvmWiringT::Database, ::Error, EvmWiringT::ExternalContext)] -pub struct Context { - /// Evm Context (internal context). - pub evm: EvmContext, - /// External contexts. - pub external: EvmWiringT::ExternalContext, -} - -impl Default for Context> { - fn default() -> Self { - Context { - evm: EvmContext::new(EmptyDB::new()), - external: (), - } - } -} - -impl Context -where - EvmWiringT: - EvmWiring, -{ - /// Creates new context with database. - pub fn new_with_db(db: DB) -> Context { - Context { - evm: EvmContext::new_with_env(db, Box::default()), - external: (), - } - } -} - -impl Context { - /// Creates new context with external and database. - pub fn new( - evm: EvmContext, - external: EvmWiringT::ExternalContext, - ) -> Context { - Context { evm, external } - } -} - -/// Context with handler configuration. -#[derive_where(Clone; EvmWiringT::Block, EvmWiringT::ChainContext, EvmWiringT::Transaction,EvmWiringT::Database, ::Error, EvmWiringT::ExternalContext)] -pub struct ContextWithEvmWiring { - /// Context of execution. - pub context: Context, - /// Handler configuration. - pub spec_id: EvmWiringT::Hardfork, -} - -impl ContextWithEvmWiring { - /// Creates new context with handler configuration. - pub fn new(context: Context, spec_id: EvmWiringT::Hardfork) -> Self { - Self { spec_id, context } - } -} - -impl Host for Context { - type EvmWiringT = EvmWiringT; - - /// Returns reference to Environment. - #[inline] - fn env(&self) -> &EnvWiring { - &self.evm.env - } - - fn env_mut(&mut self) -> &mut EnvWiring { - &mut self.evm.env - } - - fn block_hash(&mut self, requested_number: u64) -> Option { - let block_number = as_u64_saturated!(*self.env().block.number()); - - let Some(diff) = block_number.checked_sub(requested_number) else { - return Some(B256::ZERO); - }; - - // blockhash should push zero if number is same as current block number. - if diff == 0 { - return Some(B256::ZERO); - } - - if diff <= BLOCK_HASH_HISTORY { - return self - .evm - .block_hash(requested_number) - .map_err(|e| self.evm.error = Err(e)) - .ok(); - } - - Some(B256::ZERO) - } - - fn load_account_delegated(&mut self, address: Address) -> Option { - self.evm - .load_account_delegated(address) - .map_err(|e| self.evm.error = Err(e)) - .ok() - } - - fn balance(&mut self, address: Address) -> Option> { - self.evm - .balance(address) - .map_err(|e| self.evm.error = Err(e)) - .ok() - } - - fn code(&mut self, address: Address) -> Option> { - self.evm - .code(address) - .map_err(|e| self.evm.error = Err(e)) - .ok() - } - - fn code_hash(&mut self, address: Address) -> Option> { - self.evm - .code_hash(address) - .map_err(|e| self.evm.error = Err(e)) - .ok() - } - - fn sload(&mut self, address: Address, index: U256) -> Option> { - self.evm - .sload(address, index) - .map_err(|e| self.evm.error = Err(e)) - .ok() - } - - fn sstore( - &mut self, - address: Address, - index: U256, - value: U256, - ) -> Option> { - self.evm - .sstore(address, index, value) - .map_err(|e| self.evm.error = Err(e)) - .ok() - } - - fn tload(&mut self, address: Address, index: U256) -> U256 { - self.evm.tload(address, index) - } - - fn tstore(&mut self, address: Address, index: U256, value: U256) { - self.evm.tstore(address, index, value) - } - - fn log(&mut self, log: Log) { - self.evm.journaled_state.log(log); - } - - fn selfdestruct( - &mut self, - address: Address, - target: Address, - ) -> Option> { - self.evm - .inner - .journaled_state - .selfdestruct(address, target, &mut self.evm.inner.db) - .map_err(|e| self.evm.error = Err(e)) - .ok() - } -} diff --git a/crates/revm/src/context/context_precompiles.rs b/crates/revm/src/context/context_precompiles.rs deleted file mode 100644 index e14bb71823..0000000000 --- a/crates/revm/src/context/context_precompiles.rs +++ /dev/null @@ -1,242 +0,0 @@ -use super::InnerEvmContext; -use core::fmt::Debug; -use derive_where::derive_where; -use dyn_clone::DynClone; -use precompile::{ - Precompile, PrecompileResult, PrecompileSpecId, PrecompileWithAddress, Precompiles, -}; -use primitives::{Address, Bytes, HashMap, HashSet}; -use std::{boxed::Box, sync::Arc}; -use wiring::EvmWiring; - -/// A single precompile handler. -#[derive_where(Clone)] -pub enum ContextPrecompile { - /// Ordinary precompiles - Ordinary(Precompile), - /// Stateful precompile that is Arc over [`ContextStatefulPrecompile`] trait. - /// It takes a reference to input, gas limit and Context. - ContextStateful(ContextStatefulPrecompileArc), - /// Mutable stateful precompile that is Box over [`ContextStatefulPrecompileMut`] trait. - /// It takes a reference to input, gas limit and context. - ContextStatefulMut(ContextStatefulPrecompileBox), -} - -impl Debug for ContextPrecompile { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - Self::Ordinary(p) => f.debug_tuple("Ordinary").field(p).finish(), - Self::ContextStateful(_) => f.debug_tuple("ContextStateful").finish(), - Self::ContextStatefulMut(_) => f.debug_tuple("ContextStatefulMut").finish(), - } - } -} - -#[derive_where(Clone, Debug)] -enum PrecompilesCow { - /// Default precompiles, returned by `Precompiles::new`. Used to fast-path the default case. - StaticRef(&'static Precompiles), - Owned(HashMap>), -} - -/// Precompiles context. - -#[derive_where(Clone, Debug, Default)] -pub struct ContextPrecompiles { - inner: PrecompilesCow, -} - -impl ContextPrecompiles { - /// Creates a new precompiles context at the given spec ID. - /// - /// This is a cheap operation that does not allocate by reusing the global precompiles. - #[inline] - pub fn new(spec_id: PrecompileSpecId) -> Self { - Self::from_static_precompiles(Precompiles::new(spec_id)) - } - - /// Creates a new precompiles context from the given static precompiles. - /// - /// NOTE: The internal precompiles must not be `StatefulMut` or `call` will panic. - /// This is done because the default precompiles are not stateful. - #[inline] - pub fn from_static_precompiles(precompiles: &'static Precompiles) -> Self { - Self { - inner: PrecompilesCow::StaticRef(precompiles), - } - } - - /// Creates a new precompiles context from the given precompiles. - #[inline] - pub fn from_precompiles(precompiles: HashMap>) -> Self { - Self { - inner: PrecompilesCow::Owned(precompiles), - } - } - - /// Returns precompiles addresses as a HashSet. - pub fn addresses_set(&self) -> HashSet
{ - match self.inner { - PrecompilesCow::StaticRef(inner) => inner.addresses_set().clone(), - PrecompilesCow::Owned(ref inner) => inner.keys().cloned().collect(), - } - } - - /// Returns precompiles addresses. - #[inline] - pub fn addresses(&self) -> Box + '_> { - match self.inner { - PrecompilesCow::StaticRef(inner) => Box::new(inner.addresses()), - PrecompilesCow::Owned(ref inner) => Box::new(inner.keys()), - } - } - - /// Returns `true` if the precompiles contains the given address. - #[inline] - pub fn contains(&self, address: &Address) -> bool { - match self.inner { - PrecompilesCow::StaticRef(inner) => inner.contains(address), - PrecompilesCow::Owned(ref inner) => inner.contains_key(address), - } - } - - /// Call precompile and executes it. Returns the result of the precompile execution. - /// - /// Returns `None` if the precompile does not exist. - #[inline] - pub fn call( - &mut self, - address: &Address, - bytes: &Bytes, - gas_limit: u64, - evmctx: &mut InnerEvmContext, - ) -> Option { - Some(match self.inner { - PrecompilesCow::StaticRef(p) => { - p.get(address)?.call_ref(bytes, gas_limit, &evmctx.env.cfg) - } - PrecompilesCow::Owned(ref mut owned) => match owned.get_mut(address)? { - ContextPrecompile::Ordinary(p) => p.call(bytes, gas_limit, &evmctx.env.cfg), - ContextPrecompile::ContextStateful(p) => p.call(bytes, gas_limit, evmctx), - ContextPrecompile::ContextStatefulMut(p) => p.call_mut(bytes, gas_limit, evmctx), - }, - }) - } - - /// Returns a mutable reference to the precompiles map. - /// - /// Clones the precompiles map if it is shared. - #[inline] - pub fn to_mut(&mut self) -> &mut HashMap> { - if let PrecompilesCow::StaticRef(_) = self.inner { - self.mutate_into_owned(); - } - - let PrecompilesCow::Owned(inner) = &mut self.inner else { - unreachable!("self is mutated to Owned.") - }; - inner - } - - /// Mutates Self into Owned variant, or do nothing if it is already Owned. - /// Mutation will clone all precompiles. - #[cold] - fn mutate_into_owned(&mut self) { - let PrecompilesCow::StaticRef(precompiles) = self.inner else { - return; - }; - self.inner = PrecompilesCow::Owned( - precompiles - .inner() - .iter() - .map(|(k, v)| (*k, v.clone().into())) - .collect(), - ); - } -} - -impl Extend<(Address, ContextPrecompile)> - for ContextPrecompiles -{ - fn extend)>>( - &mut self, - iter: T, - ) { - self.to_mut().extend(iter.into_iter().map(Into::into)) - } -} - -impl Extend for ContextPrecompiles { - fn extend>(&mut self, iter: T) { - self.to_mut().extend(iter.into_iter().map(|precompile| { - let (address, precompile) = precompile.into(); - (address, precompile.into()) - })); - } -} - -impl Default for PrecompilesCow { - fn default() -> Self { - Self::Owned(Default::default()) - } -} - -/// Context aware stateful precompile trait. It is used to create -/// a arc precompile in [`ContextPrecompile`]. -pub trait ContextStatefulPrecompile: Sync + Send { - fn call( - &self, - bytes: &Bytes, - gas_limit: u64, - evmctx: &mut InnerEvmContext, - ) -> PrecompileResult; -} - -/// Context aware mutable stateful precompile trait. It is used to create -/// a boxed precompile in [`ContextPrecompile`]. -pub trait ContextStatefulPrecompileMut: DynClone + Send + Sync { - fn call_mut( - &mut self, - bytes: &Bytes, - gas_limit: u64, - evmctx: &mut InnerEvmContext, - ) -> PrecompileResult; -} - -dyn_clone::clone_trait_object!( ContextStatefulPrecompileMut); - -/// Arc over context stateful precompile. -pub type ContextStatefulPrecompileArc = Arc>; - -/// Box over context mutable stateful precompile -pub type ContextStatefulPrecompileBox = - Box>; - -impl From for ContextPrecompile { - fn from(p: Precompile) -> Self { - ContextPrecompile::Ordinary(p) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use wiring::DefaultEthereumWiring; - - #[test] - fn test_precompiles_context() { - let custom_address = Address::with_last_byte(0xff); - - let mut precompiles = - ContextPrecompiles::::new(PrecompileSpecId::HOMESTEAD); - assert_eq!(precompiles.addresses().count(), 4); - assert!(matches!(precompiles.inner, PrecompilesCow::StaticRef(_))); - assert!(!precompiles.contains(&custom_address)); - - let precompile = Precompile::Standard(|_, _| panic!()); - precompiles.extend([(custom_address, precompile.into())]); - assert_eq!(precompiles.addresses().count(), 5); - assert!(matches!(precompiles.inner, PrecompilesCow::Owned(_))); - assert!(precompiles.contains(&custom_address)); - } -} diff --git a/crates/revm/src/context/evm_context.rs b/crates/revm/src/context/evm_context.rs deleted file mode 100644 index ce60aa07e7..0000000000 --- a/crates/revm/src/context/evm_context.rs +++ /dev/null @@ -1,653 +0,0 @@ -use super::inner_evm_context::InnerEvmContext; -use crate::{ContextPrecompiles, EvmWiring, FrameOrResult, CALL_STACK_LIMIT}; -use bytecode::{Bytecode, Eof, EOF_MAGIC_BYTES}; -use core::ops::{Deref, DerefMut}; -use database_interface::Database; -use derive_where::derive_where; -use interpreter::CallValue; -use interpreter::{ - return_ok, CallInputs, Contract, CreateInputs, EOFCreateInputs, EOFCreateKind, Gas, - InstructionResult, Interpreter, InterpreterResult, -}; -use precompile::PrecompileErrors; -use primitives::{keccak256, Address, Bytes, B256}; -use specification::hardfork::SpecId::{self, *}; -use std::{boxed::Box, sync::Arc}; -use wiring::{ - default::{CreateScheme, EnvWiring}, - result::{EVMError, EVMResultGeneric}, - Transaction, -}; - -/// EVM context that contains the inner EVM context and precompiles. -#[derive_where(Clone, Debug; EvmWiringT::Block, EvmWiringT::ChainContext, EvmWiringT::Transaction, EvmWiringT::Database, ::Error)] -pub struct EvmContext { - /// Inner EVM context. - pub inner: InnerEvmContext, - /// Precompiles that are available for evm. - pub precompiles: ContextPrecompiles, -} - -impl Deref for EvmContext { - type Target = InnerEvmContext; - - fn deref(&self) -> &Self::Target { - &self.inner - } -} - -impl DerefMut for EvmContext { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.inner - } -} - -impl EvmContext -where - EvmWiringT: EvmWiring, -{ - /// Create new context with database. - pub fn new(db: EvmWiringT::Database) -> Self { - Self { - inner: InnerEvmContext::new(db), - precompiles: ContextPrecompiles::default(), - } - } -} - -impl EvmContext -where - EvmWiringT: EvmWiring, -{ - /// Creates a new context with the given environment and database. - #[inline] - pub fn new_with_env(db: EvmWiringT::Database, env: Box>) -> Self { - Self { - inner: InnerEvmContext::new_with_env(db, env), - precompiles: ContextPrecompiles::default(), - } - } - - /// Sets the database. - /// - /// Note that this will ignore the previous `error` if set. - #[inline] - pub fn with_db< - OEvmWiring: EvmWiring, - >( - self, - db: OEvmWiring::Database, - ) -> EvmContext { - EvmContext { - inner: self.inner.with_db(db), - precompiles: ContextPrecompiles::default(), - } - } - - /// Sets precompiles - #[inline] - pub fn set_precompiles(&mut self, precompiles: ContextPrecompiles) { - // set warm loaded addresses. - self.journaled_state - .warm_preloaded_addresses - .extend(precompiles.addresses_set()); - self.precompiles = precompiles; - } - - /// Call precompile contract - #[inline] - fn call_precompile( - &mut self, - address: &Address, - input_data: &Bytes, - gas: Gas, - ) -> EVMResultGeneric, EvmWiringT> { - let Some(outcome) = - self.precompiles - .call(address, input_data, gas.limit(), &mut self.inner) - else { - return Ok(None); - }; - - let mut result = InterpreterResult { - result: InstructionResult::Return, - gas, - output: Bytes::new(), - }; - - match outcome { - Ok(output) => { - if result.gas.record_cost(output.gas_used) { - result.result = InstructionResult::Return; - result.output = output.bytes; - } else { - result.result = InstructionResult::PrecompileOOG; - } - } - Err(PrecompileErrors::Error(e)) => { - result.result = if e.is_oog() { - InstructionResult::PrecompileOOG - } else { - InstructionResult::PrecompileError - }; - } - Err(PrecompileErrors::Fatal { msg }) => return Err(EVMError::Precompile(msg)), - } - Ok(Some(result)) - } - - /// Make call frame - #[inline] - pub fn make_call_frame( - &mut self, - inputs: &CallInputs, - ) -> EVMResultGeneric { - let gas = Gas::new(inputs.gas_limit); - - let return_result = |instruction_result: InstructionResult| { - Ok(FrameOrResult::new_call_result( - InterpreterResult { - result: instruction_result, - gas, - output: Bytes::new(), - }, - inputs.return_memory_offset.clone(), - )) - }; - - // Check depth - if self.journaled_state.depth() > CALL_STACK_LIMIT { - return return_result(InstructionResult::CallTooDeep); - } - - // Make account warm and loaded - let _ = self - .inner - .journaled_state - .load_account_delegated(inputs.bytecode_address, &mut self.inner.db) - .map_err(EVMError::Database)?; - - // Create subroutine checkpoint - let checkpoint = self.journaled_state.checkpoint(); - - // Touch address. For "EIP-158 State Clear", this will erase empty accounts. - match inputs.value { - // if transfer value is zero, load account and force the touch. - CallValue::Transfer(value) if value.is_zero() => { - self.load_account(inputs.target_address) - .map_err(EVMError::Database)?; - self.journaled_state.touch(&inputs.target_address); - } - CallValue::Transfer(value) => { - // Transfer value from caller to called account. As value get transferred - // target gets touched. - if let Some(result) = self - .inner - .journaled_state - .transfer( - &inputs.caller, - &inputs.target_address, - value, - &mut self.inner.db, - ) - .map_err(EVMError::Database)? - { - self.journaled_state.checkpoint_revert(checkpoint); - return return_result(result); - } - } - _ => {} - }; - - if let Some(result) = self.call_precompile(&inputs.bytecode_address, &inputs.input, gas)? { - if matches!(result.result, return_ok!()) { - self.journaled_state.checkpoint_commit(); - } else { - self.journaled_state.checkpoint_revert(checkpoint); - } - Ok(FrameOrResult::new_call_result( - result, - inputs.return_memory_offset.clone(), - )) - } else { - let account = self - .inner - .journaled_state - .load_code(inputs.bytecode_address, &mut self.inner.db) - .map_err(EVMError::Database)?; - - let code_hash = account.info.code_hash(); - let mut bytecode = account.info.code.clone().unwrap_or_default(); - - // ExtDelegateCall is not allowed to call non-EOF contracts. - if inputs.scheme.is_ext_delegate_call() - && !bytecode.bytes_slice().starts_with(&EOF_MAGIC_BYTES) - { - return return_result(InstructionResult::InvalidExtDelegateCallTarget); - } - - if bytecode.is_empty() { - self.journaled_state.checkpoint_commit(); - return return_result(InstructionResult::Stop); - } - - if let Bytecode::Eip7702(eip7702_bytecode) = bytecode { - bytecode = self - .inner - .journaled_state - .load_code(eip7702_bytecode.delegated_address, &mut self.inner.db) - .map_err(EVMError::Database)? - .info - .code - .clone() - .unwrap_or_default(); - } - - let contract = - Contract::new_with_context(inputs.input.clone(), bytecode, Some(code_hash), inputs); - // Create interpreter and executes call and push new CallStackFrame. - Ok(FrameOrResult::new_call_frame( - inputs.return_memory_offset.clone(), - checkpoint, - Interpreter::new(contract, gas.limit(), inputs.is_static), - )) - } - } - - /// Make create frame. - #[inline] - pub fn make_create_frame( - &mut self, - spec_id: SpecId, - inputs: &CreateInputs, - ) -> Result::Error> { - let return_error = |e| { - Ok(FrameOrResult::new_create_result( - InterpreterResult { - result: e, - gas: Gas::new(inputs.gas_limit), - output: Bytes::new(), - }, - None, - )) - }; - - // Check depth - if self.journaled_state.depth() > CALL_STACK_LIMIT { - return return_error(InstructionResult::CallTooDeep); - } - - // Prague EOF - if spec_id.is_enabled_in(PRAGUE_EOF) && inputs.init_code.starts_with(&EOF_MAGIC_BYTES) { - return return_error(InstructionResult::CreateInitCodeStartingEF00); - } - - // Fetch balance of caller. - let caller_balance = self.balance(inputs.caller)?; - - // Check if caller has enough balance to send to the created contract. - if caller_balance.data < inputs.value { - return return_error(InstructionResult::OutOfFunds); - } - - // Increase nonce of caller and check if it overflows - let old_nonce; - if let Some(nonce) = self.journaled_state.inc_nonce(inputs.caller) { - old_nonce = nonce - 1; - } else { - return return_error(InstructionResult::Return); - } - - // Create address - let mut init_code_hash = B256::ZERO; - let created_address = match inputs.scheme { - CreateScheme::Create => inputs.caller.create(old_nonce), - CreateScheme::Create2 { salt } => { - init_code_hash = keccak256(&inputs.init_code); - inputs.caller.create2(salt.to_be_bytes(), init_code_hash) - } - }; - - // created address is not allowed to be a precompile. - if self.precompiles.contains(&created_address) { - return return_error(InstructionResult::CreateCollision); - } - - // warm load account. - self.load_account(created_address)?; - - // create account, transfer funds and make the journal checkpoint. - let checkpoint = match self.journaled_state.create_account_checkpoint( - inputs.caller, - created_address, - inputs.value, - spec_id, - ) { - Ok(checkpoint) => checkpoint, - Err(e) => { - return return_error(e); - } - }; - - let bytecode = Bytecode::new_legacy(inputs.init_code.clone()); - - let contract = Contract::new( - Bytes::new(), - bytecode, - Some(init_code_hash), - created_address, - None, - inputs.caller, - inputs.value, - ); - - Ok(FrameOrResult::new_create_frame( - created_address, - checkpoint, - Interpreter::new(contract, inputs.gas_limit, false), - )) - } - - /// Make create frame. - #[inline] - pub fn make_eofcreate_frame( - &mut self, - spec_id: SpecId, - inputs: &EOFCreateInputs, - ) -> Result::Error> { - let return_error = |e| { - Ok(FrameOrResult::new_eofcreate_result( - InterpreterResult { - result: e, - gas: Gas::new(inputs.gas_limit), - output: Bytes::new(), - }, - None, - )) - }; - - let (input, initcode, created_address) = match &inputs.kind { - EOFCreateKind::Opcode { - initcode, - input, - created_address, - } => (input.clone(), initcode.clone(), Some(*created_address)), - EOFCreateKind::Tx { initdata } => { - // decode eof and init code. - // TODO handle inc_nonce handling more gracefully. - let Ok((eof, input)) = Eof::decode_dangling(initdata.clone()) else { - self.journaled_state.inc_nonce(inputs.caller); - return return_error(InstructionResult::InvalidEOFInitCode); - }; - - if eof.validate().is_err() { - // TODO (EOF) new error type. - self.journaled_state.inc_nonce(inputs.caller); - return return_error(InstructionResult::InvalidEOFInitCode); - } - - // Use nonce from tx to calculate address. - let tx = self.env.tx.common_fields(); - let create_address = tx.caller().create(tx.nonce()); - - (input, eof, Some(create_address)) - } - }; - - // Check depth - if self.journaled_state.depth() > CALL_STACK_LIMIT { - return return_error(InstructionResult::CallTooDeep); - } - - // Fetch balance of caller. - let caller_balance = self.balance(inputs.caller)?; - - // Check if caller has enough balance to send to the created contract. - if caller_balance.data < inputs.value { - return return_error(InstructionResult::OutOfFunds); - } - - // Increase nonce of caller and check if it overflows - let Some(nonce) = self.journaled_state.inc_nonce(inputs.caller) else { - // can't happen on mainnet. - return return_error(InstructionResult::Return); - }; - let old_nonce = nonce - 1; - - let created_address = created_address.unwrap_or_else(|| inputs.caller.create(old_nonce)); - - // created address is not allowed to be a precompile. - if self.precompiles.contains(&created_address) { - return return_error(InstructionResult::CreateCollision); - } - - // Load account so it needs to be marked as warm for access list. - self.load_account(created_address)?; - - // create account, transfer funds and make the journal checkpoint. - let checkpoint = match self.journaled_state.create_account_checkpoint( - inputs.caller, - created_address, - inputs.value, - spec_id, - ) { - Ok(checkpoint) => checkpoint, - Err(e) => { - return return_error(e); - } - }; - - let contract = Contract::new( - input.clone(), - // fine to clone as it is Bytes. - Bytecode::Eof(Arc::new(initcode.clone())), - None, - created_address, - None, - inputs.caller, - inputs.value, - ); - - let mut interpreter = Interpreter::new(contract, inputs.gas_limit, false); - // EOF init will enable RETURNCONTRACT opcode. - interpreter.set_is_eof_init(); - - Ok(FrameOrResult::new_eofcreate_frame( - created_address, - checkpoint, - interpreter, - )) - } -} - -/// Test utilities for the [`EvmContext`]. -#[cfg(any(test, feature = "test-utils"))] -pub(crate) mod test_utils { - use super::*; - use crate::journaled_state::JournaledState; - use database::CacheDB; - use database_interface::EmptyDB; - use interpreter::CallScheme; - use primitives::{address, HashSet, B256, U256}; - use specification::hardfork::SpecId; - use state::AccountInfo; - - /// Mock caller address. - pub const MOCK_CALLER: Address = address!("0000000000000000000000000000000000000000"); - - /// Creates `CallInputs` that calls a provided contract address from the mock caller. - pub fn create_mock_call_inputs(to: Address) -> CallInputs { - CallInputs { - input: Bytes::new(), - gas_limit: 0, - bytecode_address: to, - target_address: to, - caller: MOCK_CALLER, - value: CallValue::Transfer(U256::ZERO), - scheme: CallScheme::Call, - is_eof: false, - is_static: false, - return_memory_offset: 0..0, - } - } - - /// Creates an evm context with a cache db backend. - /// Additionally loads the mock caller account into the db, - /// and sets the balance to the provided U256 value. - pub fn create_cache_db_evm_context_with_balance< - EvmWiringT: EvmWiring>, - >( - env: Box>, - mut db: CacheDB, - balance: U256, - ) -> EvmContext { - db.insert_account_info( - test_utils::MOCK_CALLER, - AccountInfo { - nonce: 0, - balance, - code_hash: B256::default(), - code: None, - }, - ); - create_cache_db_evm_context(env, db) - } - - /// Creates a cached db evm context. - pub fn create_cache_db_evm_context>>( - env: Box>, - db: CacheDB, - ) -> EvmContext { - EvmContext { - inner: InnerEvmContext { - env, - journaled_state: JournaledState::new(SpecId::CANCUN, HashSet::default()), - db, - chain: Default::default(), - error: Ok(()), - }, - precompiles: ContextPrecompiles::default(), - } - } - - /// Returns a new `EvmContext` with an empty journaled state. - pub fn create_empty_evm_context>( - env: Box>, - db: EmptyDB, - ) -> EvmContext { - EvmContext { - inner: InnerEvmContext { - env, - journaled_state: JournaledState::new(SpecId::CANCUN, HashSet::default()), - db, - chain: Default::default(), - error: Ok(()), - }, - precompiles: ContextPrecompiles::default(), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{Frame, JournalEntry}; - use bytecode::Bytecode; - use database::CacheDB; - use database_interface::EmptyDB; - use primitives::{address, U256}; - use state::AccountInfo; - use std::boxed::Box; - use test_utils::*; - use wiring::{DefaultEthereumWiring, EthereumWiring}; - - // Tests that the `EVMContext::make_call_frame` function returns an error if the - // call stack is too deep. - #[test] - fn test_make_call_frame_stack_too_deep() { - let env = EnvWiring::::default(); - let db = EmptyDB::default(); - let mut context = - test_utils::create_empty_evm_context::(Box::new(env), db); - context.journaled_state.depth = CALL_STACK_LIMIT as usize + 1; - let contract = address!("dead10000000000000000000000000000001dead"); - let call_inputs = test_utils::create_mock_call_inputs(contract); - let res = context.make_call_frame(&call_inputs); - let Ok(FrameOrResult::Result(err)) = res else { - panic!("Expected FrameOrResult::Result"); - }; - assert_eq!( - err.interpreter_result().result, - InstructionResult::CallTooDeep - ); - } - - // Tests that the `EVMContext::make_call_frame` function returns an error if the - // transfer fails on the journaled state. It also verifies that the revert was - // checkpointed on the journaled state correctly. - #[test] - fn test_make_call_frame_transfer_revert() { - let env = EnvWiring::::default(); - let db = EmptyDB::default(); - let mut evm_context = - test_utils::create_empty_evm_context::(Box::new(env), db); - let contract = address!("dead10000000000000000000000000000001dead"); - let mut call_inputs = test_utils::create_mock_call_inputs(contract); - call_inputs.value = CallValue::Transfer(U256::from(1)); - let res = evm_context.make_call_frame(&call_inputs); - let Ok(FrameOrResult::Result(result)) = res else { - panic!("Expected FrameOrResult::Result"); - }; - assert_eq!( - result.interpreter_result().result, - InstructionResult::OutOfFunds - ); - let checkpointed = vec![vec![JournalEntry::AccountWarmed { address: contract }]]; - assert_eq!(evm_context.journaled_state.journal, checkpointed); - assert_eq!(evm_context.journaled_state.depth, 0); - } - - #[test] - fn test_make_call_frame_missing_code_context() { - type CacheEthWiring = EthereumWiring, ()>; - let env = EnvWiring::::default(); - let cdb = CacheDB::new(EmptyDB::default()); - let bal = U256::from(3_000_000_000_u128); - let mut context = - create_cache_db_evm_context_with_balance::(Box::new(env), cdb, bal); - let contract = address!("dead10000000000000000000000000000001dead"); - let call_inputs = test_utils::create_mock_call_inputs(contract); - let res = context.make_call_frame(&call_inputs); - let Ok(FrameOrResult::Result(result)) = res else { - panic!("Expected FrameOrResult::Result"); - }; - assert_eq!(result.interpreter_result().result, InstructionResult::Stop); - } - - #[test] - fn test_make_call_frame_succeeds() { - type CacheEthWiring = EthereumWiring, ()>; - let env = EnvWiring::::default(); - let mut cdb = CacheDB::new(EmptyDB::default()); - let bal = U256::from(3_000_000_000_u128); - let by = Bytecode::new_raw(Bytes::from(vec![0x60, 0x00, 0x60, 0x00])); - let contract = address!("dead10000000000000000000000000000001dead"); - cdb.insert_account_info( - contract, - AccountInfo { - nonce: 0, - balance: bal, - code_hash: by.clone().hash_slow(), - code: Some(by), - }, - ); - let mut evm_context = - create_cache_db_evm_context_with_balance::(Box::new(env), cdb, bal); - let call_inputs = test_utils::create_mock_call_inputs(contract); - let res = evm_context.make_call_frame(&call_inputs); - let Ok(FrameOrResult::Frame(Frame::Call(call_frame))) = res else { - panic!("Expected FrameOrResult::Frame(Frame::Call(..))"); - }; - assert_eq!(call_frame.return_memory_range, 0..0,); - } -} diff --git a/crates/revm/src/context/inner_evm_context.rs b/crates/revm/src/context/inner_evm_context.rs deleted file mode 100644 index ae778e1947..0000000000 --- a/crates/revm/src/context/inner_evm_context.rs +++ /dev/null @@ -1,433 +0,0 @@ -use crate::{journaled_state::JournaledState, JournalCheckpoint}; -use bytecode::{Bytecode, Eof, EOF_MAGIC_BYTES, EOF_MAGIC_HASH}; -use database_interface::Database; -use derive_where::derive_where; -use interpreter::{ - gas, return_ok, AccountLoad, Eip7702CodeLoad, InstructionResult, InterpreterResult, - SStoreResult, SelfDestructResult, StateLoad, -}; -use primitives::{Address, Bytes, HashSet, B256, U256}; -use specification::hardfork::{ - Spec, - SpecId::{self, *}, -}; -use state::Account; -use std::{boxed::Box, sync::Arc}; -use transaction::AccessListTrait; -use wiring::{ - default::{AnalysisKind, CfgEnv, EnvWiring}, - EvmWiring, Transaction, -}; - -/// EVM contexts contains data that EVM needs for execution. -#[derive_where(Clone, Debug; EvmWiringT::Block, EvmWiringT::ChainContext, EvmWiringT::Transaction, EvmWiringT::Database, ::Error)] -pub struct InnerEvmContext { - /// EVM Environment contains all the information about config, block and transaction that - /// evm needs. - pub env: Box>, - /// EVM State with journaling support. - pub journaled_state: JournaledState, - /// Database to load data from. - pub db: EvmWiringT::Database, - /// Inner context. - pub chain: EvmWiringT::ChainContext, - /// Error that happened during execution. - pub error: Result<(), ::Error>, -} - -impl InnerEvmContext -where - EvmWiringT: EvmWiring, -{ - pub fn new(db: EvmWiringT::Database) -> Self { - Self { - env: Box::default(), - journaled_state: JournaledState::new(SpecId::LATEST, HashSet::default()), - db, - chain: Default::default(), - error: Ok(()), - } - } -} - -impl InnerEvmContext { - /// Creates a new context with the given environment and database. - #[inline] - pub fn new_with_env(db: EvmWiringT::Database, env: Box>) -> Self { - Self { - env, - journaled_state: JournaledState::new(SpecId::LATEST, HashSet::default()), - db, - chain: Default::default(), - error: Ok(()), - } - } - - /// Sets the database. - /// - /// Note that this will ignore the previous `error` if set. - #[inline] - pub fn with_db< - OWiring: EvmWiring, - >( - self, - db: OWiring::Database, - ) -> InnerEvmContext { - InnerEvmContext { - env: self.env, - journaled_state: self.journaled_state, - db, - chain: Default::default(), - error: Ok(()), - } - } - - /// Returns the configured EVM spec ID. - #[inline] - pub const fn spec_id(&self) -> SpecId { - self.journaled_state.spec - } - - /// Load access list for berlin hard fork. - /// - /// Loading of accounts/storages is needed to make them warm. - #[inline] - pub fn load_access_list(&mut self) -> Result<(), ::Error> { - let Some(access_list) = self.env.tx.access_list() else { - return Ok(()); - }; - - for access_list in access_list.iter() { - self.journaled_state.initial_account_load( - access_list.0, - access_list.1.map(|i| U256::from_be_bytes(i.0)), - &mut self.db, - )?; - } - Ok(()) - } - - /// Return environment. - #[inline] - pub fn env(&mut self) -> &mut EnvWiring { - &mut self.env - } - - /// Returns reference to [`CfgEnv`]. - pub fn cfg(&self) -> &CfgEnv { - &self.env.cfg - } - - /// Returns the error by replacing it with `Ok(())`, if any. - #[inline] - pub fn take_error(&mut self) -> Result<(), ::Error> { - core::mem::replace(&mut self.error, Ok(())) - } - - /// Fetch block hash from database. - #[inline] - pub fn block_hash( - &mut self, - number: u64, - ) -> Result::Error> { - self.db.block_hash(number) - } - - /// Mark account as touched as only touched accounts will be added to state. - #[inline] - pub fn touch(&mut self, address: &Address) { - self.journaled_state.touch(address); - } - - /// Loads an account into memory. Returns `true` if it is cold accessed. - #[inline] - pub fn load_account( - &mut self, - address: Address, - ) -> Result, ::Error> { - self.journaled_state.load_account(address, &mut self.db) - } - - /// Load account from database to JournaledState. - /// - /// Return boolean pair where first is `is_cold` second bool `exists`. - #[inline] - pub fn load_account_delegated( - &mut self, - address: Address, - ) -> Result::Error> { - self.journaled_state - .load_account_delegated(address, &mut self.db) - } - - /// Return account balance and is_cold flag. - #[inline] - pub fn balance( - &mut self, - address: Address, - ) -> Result, ::Error> { - self.journaled_state - .load_account(address, &mut self.db) - .map(|acc| acc.map(|a| a.info.balance)) - } - - /// Return account code bytes and if address is cold loaded. - /// - /// In case of EOF account it will return `EOF_MAGIC` (0xEF00) as code. - #[inline] - pub fn code( - &mut self, - address: Address, - ) -> Result, ::Error> { - let a = self.journaled_state.load_code(address, &mut self.db)?; - // SAFETY: safe to unwrap as load_code will insert code if it is empty. - let code = a.info.code.as_ref().unwrap(); - if code.is_eof() { - return Ok(Eip7702CodeLoad::new_not_delegated( - EOF_MAGIC_BYTES.clone(), - a.is_cold, - )); - } - - if let Bytecode::Eip7702(code) = code { - let address = code.address(); - let is_cold = a.is_cold; - - let delegated_account = self.journaled_state.load_code(address, &mut self.db)?; - - // SAFETY: safe to unwrap as load_code will insert code if it is empty. - let delegated_code = delegated_account.info.code.as_ref().unwrap(); - - let bytes = if delegated_code.is_eof() { - EOF_MAGIC_BYTES.clone() - } else { - delegated_code.original_bytes() - }; - - return Ok(Eip7702CodeLoad::new( - StateLoad::new(bytes, is_cold), - delegated_account.is_cold, - )); - } - - Ok(Eip7702CodeLoad::new_not_delegated( - code.original_bytes(), - a.is_cold, - )) - } - - /// Get code hash of address. - /// - /// In case of EOF account it will return `EOF_MAGIC_HASH` - /// (the hash of `0xEF00`). - #[inline] - pub fn code_hash( - &mut self, - address: Address, - ) -> Result, ::Error> { - let acc = self.journaled_state.load_code(address, &mut self.db)?; - if acc.is_empty() { - return Ok(Eip7702CodeLoad::new_not_delegated(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(); - - // If bytecode is EIP-7702 then we need to load the delegated account. - if let Bytecode::Eip7702(code) = code { - let address = code.address(); - let is_cold = acc.is_cold; - - let delegated_account = self.journaled_state.load_code(address, &mut self.db)?; - - let hash = if delegated_account.is_empty() { - B256::ZERO - } else if delegated_account.info.code.as_ref().unwrap().is_eof() { - EOF_MAGIC_HASH - } else { - delegated_account.info.code_hash - }; - - return Ok(Eip7702CodeLoad::new( - StateLoad::new(hash, is_cold), - delegated_account.is_cold, - )); - } - - let hash = if code.is_eof() { - EOF_MAGIC_HASH - } else { - acc.info.code_hash - }; - - Ok(Eip7702CodeLoad::new_not_delegated(hash, acc.is_cold)) - } - - /// Load storage slot, if storage is not present inside the account then it will be loaded from database. - #[inline] - pub fn sload( - &mut self, - address: Address, - index: U256, - ) -> Result, ::Error> { - // account is always warm. reference on that statement https://eips.ethereum.org/EIPS/eip-2929 see `Note 2:` - self.journaled_state.sload(address, index, &mut self.db) - } - - /// Storage change of storage slot, before storing `sload` will be called for that slot. - #[inline] - pub fn sstore( - &mut self, - address: Address, - index: U256, - value: U256, - ) -> Result, ::Error> { - self.journaled_state - .sstore(address, index, value, &mut self.db) - } - - /// Returns transient storage value. - #[inline] - pub fn tload(&mut self, address: Address, index: U256) -> U256 { - self.journaled_state.tload(address, index) - } - - /// Stores transient storage value. - #[inline] - pub fn tstore(&mut self, address: Address, index: U256, value: U256) { - self.journaled_state.tstore(address, index, value) - } - - /// Selfdestructs the account. - #[inline] - pub fn selfdestruct( - &mut self, - address: Address, - target: Address, - ) -> Result, ::Error> { - self.journaled_state - .selfdestruct(address, target, &mut self.db) - } - - /// If error is present revert changes, otherwise save EOF bytecode. - pub fn eofcreate_return( - &mut self, - interpreter_result: &mut InterpreterResult, - address: Address, - journal_checkpoint: JournalCheckpoint, - ) { - // Note we still execute RETURN opcode and return the bytes. - // In EOF those opcodes should abort execution. - // - // In RETURN gas is still protecting us from ddos and in oog, - // behaviour will be same as if it failed on return. - // - // Bytes of RETURN will drained in `insert_eofcreate_outcome`. - if interpreter_result.result != InstructionResult::ReturnContract { - self.journaled_state.checkpoint_revert(journal_checkpoint); - return; - } - - if interpreter_result.output.len() > self.cfg().max_code_size() { - self.journaled_state.checkpoint_revert(journal_checkpoint); - interpreter_result.result = InstructionResult::CreateContractSizeLimit; - return; - } - - // deduct gas for code deployment. - let gas_for_code = interpreter_result.output.len() as u64 * gas::CODEDEPOSIT; - if !interpreter_result.gas.record_cost(gas_for_code) { - self.journaled_state.checkpoint_revert(journal_checkpoint); - interpreter_result.result = InstructionResult::OutOfGas; - return; - } - - // commit changes reduces depth by -1. - self.journaled_state.checkpoint_commit(); - - // decode bytecode has a performance hit, but it has reasonable restrains. - let bytecode = - Eof::decode(interpreter_result.output.clone()).expect("Eof is already verified"); - - // eof bytecode is going to be hashed. - self.journaled_state - .set_code(address, Bytecode::Eof(Arc::new(bytecode))); - } - - /// Handles call return. - #[inline] - pub fn call_return( - &mut self, - interpreter_result: &InterpreterResult, - journal_checkpoint: JournalCheckpoint, - ) { - // revert changes or not. - if matches!(interpreter_result.result, return_ok!()) { - self.journaled_state.checkpoint_commit(); - } else { - self.journaled_state.checkpoint_revert(journal_checkpoint); - } - } - - /// Handles create return. - #[inline] - pub fn create_return( - &mut self, - interpreter_result: &mut InterpreterResult, - address: Address, - journal_checkpoint: JournalCheckpoint, - ) { - // if return is not ok revert and return. - if !matches!(interpreter_result.result, return_ok!()) { - self.journaled_state.checkpoint_revert(journal_checkpoint); - return; - } - // Host error if present on execution - // if ok, check contract creation limit and calculate gas deduction on output len. - // - // EIP-3541: Reject new contract code starting with the 0xEF byte - if SPEC::enabled(LONDON) && interpreter_result.output.first() == Some(&0xEF) { - self.journaled_state.checkpoint_revert(journal_checkpoint); - interpreter_result.result = InstructionResult::CreateContractStartingWithEF; - return; - } - - // EIP-170: Contract code size limit - // By default limit is 0x6000 (~25kb) - if SPEC::enabled(SPURIOUS_DRAGON) - && interpreter_result.output.len() > self.cfg().max_code_size() - { - self.journaled_state.checkpoint_revert(journal_checkpoint); - interpreter_result.result = InstructionResult::CreateContractSizeLimit; - return; - } - let gas_for_code = interpreter_result.output.len() as u64 * gas::CODEDEPOSIT; - if !interpreter_result.gas.record_cost(gas_for_code) { - // record code deposit gas cost and check if we are out of gas. - // EIP-2 point 3: If contract creation does not have enough gas to pay for the - // final gas fee for adding the contract code to the state, the contract - // creation fails (i.e. goes out-of-gas) rather than leaving an empty contract. - if SPEC::enabled(HOMESTEAD) { - self.journaled_state.checkpoint_revert(journal_checkpoint); - interpreter_result.result = InstructionResult::OutOfGas; - return; - } else { - interpreter_result.output = Bytes::new(); - } - } - // if we have enough gas we can commit changes. - self.journaled_state.checkpoint_commit(); - - // Do analysis of bytecode straight away. - let bytecode = match self.env.cfg.perf_analyse_created_bytecodes { - AnalysisKind::Raw => Bytecode::new_legacy(interpreter_result.output.clone()), - AnalysisKind::Analyse => { - Bytecode::new_legacy(interpreter_result.output.clone()).into_analyzed() - } - }; - - // set code - self.journaled_state.set_code(address, bytecode); - - interpreter_result.result = InstructionResult::Return; - } -} diff --git a/crates/revm/src/evm.rs b/crates/revm/src/evm.rs index 0970e6d2b2..bfbf6c24b0 100644 --- a/crates/revm/src/evm.rs +++ b/crates/revm/src/evm.rs @@ -1,213 +1,192 @@ -use crate::{ - builder::{EvmBuilder, SetGenericStage}, - handler::Handler, - Context, ContextWithEvmWiring, EvmContext, EvmWiring, Frame, FrameOrResult, FrameResult, - InnerEvmContext, +use crate::{exec::EvmCommit, EvmExec}; +use context::{block::BlockEnv, tx::TxEnv, CfgEnv, Context}; +use context_interface::{ + block::BlockSetter, + journaled_state::JournaledState, + result::{ + EVMError, ExecutionResult, HaltReason, InvalidHeader, InvalidTransaction, ResultAndState, + }, + transaction::TransactionSetter, + BlockGetter, CfgGetter, DatabaseGetter, ErrorGetter, JournalStateGetter, + JournalStateGetterDBError, Transaction, TransactionGetter, }; -use core::fmt::{self, Debug}; use database_interface::{Database, DatabaseCommit}; -use interpreter::{Host, InterpreterAction, NewFrameAction, SharedMemory}; -use std::{boxed::Box, vec::Vec}; -use wiring::{ - default::{CfgEnv, EnvWiring}, - result::{EVMError, EVMResult, EVMResultGeneric, ExecutionResult, ResultAndState}, - Transaction, +use handler::{EthHandler, FrameResult}; +use handler_interface::{ + ExecutionHandler, Frame, FrameOrResultGen, Handler, PostExecutionHandler, PreExecutionHandler, + ValidationHandler, }; - -/// EVM call stack limit. -pub const CALL_STACK_LIMIT: u64 = 1024; - -/// EVM instance containing both internal EVM context and external context -/// and the handler that dictates the logic of EVM (or hardfork specification). -pub struct Evm<'a, EvmWiringT: EvmWiring> { - /// Context of execution, containing both EVM and external context. - pub context: Context, - /// Handler is a component of the of EVM that contains all the logic. Handler contains specification id - /// and it different depending on the specified fork. - pub handler: Handler<'a, EvmWiringT, Context>, +use interpreter::Host; +use precompile::PrecompileErrors; +use primitives::Log; +use state::EvmState; +use std::vec::Vec; + +/// Main EVM structure +pub struct Evm> { + pub context: CTX, + pub handler: HANDLER, + pub _error: core::marker::PhantomData ERROR>, } -impl Debug for Evm<'_, EvmWiringT> -where - EvmWiringT: - EvmWiring, - ::Error: Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Evm") - .field("evm context", &self.context.evm) - .finish_non_exhaustive() +impl Evm { + pub fn new(context: CTX, handler: HANDLER) -> Self { + Self { + context, + handler, + _error: core::marker::PhantomData, + } } } -impl> Evm<'_, EvmWiringT> { - /// Commit the changes to the database. - pub fn transact_commit( - &mut self, - ) -> EVMResultGeneric, EvmWiringT> { - let ResultAndState { result, state } = self.transact()?; - self.context.evm.db.commit(state); - Ok(result) +impl EvmCommit + for Evm> +where + CTX: TransactionSetter + + BlockSetter + + JournalStateGetter + + CfgGetter + + DatabaseGetter + + ErrorGetter + + JournalStateGetter< + Journal: JournaledState< + FinalOutput = (EvmState, Vec), + Database = ::Database, + >, + > + Host, + ERROR: From + + From + + From> + + From, + VAL: ValidationHandler, + PREEXEC: PreExecutionHandler, + EXEC: ExecutionHandler< + Context = CTX, + Error = ERROR, + ExecResult = FrameResult, + Frame: Frame, + >, + POSTEXEC: PostExecutionHandler< + Context = CTX, + Error = ERROR, + ExecResult = FrameResult, + // TODO make output generics + Output = ResultAndState, + >, +{ + type CommitOutput = Result, ERROR>; + + fn exec_commit(&mut self) -> Self::CommitOutput { + let res = self.transact(); + res.map(|r| { + self.context.db().commit(r.state); + r.result + }) } } -impl<'a, EvmWiringT: EvmWiring> Evm<'a, EvmWiringT> +impl EvmExec + for Evm> where - EvmWiringT::Transaction: Default, - EvmWiringT::Block: Default, + CTX: TransactionSetter + + BlockSetter + + JournalStateGetter + + CfgGetter + + DatabaseGetter + + ErrorGetter + + JournalStateGetter< + Journal: JournaledState< + FinalOutput = (EvmState, Vec), + Database = ::Database, + >, + > + Host, + ERROR: From + + From + + From> + + From, + VAL: ValidationHandler, + PREEXEC: PreExecutionHandler, + EXEC: ExecutionHandler< + Context = CTX, + Error = ERROR, + ExecResult = FrameResult, + Frame: Frame, + >, + POSTEXEC: PostExecutionHandler< + Context = CTX, + Error = ERROR, + ExecResult = FrameResult, + // TODO make output generics + Output = ResultAndState, + >, { - /// Returns evm builder with the mainnet chain spec, empty database, and empty external context. - pub fn builder() -> EvmBuilder<'a, SetGenericStage, EvmWiringT> { - EvmBuilder::new() - } -} + type Transaction = ::Transaction; -impl<'a, EvmWiringT: EvmWiring> Evm<'a, EvmWiringT> { - /// Create new EVM. - pub fn new( - mut context: Context, - handler: Handler<'a, EvmWiringT, Context>, - ) -> Evm<'a, EvmWiringT> { - context - .evm - .journaled_state - .set_spec_id(handler.spec_id.into()); - Evm { context, handler } + type Block = ::Block; + + type Output = Result, ERROR>; + + fn set_block(&mut self, block: Self::Block) { + self.context.set_block(block); } - /// Allow for evm setting to be modified by feeding current evm - /// into the builder for modifications. - pub fn modify(self) -> EvmBuilder<'a, SetGenericStage, EvmWiringT> { - let Evm { - context: - Context { - evm: - EvmContext { - inner: InnerEvmContext { db, env, .. }, - .. - }, - external, - }, - handler, - } = self; - EvmBuilder::<'a>::new_with(db, external, env, handler) + fn set_tx(&mut self, tx: Self::Transaction) { + self.context.set_tx(tx); } - /// Runs main call loop. - #[inline] - pub fn run_the_loop( - &mut self, - first_frame: Frame, - ) -> EVMResultGeneric { - let mut call_stack: Vec = Vec::with_capacity(1025); - call_stack.push(first_frame); - - #[cfg(feature = "memory_limit")] - let mut shared_memory = - SharedMemory::new_with_memory_limit(self.context.evm.env.cfg.memory_limit); - #[cfg(not(feature = "memory_limit"))] - let mut shared_memory = SharedMemory::new(); - - shared_memory.new_context(); - - // Peek the last stack frame. - let mut stack_frame = call_stack.last_mut().unwrap(); - - loop { - // Execute the frame. - let next_action = - self.handler - .execute_frame(stack_frame, &mut shared_memory, &mut self.context)?; - - // Take error and break the loop, if any. - // This error can be set in the Interpreter when it interacts with the context. - self.context.evm.take_error().map_err(EVMError::Database)?; - - let exec = &mut self.handler.execution; - let frame_or_result = match next_action { - InterpreterAction::NewFrame(NewFrameAction::Call(inputs)) => { - exec.call(&mut self.context, inputs)? - } - InterpreterAction::NewFrame(NewFrameAction::Create(inputs)) => { - exec.create(&mut self.context, inputs)? - } - InterpreterAction::NewFrame(NewFrameAction::EOFCreate(inputs)) => { - exec.eofcreate(&mut self.context, inputs)? - } - InterpreterAction::Return { result } => { - // free memory context. - shared_memory.free_context(); - - // pop last frame from the stack and consume it to create FrameResult. - let returned_frame = call_stack - .pop() - .expect("We just returned from Interpreter frame"); - - let ctx = &mut self.context; - FrameOrResult::Result(match returned_frame { - Frame::Call(frame) => { - // return_call - FrameResult::Call(exec.call_return(ctx, frame, result)?) - } - Frame::Create(frame) => { - // return_create - FrameResult::Create(exec.create_return(ctx, frame, result)?) - } - Frame::EOFCreate(frame) => { - // return_eofcreate - FrameResult::EOFCreate(exec.eofcreate_return(ctx, frame, result)?) - } - }) - } - InterpreterAction::None => unreachable!("InterpreterAction::None is not expected"), - }; - // handle result - match frame_or_result { - FrameOrResult::Frame(frame) => { - shared_memory.new_context(); - call_stack.push(frame); - stack_frame = call_stack.last_mut().unwrap(); - } - FrameOrResult::Result(result) => { - let Some(top_frame) = call_stack.last_mut() else { - // Break the loop if there are no more frames. - return Ok(result); - }; - stack_frame = top_frame; - let ctx = &mut self.context; - // Insert result to the top frame. - match result { - FrameResult::Call(outcome) => { - // return_call - exec.insert_call_outcome(ctx, stack_frame, &mut shared_memory, outcome)? - } - FrameResult::Create(outcome) => { - // return_create - exec.insert_create_outcome(ctx, stack_frame, outcome)? - } - FrameResult::EOFCreate(outcome) => { - // return_eofcreate - exec.insert_eofcreate_outcome(ctx, stack_frame, outcome)? - } - } - } - } - } + fn exec(&mut self) -> Self::Output { + self.transact() } } -impl Evm<'_, EvmWiringT> { - /// Returns specification (hardfork) that the EVM is instanced with. - /// - /// SpecId depends on the handler. - pub fn spec_id(&self) -> EvmWiringT::Hardfork { - self.handler.spec_id - } +/// Mainnet Error. +pub type Error = EVMError<::Error, InvalidTransaction>; +/// Mainnet Contexts. +pub type EthContext = + Context; + +/// Mainnet EVM type. +pub type MainEvm = Evm, EthContext>; + +impl + Evm> +where + CTX: TransactionGetter + + BlockGetter + + JournalStateGetter + + CfgGetter + + DatabaseGetter + + ErrorGetter + + JournalStateGetter< + Journal: JournaledState< + FinalOutput = (EvmState, Vec), + Database = ::Database, + >, + > + Host, + ERROR: From + + From + + From> + + From, + VAL: ValidationHandler, + PREEXEC: PreExecutionHandler, + EXEC: ExecutionHandler< + Context = CTX, + Error = ERROR, + ExecResult = FrameResult, + Frame: Frame, + >, + POSTEXEC: PostExecutionHandler< + Context = CTX, + Error = ERROR, + ExecResult = FrameResult, + Output = ResultAndState, + >, +{ /// Pre verify transaction by checking Environment, initial gas spend and if caller /// has enough balance to pay for the gas. #[inline] - pub fn preverify_transaction(&mut self) -> EVMResultGeneric<(), EvmWiringT> { + pub fn preverify_transaction(&mut self) -> Result<(), ERROR> { let output = self.preverify_transaction_inner().map(|_| ()); self.clear(); output @@ -222,11 +201,11 @@ impl Evm<'_, EvmWiringT> { /// /// This function will not validate the transaction. #[inline] - pub fn transact_preverified(&mut self) -> EVMResult { + pub fn transact_preverified(&mut self) -> Result, ERROR> { let initial_gas_spend = self .handler .validation() - .initial_tx_gas(&self.context.evm.env) + .validate_initial_tx_gas(&self.context) .inspect_err(|_| { self.clear(); })?; @@ -238,15 +217,15 @@ impl Evm<'_, EvmWiringT> { /// Pre verify transaction inner. #[inline] - fn preverify_transaction_inner(&mut self) -> EVMResultGeneric { - self.handler.validation().env(&self.context.evm.env)?; + fn preverify_transaction_inner(&mut self) -> Result { + self.handler.validation().validate_env(&self.context)?; let initial_gas_spend = self .handler .validation() - .initial_tx_gas(&self.context.evm.env)?; + .validate_initial_tx_gas(&self.context)?; self.handler .validation() - .tx_against_state(&mut self.context)?; + .validate_tx_against_state(&mut self.context)?; Ok(initial_gas_spend) } @@ -254,7 +233,7 @@ impl Evm<'_, EvmWiringT> { /// /// This function will validate the transaction. #[inline] - pub fn transact(&mut self) -> EVMResult { + pub fn transact(&mut self) -> Result, ERROR> { let initial_gas_spend = self.preverify_transaction_inner().inspect_err(|_| { self.clear(); })?; @@ -265,162 +244,177 @@ impl Evm<'_, EvmWiringT> { output } - /// Returns the reference of Env configuration - #[inline] - pub fn cfg(&self) -> &CfgEnv { - &self.context.env().cfg - } - - /// Returns the mutable reference of Env configuration - #[inline] - pub fn cfg_mut(&mut self) -> &mut CfgEnv { - &mut self.context.evm.env.cfg - } - - /// Returns the reference of transaction - #[inline] - pub fn tx(&self) -> &EvmWiringT::Transaction { - &self.context.evm.env.tx - } - - /// Returns the mutable reference of transaction - #[inline] - pub fn tx_mut(&mut self) -> &mut EvmWiringT::Transaction { - &mut self.context.evm.env.tx - } - - /// Returns the reference of database - #[inline] - pub fn db(&self) -> &EvmWiringT::Database { - &self.context.evm.db - } - - /// Returns the mutable reference of database - #[inline] - pub fn db_mut(&mut self) -> &mut EvmWiringT::Database { - &mut self.context.evm.db - } - - /// Returns the reference of block - #[inline] - pub fn block(&self) -> &EvmWiringT::Block { - &self.context.evm.env.block - } - - /// Returns the mutable reference of block - #[inline] - pub fn block_mut(&mut self) -> &mut EvmWiringT::Block { - &mut self.context.evm.env.block - } - - /// Modify spec id, this will create new EVM that matches this spec id. - pub fn modify_spec_id(&mut self, spec_id: EvmWiringT::Hardfork) { - self.context.evm.journaled_state.set_spec_id(spec_id.into()); - self.handler.modify_spec_id(spec_id); - } - - /// Returns internal database and external struct. - #[inline] - pub fn into_context(self) -> Context { - self.context - } - - /// Returns database, [`EnvWiring`] and Hardfork. - #[inline] - pub fn into_db_and_env_with_handler_cfg( - self, - ) -> ( - EvmWiringT::Database, - Box>, - EvmWiringT::Hardfork, - ) { - ( - self.context.evm.inner.db, - self.context.evm.inner.env, - self.handler.spec_id, - ) - } - - /// Returns [Context] and hardfork. - #[inline] - pub fn into_context_with_spec_id(self) -> ContextWithEvmWiring { - ContextWithEvmWiring::new(self.context, self.handler.spec_id) - } - /// Transact pre-verified transaction. - fn transact_preverified_inner(&mut self, initial_gas_spend: u64) -> EVMResult { - let ctx = &mut self.context; + fn transact_preverified_inner( + &mut self, + initial_gas_spend: u64, + ) -> Result, ERROR> { + let context = &mut self.context; let pre_exec = self.handler.pre_execution(); // load access list and beneficiary if needed. - pre_exec.load_accounts(ctx)?; - - // load precompiles - let precompiles = pre_exec.load_precompiles(); - ctx.evm.set_precompiles(precompiles); + pre_exec.load_accounts(context)?; // deduce caller balance with its limit. - pre_exec.deduct_caller(ctx)?; + pre_exec.deduct_caller(context)?; - let gas_limit = ctx.evm.env.tx.common_fields().gas_limit() - initial_gas_spend; + let gas_limit = context.tx().common_fields().gas_limit() - initial_gas_spend; // apply EIP-7702 auth list. - let eip7702_gas_refund = pre_exec.apply_eip7702_auth_list(ctx)? as i64; + let eip7702_gas_refund = pre_exec.apply_eip7702_auth_list(context)? as i64; // start execution + + //let instructions = self.handler.take_instruction_table(); let exec = self.handler.execution(); // create first frame action - let first_frame_action = exec.first_frame_creation(ctx, gas_limit)?; - - // call handler to create first frame. - let first_frame_or_result = match first_frame_action { - NewFrameAction::Call(inputs) => exec.call(ctx, inputs)?, - NewFrameAction::Create(inputs) => exec.create(ctx, inputs)?, - NewFrameAction::EOFCreate(inputs) => exec.eofcreate(ctx, inputs)?, + let first_frame = exec.init_first_frame(context, gas_limit)?; + let frame_result = match first_frame { + FrameOrResultGen::Frame(frame) => exec.run(context, frame)?, + FrameOrResultGen::Result(result) => result, }; - // Starts the main running loop or return the result. - let mut result = match first_frame_or_result { - FrameOrResult::Frame(first_frame) => self.run_the_loop(first_frame)?, - FrameOrResult::Result(result) => result, - }; - - let ctx = &mut self.context; - - // handle output of call/create calls. - self.handler - .execution() - .last_frame_return(ctx, &mut result)?; + let mut exec_result = exec.last_frame_result(context, frame_result)?; let post_exec = self.handler.post_execution(); // calculate final refund and add EIP-7702 refund to gas. - post_exec.refund(ctx, result.gas_mut(), eip7702_gas_refund); + post_exec.refund(context, &mut exec_result, eip7702_gas_refund); // Reimburse the caller - post_exec.reimburse_caller(ctx, result.gas())?; + post_exec.reimburse_caller(context, &mut exec_result)?; // Reward beneficiary - post_exec.reward_beneficiary(ctx, result.gas())?; + post_exec.reward_beneficiary(context, &mut exec_result)?; // Returns output of transaction. - post_exec.output(ctx, result) + post_exec.output(context, exec_result) } } +/* + #[cfg(test)] mod tests { use super::*; + use crate::{ + handler::mainnet::{EthExecution, EthPostExecution, EthPreExecution, EthValidation}, + EvmHandler, + }; use bytecode::{ opcode::{PUSH1, SSTORE}, Bytecode, }; + use core::{fmt::Debug, hash::Hash}; use database::BenchmarkDB; + use database_interface::Database; + use interpreter::table::InstructionTables; use primitives::{address, TxKind, U256}; use specification::{ eip7702::{Authorization, RecoveredAuthorization, Signature}, - hardfork::SpecId, + hardfork::{Spec, SpecId}, + spec_to_generic, }; use transaction::TransactionType; - use wiring::EthereumWiring; + use context_interface::{ + default::{self, block::BlockEnv, Env, TxEnv}, + result::{EVMErrorWiring, HaltReason}, + EthereumWiring, EvmWiring as InnerEvmWiring, + }; + + #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)] + struct CEthereumWiring<'a, DB: Database, EXT> { + phantom: core::marker::PhantomData<&'a (DB, EXT)>, + } + + impl<'a, DB: Database, EXT: Debug> InnerEvmWiring for CEthereumWiring<'a, DB, EXT> { + type Database = DB; + type ExternalContext = EXT; + type ChainContext = (); + type Block = default::block::BlockEnv; + type Transaction = &'a default::TxEnv; + type Hardfork = SpecId; + type HaltReason = HaltReason; + } + + impl<'a, DB: Database, EXT: Debug> EvmWiring for CEthereumWiring<'a, DB, EXT> { + fn handler<'evm>(hardfork: Self::Hardfork) -> EvmHandler<'evm, Self> + where + DB: Database, + 'a: 'evm, + { + spec_to_generic!( + hardfork, + EvmHandler { + spec_id: hardfork, + //instruction_table: InstructionTables::new_plain::(), + registers: Vec::new(), + pre_execution: + EthPreExecution::, EVMErrorWiring>::new_boxed( + SPEC::SPEC_ID + ), + validation: EthValidation::, EVMErrorWiring>::new_boxed( + SPEC::SPEC_ID + ), + post_execution: EthPostExecution::< + Context, + EVMErrorWiring, + HaltReason, + >::new_boxed(SPEC::SPEC_ID), + execution: EthExecution::, EVMErrorWiring>::new_boxed( + SPEC::SPEC_ID + ), + } + ) + } + } + + //pub type DefaultEthereumWiring = EthereumWiring; + + #[test] + fn sanity_tx_ref() { + let delegate = address!("0000000000000000000000000000000000000000"); + let caller = address!("0000000000000000000000000000000000000001"); + let auth = address!("0000000000000000000000000000000000000100"); + + let mut tx = TxEnv::default(); + tx.tx_type = TransactionType::Eip7702; + tx.gas_limit = 100_000; + tx.authorization_list = vec![RecoveredAuthorization::new_unchecked( + Authorization { + chain_id: U256::from(1), + address: delegate, + nonce: 0, + } + .into_signed(Signature::test_signature()), + Some(auth), + )] + .into(); + tx.caller = caller; + tx.transact_to = TxKind::Call(auth); + + let mut tx2 = TxEnv::default(); + tx2.tx_type = TransactionType::Legacy; + // nonce was bumped from 0 to 1 + tx2.nonce = 1; + + let mut evm = EvmBuilder::new_with( + BenchmarkDB::default(), + (), + Env::boxed(CfgEnv::default(), BlockEnv::default(), &tx), + CEthereumcontext_interface::handler(SpecId::LATEST), + ) + .build(); + + let _ = evm.transact().unwrap(); + + let mut evm = evm + .modify() + .modify_tx_env(|t| { + *t = &tx2; + }) + .build(); + + let _ = evm.transact().unwrap(); + } #[test] fn sanity_eip7702_tx() { @@ -433,7 +427,7 @@ mod tests { let mut evm = Evm::>::builder() .with_spec_id(SpecId::PRAGUE) .with_db(BenchmarkDB::new_bytecode(bytecode)) - .with_default_ext_ctx() + .with_default_ext_context() .modify_tx_env(|tx| { tx.tx_type = TransactionType::Eip7702; tx.gas_limit = 100_000; @@ -463,3 +457,5 @@ mod tests { ); } } + +*/ diff --git a/crates/revm/src/evm_wiring.rs b/crates/revm/src/evm_wiring.rs deleted file mode 100644 index 280fbbfee7..0000000000 --- a/crates/revm/src/evm_wiring.rs +++ /dev/null @@ -1,35 +0,0 @@ -use crate::{ - handler::{ExecutionHandler, PostExecutionHandler, PreExecutionHandler, ValidationHandler}, - EvmHandler, -}; -use database_interface::Database; -use interpreter::table::InstructionTables; -use specification::spec_to_generic; -use std::fmt::Debug; -use std::vec::Vec; -use wiring::{EthereumWiring, EvmWiring as PrimitiveEvmWiring}; - -pub trait EvmWiring: PrimitiveEvmWiring { - /// Creates a new handler with the given hardfork. - fn handler<'evm>(hardfork: Self::Hardfork) -> EvmHandler<'evm, Self>; -} - -impl EvmWiring for EthereumWiring { - fn handler<'evm>(hardfork: Self::Hardfork) -> EvmHandler<'evm, Self> - where - DB: Database, - { - spec_to_generic!( - hardfork, - EvmHandler { - spec_id: hardfork, - instruction_table: InstructionTables::new_plain::(), - registers: Vec::new(), - validation: ValidationHandler::new::(), - pre_execution: PreExecutionHandler::new::(), - post_execution: PostExecutionHandler::mainnet::(), - execution: ExecutionHandler::new::(), - } - ) - } -} diff --git a/crates/revm/src/exec.rs b/crates/revm/src/exec.rs new file mode 100644 index 0000000000..3963e97fb2 --- /dev/null +++ b/crates/revm/src/exec.rs @@ -0,0 +1,19 @@ +use context_interface::{Block, Transaction}; + +pub trait EvmExec { + type Transaction: Transaction; + type Block: Block; + type Output; + + fn set_block(&mut self, block: Self::Block); + + fn set_tx(&mut self, tx: Self::Transaction); + + fn exec(&mut self) -> Self::Output; +} + +pub trait EvmCommit: EvmExec { + type CommitOutput; + + fn exec_commit(&mut self) -> Self::CommitOutput; +} diff --git a/crates/revm/src/frame.rs b/crates/revm/src/frame.rs deleted file mode 100644 index 4df3622ff2..0000000000 --- a/crates/revm/src/frame.rs +++ /dev/null @@ -1,297 +0,0 @@ -use crate::JournalCheckpoint; -use core::ops::Range; -use interpreter::{ - CallOutcome, CreateOutcome, Gas, InstructionResult, Interpreter, InterpreterResult, -}; -use primitives::Address; -use std::boxed::Box; -use wiring::result::Output; - -/// Call CallStackFrame. -#[derive(Debug)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct CallFrame { - /// Call frame has return memory range where output will be stored. - pub return_memory_range: Range, - /// Frame data. - pub frame_data: FrameData, -} - -#[derive(Debug)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct CreateFrame { - /// Create frame has a created address. - pub created_address: Address, - /// Frame data. - pub frame_data: FrameData, -} - -/// Eof Create Frame. -#[derive(Debug)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct EOFCreateFrame { - pub created_address: Address, - pub frame_data: FrameData, -} - -#[derive(Debug)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct FrameData { - /// Journal checkpoint. - pub checkpoint: JournalCheckpoint, - /// Interpreter. - pub interpreter: Interpreter, -} - -/// Call stack frame. -#[derive(Debug)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum Frame { - Call(Box), - Create(Box), - EOFCreate(Box), -} - -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug)] -pub enum FrameResult { - Call(CallOutcome), - Create(CreateOutcome), - EOFCreate(CreateOutcome), -} - -impl FrameResult { - /// Casts frame result to interpreter result. - #[inline] - pub fn into_interpreter_result(self) -> InterpreterResult { - match self { - FrameResult::Call(outcome) => outcome.result, - FrameResult::Create(outcome) => outcome.result, - FrameResult::EOFCreate(outcome) => outcome.result, - } - } - - /// Returns execution output. - #[inline] - pub fn output(&self) -> Output { - match self { - FrameResult::Call(outcome) => Output::Call(outcome.result.output.clone()), - FrameResult::Create(outcome) => { - Output::Create(outcome.result.output.clone(), outcome.address) - } - FrameResult::EOFCreate(outcome) => { - Output::Create(outcome.result.output.clone(), outcome.address) - } - } - } - - /// Returns reference to gas. - #[inline] - pub fn gas(&self) -> &Gas { - match self { - FrameResult::Call(outcome) => &outcome.result.gas, - FrameResult::Create(outcome) => &outcome.result.gas, - FrameResult::EOFCreate(outcome) => &outcome.result.gas, - } - } - - /// Returns mutable reference to interpreter result. - #[inline] - pub fn gas_mut(&mut self) -> &mut Gas { - match self { - FrameResult::Call(outcome) => &mut outcome.result.gas, - FrameResult::Create(outcome) => &mut outcome.result.gas, - FrameResult::EOFCreate(outcome) => &mut outcome.result.gas, - } - } - - /// Returns reference to interpreter result. - #[inline] - pub fn interpreter_result(&self) -> &InterpreterResult { - match self { - FrameResult::Call(outcome) => &outcome.result, - FrameResult::Create(outcome) => &outcome.result, - FrameResult::EOFCreate(outcome) => &outcome.result, - } - } - - /// Returns mutable reference to interpreter result. - #[inline] - pub fn interpreter_result_mut(&mut self) -> &InterpreterResult { - match self { - FrameResult::Call(outcome) => &mut outcome.result, - FrameResult::Create(outcome) => &mut outcome.result, - FrameResult::EOFCreate(outcome) => &mut outcome.result, - } - } - - /// Return Instruction result. - #[inline] - pub fn instruction_result(&self) -> InstructionResult { - self.interpreter_result().result - } -} - -/// Contains either a frame or a result. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug)] -pub enum FrameOrResult { - /// Boxed call or create frame. - Frame(Frame), - /// Call or create result. - Result(FrameResult), -} - -impl Frame { - pub fn new_create( - created_address: Address, - checkpoint: JournalCheckpoint, - interpreter: Interpreter, - ) -> Self { - Frame::Create(Box::new(CreateFrame { - created_address, - frame_data: FrameData { - checkpoint, - interpreter, - }, - })) - } - - pub fn new_call( - return_memory_range: Range, - checkpoint: JournalCheckpoint, - interpreter: Interpreter, - ) -> Self { - Frame::Call(Box::new(CallFrame { - return_memory_range, - frame_data: FrameData { - checkpoint, - interpreter, - }, - })) - } - - /// Returns true if frame is call frame. - pub fn is_call(&self) -> bool { - matches!(self, Frame::Call { .. }) - } - - /// Returns true if frame is create frame. - pub fn is_create(&self) -> bool { - matches!(self, Frame::Create { .. }) - } - - /// Returns created address if frame is create otherwise returns None. - pub fn created_address(&self) -> Option
{ - match self { - Frame::Create(create_frame) => Some(create_frame.created_address), - _ => None, - } - } - - /// Takes frame and returns frame data. - pub fn into_frame_data(self) -> FrameData { - match self { - Frame::Call(call_frame) => call_frame.frame_data, - Frame::Create(create_frame) => create_frame.frame_data, - Frame::EOFCreate(eof_create_frame) => eof_create_frame.frame_data, - } - } - - /// Returns reference to frame data. - pub fn frame_data(&self) -> &FrameData { - match self { - Self::Call(call_frame) => &call_frame.frame_data, - Self::Create(create_frame) => &create_frame.frame_data, - Self::EOFCreate(eof_create_frame) => &eof_create_frame.frame_data, - } - } - - /// Returns mutable reference to frame data. - pub fn frame_data_mut(&mut self) -> &mut FrameData { - match self { - Self::Call(call_frame) => &mut call_frame.frame_data, - Self::Create(create_frame) => &mut create_frame.frame_data, - Self::EOFCreate(eof_create_frame) => &mut eof_create_frame.frame_data, - } - } - - /// Returns a reference to the interpreter. - pub fn interpreter(&self) -> &Interpreter { - &self.frame_data().interpreter - } - - /// Returns a mutable reference to the interpreter. - pub fn interpreter_mut(&mut self) -> &mut Interpreter { - &mut self.frame_data_mut().interpreter - } -} - -impl FrameOrResult { - /// Creates new create frame. - pub fn new_create_frame( - created_address: Address, - checkpoint: JournalCheckpoint, - interpreter: Interpreter, - ) -> Self { - Self::Frame(Frame::new_create(created_address, checkpoint, interpreter)) - } - - pub fn new_eofcreate_frame( - created_address: Address, - checkpoint: JournalCheckpoint, - interpreter: Interpreter, - ) -> Self { - Self::Frame(Frame::EOFCreate(Box::new(EOFCreateFrame { - created_address, - frame_data: FrameData { - checkpoint, - interpreter, - }, - }))) - } - - /// Creates new call frame. - pub fn new_call_frame( - return_memory_range: Range, - checkpoint: JournalCheckpoint, - interpreter: Interpreter, - ) -> Self { - Self::Frame(Frame::new_call( - return_memory_range, - checkpoint, - interpreter, - )) - } - - /// Creates new create result. - pub fn new_create_result( - interpreter_result: InterpreterResult, - address: Option
, - ) -> Self { - FrameOrResult::Result(FrameResult::Create(CreateOutcome { - result: interpreter_result, - address, - })) - } - - pub fn new_eofcreate_result( - interpreter_result: InterpreterResult, - address: Option
, - ) -> Self { - FrameOrResult::Result(FrameResult::EOFCreate(CreateOutcome { - result: interpreter_result, - address, - })) - } - - pub fn new_call_result( - interpreter_result: InterpreterResult, - memory_offset: Range, - ) -> Self { - FrameOrResult::Result(FrameResult::Call(CallOutcome { - result: interpreter_result, - memory_offset, - })) - } -} diff --git a/crates/revm/src/handler.rs b/crates/revm/src/handler.rs deleted file mode 100644 index e79a025fca..0000000000 --- a/crates/revm/src/handler.rs +++ /dev/null @@ -1,209 +0,0 @@ -// Modules. -mod handle_types; -pub mod mainnet; -pub mod register; - -// Exports. -pub use handle_types::*; - -// Includes. - -use crate::{Context, EvmWiring, Frame}; -use core::mem; -use interpreter::{table::InstructionTables, Host, InterpreterAction, SharedMemory}; -use register::{EvmHandler, HandleRegisters}; -use specification::spec_to_generic; -use std::vec::Vec; -use wiring::{ - result::{EVMResultGeneric, InvalidTransaction}, - Transaction, -}; - -use self::register::{HandleRegister, HandleRegisterBox}; - -/// Handler acts as a proxy and allow to define different behavior for different -/// sections of the code. This allows nice integration of different chains or -/// to disable some mainnet behavior. -pub struct Handler<'a, EvmWiringT: EvmWiring, H: Host + 'a> { - /// Handler hardfork - pub spec_id: EvmWiringT::Hardfork, - /// Instruction table type. - pub instruction_table: InstructionTables<'a, H>, - /// Registers that will be called on initialization. - pub registers: Vec>, - /// Validity handles. - pub validation: ValidationHandler<'a, EvmWiringT>, - /// Pre execution handle. - pub pre_execution: PreExecutionHandler<'a, EvmWiringT>, - /// Post Execution handle. - pub post_execution: PostExecutionHandler<'a, EvmWiringT>, - /// Execution loop that handles frames. - pub execution: ExecutionHandler<'a, EvmWiringT>, -} - -impl<'a, EvmWiringT> EvmHandler<'a, EvmWiringT> -where - EvmWiringT: EvmWiring>>, -{ - /// Creates a base/vanilla Ethereum handler with the provided spec id. - pub fn mainnet_with_spec(spec_id: EvmWiringT::Hardfork) -> Self { - spec_to_generic!( - spec_id.into(), - Self { - spec_id, - instruction_table: InstructionTables::new_plain::(), - registers: Vec::new(), - validation: ValidationHandler::new::(), - pre_execution: PreExecutionHandler::new::(), - post_execution: PostExecutionHandler::mainnet::(), - execution: ExecutionHandler::new::(), - } - ) - } -} - -impl<'a, EvmWiringT: EvmWiring> EvmHandler<'a, EvmWiringT> { - /// Returns the specification ID. - pub fn spec_id(&self) -> EvmWiringT::Hardfork { - self.spec_id - } - - /// Executes call frame. - pub fn execute_frame( - &self, - frame: &mut Frame, - shared_memory: &mut SharedMemory, - context: &mut Context, - ) -> EVMResultGeneric { - self.execution - .execute_frame(frame, shared_memory, &self.instruction_table, context) - } - - /// Take instruction table. - pub fn take_instruction_table(&mut self) -> InstructionTables<'a, Context> { - let spec_id = self.spec_id(); - mem::replace( - &mut self.instruction_table, - spec_to_generic!(spec_id.into(), InstructionTables::new_plain::()), - ) - } - - /// Set instruction table. - pub fn set_instruction_table(&mut self, table: InstructionTables<'a, Context>) { - self.instruction_table = table; - } - - /// Returns reference to pre execution handler. - pub fn pre_execution(&self) -> &PreExecutionHandler<'a, EvmWiringT> { - &self.pre_execution - } - - /// Returns reference to pre execution handler. - pub fn post_execution(&self) -> &PostExecutionHandler<'a, EvmWiringT> { - &self.post_execution - } - - /// Returns reference to frame handler. - pub fn execution(&self) -> &ExecutionHandler<'a, EvmWiringT> { - &self.execution - } - - /// Returns reference to validation handler. - pub fn validation(&self) -> &ValidationHandler<'a, EvmWiringT> { - &self.validation - } - - /// Append handle register. - pub fn append_handler_register(&mut self, register: HandleRegisters<'a, EvmWiringT>) { - register.register(self); - self.registers.push(register); - } - - /// Append plain handle register. - pub fn append_handler_register_plain(&mut self, register: HandleRegister) { - register(self); - self.registers.push(HandleRegisters::Plain(register)); - } - - /// Append boxed handle register. - pub fn append_handler_register_box(&mut self, register: HandleRegisterBox<'a, EvmWiringT>) { - register(self); - self.registers.push(HandleRegisters::Box(register)); - } -} - -impl<'a, EvmWiringT: EvmWiring> EvmHandler<'a, EvmWiringT> { - /// Pop last handle register and reapply all registers that are left. - pub fn pop_handle_register(&mut self) -> Option> { - let out = self.registers.pop(); - if out.is_some() { - let registers = core::mem::take(&mut self.registers); - let mut base_handler = EvmWiringT::handler::<'a>(self.spec_id); - // apply all registers to default handler and raw mainnet instruction table. - for register in registers { - base_handler.append_handler_register(register) - } - *self = base_handler; - } - out - } - - /// Creates the Handler with variable SpecId, inside it will call function with Generic Spec. - pub fn modify_spec_id(&mut self, spec_id: EvmWiringT::Hardfork) { - if self.spec_id == spec_id { - return; - } - - let registers = core::mem::take(&mut self.registers); - // register for optimism is added as a register, so we need to create mainnet handler here. - let mut handler = EvmWiringT::handler::<'a>(spec_id); - // apply all registers to default handler and raw mainnet instruction table. - for register in registers { - handler.append_handler_register(register) - } - handler.spec_id = spec_id; - *self = handler; - } -} - -#[cfg(test)] -mod test { - extern crate alloc; - - use alloc::boxed::Box; - use core::cell::RefCell; - use database_interface::EmptyDB; - use std::{rc::Rc, sync::Arc}; - use wiring::{result::EVMError, EthereumWiring, EvmWiring}; - - use super::*; - - type TestEvmWiring = EthereumWiring; - - #[test] - fn test_handler_register_pop() { - let register = |inner: &Rc>| -> HandleRegisterBox<'_, TestEvmWiring> { - let inner = inner.clone(); - Box::new(move |h| { - *inner.borrow_mut() += 1; - h.post_execution.output = Arc::new(|_, _| Err(EVMError::Custom("test".into()))) - }) - }; - - let mut handler = EvmHandler::<'_, TestEvmWiring>::mainnet_with_spec( - ::Hardfork::default(), - ); - let test = Rc::new(RefCell::new(0)); - - handler.append_handler_register_box(register(&test)); - assert_eq!(*test.borrow(), 1); - - handler.append_handler_register_box(register(&test)); - assert_eq!(*test.borrow(), 2); - - assert!(handler.pop_handle_register().is_some()); - - // first handler is reapplied - assert_eq!(*test.borrow(), 3); - } -} diff --git a/crates/revm/src/handler/handle_types.rs b/crates/revm/src/handler/handle_types.rs deleted file mode 100644 index 28f1e59241..0000000000 --- a/crates/revm/src/handler/handle_types.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Modules - -pub mod execution; -pub mod generic; -pub mod post_execution; -pub mod pre_execution; -pub mod validation; - -// Exports - -pub use execution::{ - ExecutionHandler, FrameCallHandle, FrameCallReturnHandle, FrameCreateHandle, - FrameCreateReturnHandle, InsertCallOutcomeHandle, InsertCreateOutcomeHandle, -}; -pub use generic::{GenericContextHandle, GenericContextHandleRet}; -pub use post_execution::{ - EndHandle, OutputHandle, PostExecutionHandler, ReimburseCallerHandle, RewardBeneficiaryHandle, -}; -pub use pre_execution::{ - DeductCallerHandle, LoadAccountsHandle, LoadPrecompilesHandle, PreExecutionHandler, -}; -pub use validation::{ - ValidateEnvHandle, ValidateInitialTxGasHandle, ValidateTxEnvAgainstState, ValidationHandler, -}; diff --git a/crates/revm/src/handler/handle_types/execution.rs b/crates/revm/src/handler/handle_types/execution.rs deleted file mode 100644 index f898f5a932..0000000000 --- a/crates/revm/src/handler/handle_types/execution.rs +++ /dev/null @@ -1,289 +0,0 @@ -use crate::{ - frame::EOFCreateFrame, handler::mainnet, CallFrame, Context, CreateFrame, EvmWiring, Frame, - FrameOrResult, FrameResult, -}; -use interpreter::{ - table::InstructionTables, CallInputs, CallOutcome, CreateInputs, CreateOutcome, - EOFCreateInputs, InterpreterAction, InterpreterResult, NewFrameAction, SharedMemory, -}; -use specification::hardfork::Spec; -use std::{boxed::Box, sync::Arc}; -use wiring::result::EVMResultGeneric; - -/// Handles creation of first frame -pub type FirstFrameCreation<'a, EvmWiringT> = - Arc, u64) -> EVMResultGeneric + 'a>; - -/// Handles first frame return handle. -pub type LastFrameReturnHandle<'a, EvmWiringT> = Arc< - dyn Fn(&mut Context, &mut FrameResult) -> EVMResultGeneric<(), EvmWiringT> + 'a, ->; - -/// Executes a single frame. Errors can be returned in the EVM context. -pub type ExecuteFrameHandle<'a, EvmWiringT> = Arc< - dyn Fn( - &mut Frame, - &mut SharedMemory, - &InstructionTables<'_, Context>, - &mut Context, - ) -> EVMResultGeneric - + 'a, ->; - -/// Handle sub call. -pub type FrameCallHandle<'a, EvmWiringT> = Arc< - dyn Fn(&mut Context, Box) -> EVMResultGeneric - + 'a, ->; - -/// Handle call return -pub type FrameCallReturnHandle<'a, EvmWiringT> = Arc< - dyn Fn( - &mut Context, - Box, - InterpreterResult, - ) -> EVMResultGeneric - + 'a, ->; - -/// Insert call outcome to the parent -pub type InsertCallOutcomeHandle<'a, EvmWiringT> = Arc< - dyn Fn( - &mut Context, - &mut Frame, - &mut SharedMemory, - CallOutcome, - ) -> EVMResultGeneric<(), EvmWiringT> - + 'a, ->; - -/// Handle sub create. -pub type FrameCreateHandle<'a, EvmWiringT> = Arc< - dyn Fn( - &mut Context, - Box, - ) -> EVMResultGeneric - + 'a, ->; - -/// Handle create return -pub type FrameCreateReturnHandle<'a, EvmWiringT> = Arc< - dyn Fn( - &mut Context, - Box, - InterpreterResult, - ) -> EVMResultGeneric - + 'a, ->; - -/// Insert call outcome to the parent -pub type InsertCreateOutcomeHandle<'a, EvmWiringT> = Arc< - dyn Fn(&mut Context, &mut Frame, CreateOutcome) -> EVMResultGeneric<(), EvmWiringT> - + 'a, ->; - -/// Handle EOF sub create. -pub type FrameEOFCreateHandle<'a, EvmWiringT> = Arc< - dyn Fn( - &mut Context, - Box, - ) -> EVMResultGeneric - + 'a, ->; - -/// Handle EOF create return -pub type FrameEOFCreateReturnHandle<'a, EvmWiringT> = Arc< - dyn Fn( - &mut Context, - Box, - InterpreterResult, - ) -> EVMResultGeneric - + 'a, ->; - -/// Insert EOF crate outcome to the parent -pub type InsertEOFCreateOutcomeHandle<'a, EvmWiringT> = Arc< - dyn Fn(&mut Context, &mut Frame, CreateOutcome) -> EVMResultGeneric<(), EvmWiringT> - + 'a, ->; - -/// Handles related to stack frames. -pub struct ExecutionHandler<'a, EvmWiringT: EvmWiring> { - /// Handler that created first frame action. It uses transaction - /// to determine if it is a call or create or EOF create. - pub first_frame_creation: FirstFrameCreation<'a, EvmWiringT>, - /// Handles last frame return, modified gas for refund and - /// sets tx gas limit. - pub last_frame_return: LastFrameReturnHandle<'a, EvmWiringT>, - /// Executes a single frame. - pub execute_frame: ExecuteFrameHandle<'a, EvmWiringT>, - /// Frame call - pub call: FrameCallHandle<'a, EvmWiringT>, - /// Call return - pub call_return: FrameCallReturnHandle<'a, EvmWiringT>, - /// Insert call outcome - pub insert_call_outcome: InsertCallOutcomeHandle<'a, EvmWiringT>, - /// Frame crate - pub create: FrameCreateHandle<'a, EvmWiringT>, - /// Crate return - pub create_return: FrameCreateReturnHandle<'a, EvmWiringT>, - /// Insert create outcome. - pub insert_create_outcome: InsertCreateOutcomeHandle<'a, EvmWiringT>, - /// Frame EOFCreate - pub eofcreate: FrameEOFCreateHandle<'a, EvmWiringT>, - /// EOFCreate return - pub eofcreate_return: FrameEOFCreateReturnHandle<'a, EvmWiringT>, - /// Insert EOFCreate outcome. - pub insert_eofcreate_outcome: InsertEOFCreateOutcomeHandle<'a, EvmWiringT>, -} - -impl<'a, EvmWiringT: EvmWiring + 'a> ExecutionHandler<'a, EvmWiringT> { - /// Creates mainnet ExecutionHandler. - pub fn new() -> Self { - Self { - first_frame_creation: Arc::new(mainnet::first_frame_creation::), - last_frame_return: Arc::new(mainnet::last_frame_return::), - execute_frame: Arc::new(mainnet::execute_frame::), - call: Arc::new(mainnet::call::), - call_return: Arc::new(mainnet::call_return::), - insert_call_outcome: Arc::new(mainnet::insert_call_outcome), - create: Arc::new(mainnet::create::), - create_return: Arc::new(mainnet::create_return::), - insert_create_outcome: Arc::new(mainnet::insert_create_outcome), - eofcreate: Arc::new(mainnet::eofcreate::), - eofcreate_return: Arc::new(mainnet::eofcreate_return::), - insert_eofcreate_outcome: Arc::new(mainnet::insert_eofcreate_outcome), - } - } -} - -impl<'a, EvmWiringT: EvmWiring> ExecutionHandler<'a, EvmWiringT> { - /// Executes single frame. - #[inline] - pub fn execute_frame( - &self, - frame: &mut Frame, - shared_memory: &mut SharedMemory, - instruction_tables: &InstructionTables<'_, Context>, - context: &mut Context, - ) -> EVMResultGeneric { - (self.execute_frame)(frame, shared_memory, instruction_tables, context) - } - - /// Handle call return, depending on instruction result gas will be reimbursed or not. - #[inline] - pub fn first_frame_creation( - &self, - context: &mut Context, - gas_limit: u64, - ) -> EVMResultGeneric { - (self.first_frame_creation)(context, gas_limit) - } - - /// Handle call return, depending on instruction result gas will be reimbursed or not. - #[inline] - pub fn last_frame_return( - &self, - context: &mut Context, - frame_result: &mut FrameResult, - ) -> EVMResultGeneric<(), EvmWiringT> { - (self.last_frame_return)(context, frame_result) - } - - /// Call frame call handler. - #[inline] - pub fn call( - &self, - context: &mut Context, - inputs: Box, - ) -> EVMResultGeneric { - (self.call)(context, inputs) - } - - /// Call registered handler for call return. - #[inline] - pub fn call_return( - &self, - context: &mut Context, - frame: Box, - interpreter_result: InterpreterResult, - ) -> EVMResultGeneric { - (self.call_return)(context, frame, interpreter_result) - } - - /// Call registered handler for inserting call outcome. - #[inline] - pub fn insert_call_outcome( - &self, - context: &mut Context, - frame: &mut Frame, - shared_memory: &mut SharedMemory, - outcome: CallOutcome, - ) -> EVMResultGeneric<(), EvmWiringT> { - (self.insert_call_outcome)(context, frame, shared_memory, outcome) - } - - /// Call Create frame - #[inline] - pub fn create( - &self, - context: &mut Context, - inputs: Box, - ) -> EVMResultGeneric { - (self.create)(context, inputs) - } - - /// Call handler for create return. - #[inline] - pub fn create_return( - &self, - context: &mut Context, - frame: Box, - interpreter_result: InterpreterResult, - ) -> EVMResultGeneric { - (self.create_return)(context, frame, interpreter_result) - } - - /// Call handler for inserting create outcome. - #[inline] - pub fn insert_create_outcome( - &self, - context: &mut Context, - frame: &mut Frame, - outcome: CreateOutcome, - ) -> EVMResultGeneric<(), EvmWiringT> { - (self.insert_create_outcome)(context, frame, outcome) - } - - /// Call Create frame - #[inline] - pub fn eofcreate( - &self, - context: &mut Context, - inputs: Box, - ) -> EVMResultGeneric { - (self.eofcreate)(context, inputs) - } - - /// Call handler for create return. - #[inline] - pub fn eofcreate_return( - &self, - context: &mut Context, - frame: Box, - interpreter_result: InterpreterResult, - ) -> EVMResultGeneric { - (self.eofcreate_return)(context, frame, interpreter_result) - } - - /// Call handler for inserting create outcome. - #[inline] - pub fn insert_eofcreate_outcome( - &self, - context: &mut Context, - frame: &mut Frame, - outcome: CreateOutcome, - ) -> EVMResultGeneric<(), EvmWiringT> { - (self.insert_eofcreate_outcome)(context, frame, outcome) - } -} diff --git a/crates/revm/src/handler/handle_types/generic.rs b/crates/revm/src/handler/handle_types/generic.rs deleted file mode 100644 index b68deb7375..0000000000 --- a/crates/revm/src/handler/handle_types/generic.rs +++ /dev/null @@ -1,10 +0,0 @@ -use crate::Context; -use std::sync::Arc; -use wiring::result::EVMResultGeneric; - -/// Generic Handle that takes a mutable reference to the context and returns a result. -pub type GenericContextHandle<'a, EvmWiring> = GenericContextHandleRet<'a, EvmWiring, ()>; - -/// Generic handle that takes a mutable reference to the context and returns a result. -pub type GenericContextHandleRet<'a, EvmWiringT, ReturnT> = - Arc) -> EVMResultGeneric + 'a>; diff --git a/crates/revm/src/handler/handle_types/post_execution.rs b/crates/revm/src/handler/handle_types/post_execution.rs deleted file mode 100644 index 66ca67675c..0000000000 --- a/crates/revm/src/handler/handle_types/post_execution.rs +++ /dev/null @@ -1,110 +0,0 @@ -// Includes. -use crate::{handler::mainnet, Context, EvmWiring, FrameResult}; -use interpreter::Gas; -use specification::hardfork::Spec; -use std::sync::Arc; -use wiring::result::{EVMResult, EVMResultGeneric, ResultAndState}; - -/// Reimburse the caller with ethereum it didn't spent. -pub type ReimburseCallerHandle<'a, EvmWiringT> = - Arc, &Gas) -> EVMResultGeneric<(), EvmWiringT> + 'a>; - -/// Reward beneficiary with transaction rewards. -pub type RewardBeneficiaryHandle<'a, EvmWiringT> = ReimburseCallerHandle<'a, EvmWiringT>; - -/// Main return handle, takes state from journal and transforms internal result to external. -pub type OutputHandle<'a, EvmWiringT> = - Arc, FrameResult) -> EVMResult + 'a>; - -/// End handle, takes result and state and returns final result. -/// This will be called after all the other handlers. -/// -/// It is useful for catching errors and returning them in a different way. -pub type EndHandle<'a, EvmWiringT> = - Arc, EVMResult) -> EVMResult + 'a>; - -/// Clear handle, doesn't have output, its purpose is to clear the -/// context. It will always be called even on failed validation. -pub type ClearHandle<'a, EvmWiringT> = Arc) + 'a>; - -/// Refund handle, calculates the final refund. -pub type RefundHandle<'a, EvmWiringT> = Arc, &mut Gas, i64) + 'a>; -/// Handles related to post execution after the stack loop is finished. -pub struct PostExecutionHandler<'a, EvmWiringT: EvmWiring> { - /// Calculate final refund - pub refund: RefundHandle<'a, EvmWiringT>, - /// Reimburse the caller with ethereum it didn't spend. - pub reimburse_caller: ReimburseCallerHandle<'a, EvmWiringT>, - /// Reward the beneficiary with caller fee. - pub reward_beneficiary: RewardBeneficiaryHandle<'a, EvmWiringT>, - /// Main return handle, returns the output of the transact. - pub output: OutputHandle<'a, EvmWiringT>, - /// Called when execution ends. - /// End handle in comparison to output handle will be called every time after execution. - /// Output in case of error will not be called. - pub end: EndHandle<'a, EvmWiringT>, - /// Clear handle will be called always. In comparison to end that - /// is called only on execution end, clear handle is called even if validation fails. - pub clear: ClearHandle<'a, EvmWiringT>, -} - -impl<'a, EvmWiringT: EvmWiring + 'a> PostExecutionHandler<'a, EvmWiringT> { - /// Creates mainnet MainHandles. - pub fn mainnet() -> Self { - Self { - refund: Arc::new(mainnet::refund::), - reimburse_caller: Arc::new(mainnet::reimburse_caller::), - reward_beneficiary: Arc::new(mainnet::reward_beneficiary::), - output: Arc::new(mainnet::output::), - end: Arc::new(mainnet::end::), - clear: Arc::new(mainnet::clear::), - } - } -} - -impl<'a, EvmWiringT: EvmWiring> PostExecutionHandler<'a, EvmWiringT> { - /// Calculate final refund - pub fn refund(&self, context: &mut Context, gas: &mut Gas, eip7702_refund: i64) { - (self.refund)(context, gas, eip7702_refund) - } - - /// Reimburse the caller with gas that were not spend. - pub fn reimburse_caller( - &self, - context: &mut Context, - gas: &Gas, - ) -> EVMResultGeneric<(), EvmWiringT> { - (self.reimburse_caller)(context, gas) - } - /// Reward beneficiary - pub fn reward_beneficiary( - &self, - context: &mut Context, - gas: &Gas, - ) -> EVMResultGeneric<(), EvmWiringT> { - (self.reward_beneficiary)(context, gas) - } - - /// Returns the output of transaction. - pub fn output( - &self, - context: &mut Context, - result: FrameResult, - ) -> EVMResult { - (self.output)(context, result) - } - - /// End handler. - pub fn end( - &self, - context: &mut Context, - end_output: EVMResultGeneric, EvmWiringT>, - ) -> EVMResult { - (self.end)(context, end_output) - } - - /// Clean handler. - pub fn clear(&self, context: &mut Context) { - (self.clear)(context) - } -} diff --git a/crates/revm/src/handler/handle_types/pre_execution.rs b/crates/revm/src/handler/handle_types/pre_execution.rs deleted file mode 100644 index b00f7944bb..0000000000 --- a/crates/revm/src/handler/handle_types/pre_execution.rs +++ /dev/null @@ -1,76 +0,0 @@ -// Includes. -use super::{GenericContextHandle, GenericContextHandleRet}; -use crate::{handler::mainnet, Context, ContextPrecompiles, EvmWiring}; -use specification::hardfork::Spec; -use std::sync::Arc; -use wiring::result::EVMResultGeneric; - -/// Loads precompiles into Evm -pub type LoadPrecompilesHandle<'a, EvmWiringT> = - Arc ContextPrecompiles + 'a>; - -/// Load access list accounts and beneficiary. -/// There is no need to load Caller as it is assumed that -/// it will be loaded in DeductCallerHandle. -pub type LoadAccountsHandle<'a, EvmWiringT> = GenericContextHandle<'a, EvmWiringT>; - -/// Deduct the caller to its limit. -pub type DeductCallerHandle<'a, EvmWiringT> = GenericContextHandle<'a, EvmWiringT>; - -/// Load Auth list for EIP-7702, and returns number of created accounts. -pub type ApplyEIP7702AuthListHandle<'a, EvmWiringT> = GenericContextHandleRet<'a, EvmWiringT, u64>; - -/// Handles related to pre execution before the stack loop is started. -pub struct PreExecutionHandler<'a, EvmWiringT: EvmWiring> { - /// Load precompiles - pub load_precompiles: LoadPrecompilesHandle<'a, EvmWiringT>, - /// Main load handle - pub load_accounts: LoadAccountsHandle<'a, EvmWiringT>, - /// Deduct max value from the caller. - pub deduct_caller: DeductCallerHandle<'a, EvmWiringT>, - /// Apply EIP-7702 auth list - pub apply_eip7702_auth_list: ApplyEIP7702AuthListHandle<'a, EvmWiringT>, -} - -impl<'a, EvmWiringT: EvmWiring + 'a> PreExecutionHandler<'a, EvmWiringT> { - /// Creates mainnet MainHandles. - pub fn new() -> Self { - Self { - load_precompiles: Arc::new(mainnet::load_precompiles::), - load_accounts: Arc::new(mainnet::load_accounts::), - deduct_caller: Arc::new(mainnet::deduct_caller::), - apply_eip7702_auth_list: Arc::new(mainnet::apply_eip7702_auth_list::), - } - } -} - -impl<'a, EvmWiringT: EvmWiring> PreExecutionHandler<'a, EvmWiringT> { - /// Deduct caller to its limit. - pub fn deduct_caller( - &self, - context: &mut Context, - ) -> EVMResultGeneric<(), EvmWiringT> { - (self.deduct_caller)(context) - } - - /// Main load - pub fn load_accounts( - &self, - context: &mut Context, - ) -> EVMResultGeneric<(), EvmWiringT> { - (self.load_accounts)(context) - } - - /// Apply EIP-7702 auth list and return gas refund on account that were already present. - pub fn apply_eip7702_auth_list( - &self, - context: &mut Context, - ) -> EVMResultGeneric { - (self.apply_eip7702_auth_list)(context) - } - - /// Load precompiles - pub fn load_precompiles(&self) -> ContextPrecompiles { - (self.load_precompiles)() - } -} diff --git a/crates/revm/src/handler/handle_types/validation.rs b/crates/revm/src/handler/handle_types/validation.rs deleted file mode 100644 index 5dd90fbb72..0000000000 --- a/crates/revm/src/handler/handle_types/validation.rs +++ /dev/null @@ -1,65 +0,0 @@ -use crate::{handler::mainnet, Context, EvmWiring}; -use specification::hardfork::Spec; -use std::sync::Arc; -use transaction::Transaction; -use wiring::{ - default::EnvWiring, - result::{EVMResultGeneric, InvalidTransaction}, -}; - -/// Handle that validates env. -pub type ValidateEnvHandle<'a, EvmWiringT> = - Arc) -> EVMResultGeneric<(), EvmWiringT> + 'a>; - -/// Handle that validates transaction environment against the state. -/// Second parametar is initial gas. -pub type ValidateTxEnvAgainstState<'a, EvmWiringT> = - Arc) -> EVMResultGeneric<(), EvmWiringT> + 'a>; - -/// Initial gas calculation handle -pub type ValidateInitialTxGasHandle<'a, EvmWiringT> = - Arc) -> EVMResultGeneric + 'a>; - -/// Handles related to validation. -pub struct ValidationHandler<'a, EvmWiringT: EvmWiring> { - /// Validate and calculate initial transaction gas. - pub initial_tx_gas: ValidateInitialTxGasHandle<'a, EvmWiringT>, - /// Validate transactions against state data. - pub tx_against_state: ValidateTxEnvAgainstState<'a, EvmWiringT>, - /// Validate Env. - pub env: ValidateEnvHandle<'a, EvmWiringT>, -} - -impl<'a, EvmWiringT: EvmWiring + 'a> ValidationHandler<'a, EvmWiringT> -where - ::TransactionError: From, -{ - /// Create new ValidationHandles - pub fn new() -> Self { - Self { - initial_tx_gas: Arc::new(mainnet::validate_initial_tx_gas::), - env: Arc::new(mainnet::validate_env::), - tx_against_state: Arc::new(mainnet::validate_tx_against_state::), - } - } -} - -impl<'a, EvmWiringT: EvmWiring> ValidationHandler<'a, EvmWiringT> { - /// Validate env. - pub fn env(&self, env: &EnvWiring) -> EVMResultGeneric<(), EvmWiringT> { - (self.env)(env) - } - - /// Initial gas - pub fn initial_tx_gas(&self, env: &EnvWiring) -> EVMResultGeneric { - (self.initial_tx_gas)(env) - } - - /// Validate ttansaction against the state. - pub fn tx_against_state( - &self, - context: &mut Context, - ) -> EVMResultGeneric<(), EvmWiringT> { - (self.tx_against_state)(context) - } -} diff --git a/crates/revm/src/handler/mainnet.rs b/crates/revm/src/handler/mainnet.rs deleted file mode 100644 index 4c27175c59..0000000000 --- a/crates/revm/src/handler/mainnet.rs +++ /dev/null @@ -1,23 +0,0 @@ -//! Mainnet related handlers. - -mod execution; -mod post_execution; -mod pre_execution; -mod validation; - -// Public exports - -pub use execution::{ - call, call_return, create, create_return, eofcreate, eofcreate_return, execute_frame, - first_frame_creation, insert_call_outcome, insert_create_outcome, insert_eofcreate_outcome, - last_frame_return, -}; -pub use post_execution::{clear, end, output, refund, reimburse_caller, reward_beneficiary}; -pub use pre_execution::{ - apply_eip7702_auth_list, deduct_caller, deduct_caller_inner, load_accounts, load_precompiles, -}; -pub use validation::{ - validate_block_env, validate_eip4844_tx, validate_env, validate_initial_tx_gas, - validate_priority_fee_tx, validate_tx_against_account, validate_tx_against_state, - validate_tx_env, -}; diff --git a/crates/revm/src/handler/mainnet/execution.rs b/crates/revm/src/handler/mainnet/execution.rs deleted file mode 100644 index 062fcd50c4..0000000000 --- a/crates/revm/src/handler/mainnet/execution.rs +++ /dev/null @@ -1,302 +0,0 @@ -use crate::{ - frame::EOFCreateFrame, CallFrame, Context, CreateFrame, EvmWiring, Frame, FrameOrResult, - FrameResult, -}; -use bytecode::EOF_MAGIC_BYTES; -use core::mem; -use interpreter::{ - return_ok, return_revert, table::InstructionTables, CallInputs, CallOutcome, CallScheme, - CallValue, CreateInputs, CreateOutcome, CreateScheme, EOFCreateInputs, EOFCreateKind, Gas, - InterpreterAction, InterpreterResult, NewFrameAction, SharedMemory, EMPTY_SHARED_MEMORY, -}; -use primitives::TxKind; -use specification::hardfork::{Spec, SpecId}; -use std::boxed::Box; -use wiring::{ - result::{EVMError, EVMResultGeneric}, - Transaction, -}; - -/// Execute frame -#[inline] -pub fn execute_frame( - frame: &mut Frame, - shared_memory: &mut SharedMemory, - instruction_tables: &InstructionTables<'_, Context>, - context: &mut Context, -) -> EVMResultGeneric { - let interpreter = frame.interpreter_mut(); - let memory = mem::replace(shared_memory, EMPTY_SHARED_MEMORY); - let next_action = match instruction_tables { - InstructionTables::Plain(table) => interpreter.run(memory, table, context), - InstructionTables::Boxed(table) => interpreter.run(memory, table, context), - }; - // Take the shared memory back. - *shared_memory = interpreter.take_memory(); - - Ok(next_action) -} - -/// First frame creation. -pub fn first_frame_creation( - context: &mut Context, - gas_limit: u64, -) -> EVMResultGeneric { - // Make new frame action. - let tx = &context.evm.env.tx; - - let input = tx.common_fields().input().clone(); - - let new_frame = match tx.kind() { - TxKind::Call(target_address) => NewFrameAction::Call(Box::new(CallInputs { - input, - gas_limit, - target_address, - bytecode_address: target_address, - caller: tx.common_fields().caller(), - value: CallValue::Transfer(tx.common_fields().value()), - scheme: CallScheme::Call, - is_static: false, - is_eof: false, - return_memory_offset: 0..0, - })), - TxKind::Create => { - // if first byte of data is magic 0xEF00, then it is EOFCreate. - if SPEC::enabled(SpecId::PRAGUE_EOF) && input.starts_with(&EOF_MAGIC_BYTES) { - NewFrameAction::EOFCreate(Box::new(EOFCreateInputs::new( - tx.common_fields().caller(), - tx.common_fields().value(), - gas_limit, - EOFCreateKind::Tx { initdata: input }, - ))) - } else { - NewFrameAction::Create(Box::new(CreateInputs { - caller: tx.common_fields().caller(), - scheme: CreateScheme::Create, - value: tx.common_fields().value(), - init_code: input, - gas_limit, - })) - } - } - }; - - Ok(new_frame) -} - -/// Handle output of the transaction -#[inline] -pub fn last_frame_return( - context: &mut Context, - frame_result: &mut FrameResult, -) -> EVMResultGeneric<(), EvmWiringT> { - let instruction_result = frame_result.interpreter_result().result; - let gas = frame_result.gas_mut(); - let remaining = gas.remaining(); - let refunded = gas.refunded(); - - // Spend the gas limit. Gas is reimbursed when the tx returns successfully. - *gas = Gas::new_spent(context.evm.env.tx.common_fields().gas_limit()); - - match instruction_result { - return_ok!() => { - gas.erase_cost(remaining); - gas.record_refund(refunded); - } - return_revert!() => { - gas.erase_cost(remaining); - } - _ => {} - } - Ok(()) -} - -/// Handle frame sub call. -#[inline] -pub fn call( - context: &mut Context, - inputs: Box, -) -> EVMResultGeneric { - context.evm.make_call_frame(&inputs) -} - -#[inline] -pub fn call_return( - context: &mut Context, - frame: Box, - interpreter_result: InterpreterResult, -) -> EVMResultGeneric { - context - .evm - .call_return(&interpreter_result, frame.frame_data.checkpoint); - Ok(CallOutcome::new( - interpreter_result, - frame.return_memory_range, - )) -} - -#[inline] -pub fn insert_call_outcome( - context: &mut Context, - frame: &mut Frame, - shared_memory: &mut SharedMemory, - outcome: CallOutcome, -) -> EVMResultGeneric<(), EvmWiringT> { - context.evm.take_error().map_err(EVMError::Database)?; - - frame - .frame_data_mut() - .interpreter - .insert_call_outcome(shared_memory, outcome); - Ok(()) -} - -/// Handle frame sub create. -#[inline] -pub fn create( - context: &mut Context, - inputs: Box, -) -> EVMResultGeneric { - context - .evm - .make_create_frame(SPEC::SPEC_ID, &inputs) - .map_err(EVMError::Database) -} - -#[inline] -pub fn create_return( - context: &mut Context, - frame: Box, - mut interpreter_result: InterpreterResult, -) -> EVMResultGeneric { - context.evm.create_return::( - &mut interpreter_result, - frame.created_address, - frame.frame_data.checkpoint, - ); - Ok(CreateOutcome::new( - interpreter_result, - Some(frame.created_address), - )) -} - -#[inline] -pub fn insert_create_outcome( - context: &mut Context, - frame: &mut Frame, - outcome: CreateOutcome, -) -> EVMResultGeneric<(), EvmWiringT> { - context.evm.take_error().map_err(EVMError::Database)?; - - frame - .frame_data_mut() - .interpreter - .insert_create_outcome(outcome); - Ok(()) -} - -/// Handle frame sub create. -#[inline] -pub fn eofcreate( - context: &mut Context, - inputs: Box, -) -> EVMResultGeneric { - context - .evm - .make_eofcreate_frame(SPEC::SPEC_ID, &inputs) - .map_err(EVMError::Database) -} - -#[inline] -pub fn eofcreate_return( - context: &mut Context, - frame: Box, - mut interpreter_result: InterpreterResult, -) -> EVMResultGeneric { - context.evm.eofcreate_return::( - &mut interpreter_result, - frame.created_address, - frame.frame_data.checkpoint, - ); - Ok(CreateOutcome::new( - interpreter_result, - Some(frame.created_address), - )) -} - -#[inline] -pub fn insert_eofcreate_outcome( - context: &mut Context, - frame: &mut Frame, - outcome: CreateOutcome, -) -> EVMResultGeneric<(), EvmWiringT> { - context.evm.take_error().map_err(EVMError::Database)?; - - frame - .frame_data_mut() - .interpreter - .insert_eofcreate_outcome(outcome); - Ok(()) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::handler::mainnet::refund; - use interpreter::InstructionResult; - use primitives::Bytes; - use specification::hardfork::CancunSpec; - use wiring::{default::EnvWiring, DefaultEthereumWiring}; - - /// Creates frame result. - fn call_last_frame_return(instruction_result: InstructionResult, gas: Gas) -> Gas { - let mut env = EnvWiring::::default(); - env.tx.gas_limit = 100; - - let mut ctx = Context::default(); - ctx.evm.inner.env = Box::new(env); - let mut first_frame = FrameResult::Call(CallOutcome::new( - InterpreterResult { - result: instruction_result, - output: Bytes::new(), - gas, - }, - 0..0, - )); - last_frame_return::(&mut ctx, &mut first_frame).unwrap(); - refund::(&mut ctx, first_frame.gas_mut(), 0); - *first_frame.gas() - } - - #[test] - fn test_consume_gas() { - let gas = call_last_frame_return(InstructionResult::Stop, Gas::new(90)); - assert_eq!(gas.remaining(), 90); - assert_eq!(gas.spent(), 10); - assert_eq!(gas.refunded(), 0); - } - - #[test] - fn test_consume_gas_with_refund() { - let mut return_gas = Gas::new(90); - return_gas.record_refund(30); - - let gas = call_last_frame_return(InstructionResult::Stop, return_gas); - assert_eq!(gas.remaining(), 90); - assert_eq!(gas.spent(), 10); - assert_eq!(gas.refunded(), 2); - - let gas = call_last_frame_return(InstructionResult::Revert, return_gas); - assert_eq!(gas.remaining(), 90); - assert_eq!(gas.spent(), 10); - assert_eq!(gas.refunded(), 0); - } - - #[test] - fn test_revert_gas() { - let gas = call_last_frame_return(InstructionResult::Revert, Gas::new(90)); - assert_eq!(gas.remaining(), 90); - assert_eq!(gas.spent(), 10); - assert_eq!(gas.refunded(), 0); - } -} diff --git a/crates/revm/src/handler/mainnet/post_execution.rs b/crates/revm/src/handler/mainnet/post_execution.rs deleted file mode 100644 index 784415e25f..0000000000 --- a/crates/revm/src/handler/mainnet/post_execution.rs +++ /dev/null @@ -1,141 +0,0 @@ -use crate::{Context, EvmWiring, FrameResult}; -use interpreter::{Gas, SuccessOrHalt}; -use primitives::U256; -use specification::hardfork::{Spec, SpecId}; -use wiring::{ - result::{EVMError, EVMResult, EVMResultGeneric, ExecutionResult, ResultAndState}, - Block, Transaction, -}; - -/// Mainnet end handle does not change the output. -#[inline] -pub fn end( - _context: &mut Context, - evm_output: EVMResult, -) -> EVMResult { - evm_output -} - -/// Clear handle clears error and journal state. -#[inline] -pub fn clear(context: &mut Context) { - // clear error and journaled state. - let _ = context.evm.take_error(); - context.evm.inner.journaled_state.clear(); -} - -/// Reward beneficiary with gas fee. -#[inline] -pub fn reward_beneficiary( - context: &mut Context, - gas: &Gas, -) -> EVMResultGeneric<(), EvmWiringT> { - let beneficiary = *context.evm.env.block.coinbase(); - let effective_gas_price = context.evm.env.effective_gas_price(); - - // transfer fee to coinbase/beneficiary. - // EIP-1559 discard basefee for coinbase transfer. Basefee amount of gas is discarded. - let coinbase_gas_price = if SPEC::enabled(SpecId::LONDON) { - effective_gas_price.saturating_sub(*context.evm.env.block.basefee()) - } else { - effective_gas_price - }; - - let coinbase_account = context - .evm - .inner - .journaled_state - .load_account(beneficiary, &mut context.evm.inner.db) - .map_err(EVMError::Database)?; - - coinbase_account.data.mark_touch(); - coinbase_account.data.info.balance = coinbase_account - .data - .info - .balance - .saturating_add(coinbase_gas_price * U256::from(gas.spent() - gas.refunded() as u64)); - - Ok(()) -} - -pub fn refund( - _context: &mut Context, - gas: &mut Gas, - eip7702_refund: i64, -) { - gas.record_refund(eip7702_refund); - - // Calculate gas refund for transaction. - // If spec is set to london, it will decrease the maximum refund amount to 5th part of - // gas spend. (Before london it was 2th part of gas spend) - gas.set_final_refund(SPEC::SPEC_ID.is_enabled_in(SpecId::LONDON)); -} - -#[inline] -pub fn reimburse_caller( - context: &mut Context, - gas: &Gas, -) -> EVMResultGeneric<(), EvmWiringT> { - let caller = context.evm.env.tx.common_fields().caller(); - let effective_gas_price = context.evm.env.effective_gas_price(); - - // return balance of not spend gas. - let caller_account = context - .evm - .inner - .journaled_state - .load_account(caller, &mut context.evm.inner.db) - .map_err(EVMError::Database)?; - - caller_account.data.info.balance = - caller_account.data.info.balance.saturating_add( - effective_gas_price * U256::from(gas.remaining() + gas.refunded() as u64), - ); - - Ok(()) -} - -/// Main return handle, returns the output of the transaction. -#[inline] -pub fn output( - context: &mut Context, - result: FrameResult, -) -> EVMResult { - context.evm.take_error().map_err(EVMError::Database)?; - - // used gas with refund calculated. - let gas_refunded = result.gas().refunded() as u64; - let final_gas_used = result.gas().spent() - gas_refunded; - let output = result.output(); - let instruction_result = result.into_interpreter_result(); - - // reset journal and return present state. - let (state, logs) = context.evm.journaled_state.finalize(); - - let result = match SuccessOrHalt::::from(instruction_result.result) { - SuccessOrHalt::Success(reason) => ExecutionResult::Success { - reason, - gas_used: final_gas_used, - gas_refunded, - logs, - output, - }, - SuccessOrHalt::Revert => ExecutionResult::Revert { - gas_used: final_gas_used, - output: output.into_data(), - }, - SuccessOrHalt::Halt(reason) => ExecutionResult::Halt { - reason, - gas_used: final_gas_used, - }, - // Only two internal return flags. - flag @ (SuccessOrHalt::FatalExternalError | SuccessOrHalt::Internal(_)) => { - panic!( - "Encountered unexpected internal return flag: {:?} with instruction result: {:?}", - flag, instruction_result - ) - } - }; - - Ok(ResultAndState { result, state }) -} diff --git a/crates/revm/src/handler/mainnet/pre_execution.rs b/crates/revm/src/handler/mainnet/pre_execution.rs deleted file mode 100644 index 2741c5073e..0000000000 --- a/crates/revm/src/handler/mainnet/pre_execution.rs +++ /dev/null @@ -1,196 +0,0 @@ -//! Handles related to the main function of the EVM. -//! -//! They handle initial setup of the EVM, call loop and the final return of the EVM - -use crate::{Context, ContextPrecompiles, EvmWiring, JournalEntry}; -use bytecode::Bytecode; -use precompile::PrecompileSpecId; -use primitives::{BLOCKHASH_STORAGE_ADDRESS, U256}; -use specification::{ - eip7702, - hardfork::{Spec, SpecId}, -}; -use state::Account; -use transaction::{eip7702::Authorization, Eip7702Tx}; -use wiring::{ - default::EnvWiring, - result::{EVMError, EVMResultGeneric}, - Block, Transaction, TransactionType, -}; - -/// Main precompile load -#[inline] -pub fn load_precompiles() -> ContextPrecompiles { - ContextPrecompiles::new(PrecompileSpecId::from_spec_id(SPEC::SPEC_ID)) -} - -/// Main load handle -#[inline] -pub fn load_accounts( - context: &mut Context, -) -> EVMResultGeneric<(), EvmWiringT> { - // set journaling state flag. - context.evm.journaled_state.set_spec_id(SPEC::SPEC_ID); - - // load coinbase - // EIP-3651: Warm COINBASE. Starts the `COINBASE` address warm - if SPEC::enabled(SpecId::SHANGHAI) { - let coinbase = *context.evm.inner.env.block.coinbase(); - context - .evm - .journaled_state - .warm_preloaded_addresses - .insert(coinbase); - } - - // Load blockhash storage address - // EIP-2935: Serve historical block hashes from state - if SPEC::enabled(SpecId::PRAGUE) { - context - .evm - .journaled_state - .warm_preloaded_addresses - .insert(BLOCKHASH_STORAGE_ADDRESS); - } - - // Load access list - context.evm.load_access_list().map_err(EVMError::Database)?; - Ok(()) -} - -/// Helper function that deducts the caller balance. -#[inline] -pub fn deduct_caller_inner( - caller_account: &mut Account, - env: &EnvWiring, -) { - // Subtract gas costs from the caller's account. - // We need to saturate the gas cost to prevent underflow in case that `disable_balance_check` is enabled. - let mut gas_cost = - U256::from(env.tx.common_fields().gas_limit()).saturating_mul(env.effective_gas_price()); - - // EIP-4844 - if let Some(data_fee) = env.calc_data_fee() { - gas_cost = gas_cost.saturating_add(data_fee); - } - - // set new caller account balance. - caller_account.info.balance = caller_account.info.balance.saturating_sub(gas_cost); - - // bump the nonce for calls. Nonce for CREATE will be bumped in `handle_create`. - if env.tx.kind().is_call() { - // Nonce is already checked - caller_account.info.nonce = caller_account.info.nonce.saturating_add(1); - } - - // touch account so we know it is changed. - caller_account.mark_touch(); -} - -/// Deducts the caller balance to the transaction limit. -#[inline] -pub fn deduct_caller( - context: &mut Context, -) -> EVMResultGeneric<(), EvmWiringT> { - let caller = context.evm.inner.env.tx.common_fields().caller(); - // load caller's account. - let caller_account = context - .evm - .inner - .journaled_state - .load_account(caller, &mut context.evm.inner.db) - .map_err(EVMError::Database)?; - - // deduct gas cost from caller's account. - deduct_caller_inner::(caller_account.data, &context.evm.inner.env); - - // Ensure tx kind is call - if context.evm.inner.env.tx.kind().is_call() { - // Push NonceChange entry - context - .evm - .inner - .journaled_state - .journal - .last_mut() - .unwrap() - .push(JournalEntry::NonceChange { address: caller }); - } - Ok(()) -} - -/// Apply EIP-7702 auth list and return number gas refund on already created accounts. -#[inline] -pub fn apply_eip7702_auth_list( - context: &mut Context, -) -> EVMResultGeneric { - // EIP-7702. Load bytecode to authorized accounts. - if !SPEC::enabled(SpecId::PRAGUE) { - return Ok(0); - } - - // return if there is no auth list. - let tx = &context.evm.inner.env.tx; - if tx.tx_type().into() != TransactionType::Eip7702 { - return Ok(0); - } - - //let authorization_list = tx.eip7702().authorization_list(); - - let mut refunded_accounts = 0; - for authorization in tx.eip7702().authorization_list_iter() { - // 1. recover authority and authorized addresses. - // authority = ecrecover(keccak(MAGIC || rlp([chain_id, address, nonce])), y_parity, r, s] - let Some(authority) = authorization.authority() else { - continue; - }; - - // 2. Verify the chain id is either 0 or the chain's current ID. - if !authorization.chain_id().is_zero() - && authorization.chain_id() != U256::from(context.evm.inner.env.cfg.chain_id) - { - continue; - } - - // warm authority account and check nonce. - // 3. Add authority to accessed_addresses (as defined in EIP-2929.) - let mut authority_acc = context - .evm - .inner - .journaled_state - .load_code(authority, &mut context.evm.inner.db) - .map_err(EVMError::Database)?; - - // 4. Verify the code of authority is either empty or already delegated. - if let Some(bytecode) = &authority_acc.info.code { - // if it is not empty and it is not eip7702 - if !bytecode.is_empty() && !bytecode.is_eip7702() { - continue; - } - } - - // 5. Verify the nonce of authority is equal to nonce. - if authorization.nonce() != authority_acc.info.nonce { - continue; - } - - // 6. Refund the sender PER_EMPTY_ACCOUNT_COST - PER_AUTH_BASE_COST gas if authority exists in the trie. - if !authority_acc.is_empty() { - refunded_accounts += 1; - } - - // 7. Set the code of authority to be 0xef0100 || address. This is a delegation designation. - let bytecode = Bytecode::new_eip7702(authorization.address()); - authority_acc.info.code_hash = bytecode.hash_slow(); - authority_acc.info.code = Some(bytecode); - - // 8. Increase the nonce of authority by one. - authority_acc.info.nonce = authority_acc.info.nonce.saturating_add(1); - authority_acc.mark_touch(); - } - - let refunded_gas = - refunded_accounts * (eip7702::PER_EMPTY_ACCOUNT_COST - eip7702::PER_AUTH_BASE_COST); - - Ok(refunded_gas) -} diff --git a/crates/revm/src/handler/register.rs b/crates/revm/src/handler/register.rs deleted file mode 100644 index f71d9b24f6..0000000000 --- a/crates/revm/src/handler/register.rs +++ /dev/null @@ -1,32 +0,0 @@ -use crate::{handler::Handler, Context, EvmWiring}; -use std::boxed::Box; - -/// EVM Handler -pub type EvmHandler<'a, EvmWiringT> = Handler<'a, EvmWiringT, Context>; - -// Handle register -pub type HandleRegister = for<'a> fn(&mut EvmHandler<'a, EvmWiringT>); - -// Boxed handle register -pub type HandleRegisterBox<'a, EvmWiringT> = - Box Fn(&mut EvmHandler<'e, EvmWiringT>) + 'a>; - -pub enum HandleRegisters<'a, EvmWiringT: EvmWiring> { - /// Plain function register - Plain(HandleRegister), - /// Boxed function register. - Box(HandleRegisterBox<'a, EvmWiringT>), -} - -impl<'register, EvmWiringT: EvmWiring> HandleRegisters<'register, EvmWiringT> { - /// Call register function to modify EvmHandler. - pub fn register<'evm>(&self, handler: &mut EvmHandler<'evm, EvmWiringT>) - where - 'evm: 'register, - { - match self { - HandleRegisters::Plain(f) => f(handler), - HandleRegisters::Box(f) => f(handler), - } - } -} diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs index c1663e252d..865c02a66d 100644 --- a/crates/revm/src/lib.rs +++ b/crates/revm/src/lib.rs @@ -2,44 +2,31 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] #![cfg_attr(not(feature = "std"), no_std)] -#[macro_use] #[cfg(not(feature = "std"))] extern crate alloc as std; // reexport dependencies pub use bytecode; +pub use context; +pub use context_interface; pub use database_interface; +pub use handler; +pub use handler_interface; pub use interpreter; pub use precompile; pub use primitives; pub use specification; pub use state; -pub use transaction; -pub use wiring; -// Define modules. -mod builder; -mod context; -#[cfg(any(test, feature = "test-utils"))] -pub mod test_utils; +// Modules. mod evm; -mod evm_wiring; -mod frame; -pub mod handler; -mod journaled_state; +mod exec; // Export items. -pub use builder::EvmBuilder; -pub use context::{ - Context, ContextPrecompile, ContextPrecompiles, ContextStatefulPrecompile, - ContextStatefulPrecompileArc, ContextStatefulPrecompileBox, ContextStatefulPrecompileMut, - ContextWithEvmWiring, EvmContext, InnerEvmContext, -}; +pub use context::journaled_state::{JournalEntry, JournaledState}; +pub use context::Context; pub use database_interface::{Database, DatabaseCommit, DatabaseRef}; -pub use evm::{Evm, CALL_STACK_LIMIT}; -pub use evm_wiring::EvmWiring; -pub use frame::{CallFrame, CreateFrame, Frame, FrameData, FrameOrResult, FrameResult}; -pub use handler::{register::EvmHandler, Handler}; -pub use journaled_state::{JournalCheckpoint, JournalEntry, JournaledState}; +pub use evm::{Error, EthContext, Evm, MainEvm}; +pub use exec::{EvmCommit, EvmExec}; diff --git a/crates/revm/src/test_utils.rs b/crates/revm/src/test_utils.rs deleted file mode 100644 index 5740022066..0000000000 --- a/crates/revm/src/test_utils.rs +++ /dev/null @@ -1,2 +0,0 @@ -#[doc(hidden)] -pub use crate::context::evm_context::test_utils::*; diff --git a/crates/specification/src/constants.rs b/crates/specification/src/constants.rs index 4f489fb11b..31cf337c40 100644 --- a/crates/specification/src/constants.rs +++ b/crates/specification/src/constants.rs @@ -10,3 +10,6 @@ pub const MAX_CODE_SIZE: usize = 0x6000; /// /// Limit of maximum initcode size is `2 * MAX_CODE_SIZE`. pub const MAX_INITCODE_SIZE: usize = 2 * MAX_CODE_SIZE; + +/// EVM call stack limit. +pub const CALL_STACK_LIMIT: u64 = 1024; diff --git a/crates/specification/src/eip4844.rs b/crates/specification/src/eip4844.rs index 0c18be4002..9bab7e21dc 100644 --- a/crates/specification/src/eip4844.rs +++ b/crates/specification/src/eip4844.rs @@ -1,4 +1,4 @@ -/// === EIP-4844 constants === +// === EIP-4844 constants === /// Gas consumption of a single data blob (== blob byte size). pub const GAS_PER_BLOB: u64 = 1 << 17; diff --git a/crates/specification/src/hardfork.rs b/crates/specification/src/hardfork.rs index 56d7590a6b..2363a355b8 100644 --- a/crates/specification/src/hardfork.rs +++ b/crates/specification/src/hardfork.rs @@ -1,5 +1,6 @@ #![allow(non_camel_case_types)] +pub use std::string::{String, ToString}; pub use SpecId::*; /// Specification IDs and their activation block. @@ -9,26 +10,26 @@ pub use SpecId::*; #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, enumn::N)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum SpecId { - FRONTIER = 0, // Frontier 0 - FRONTIER_THAWING = 1, // Frontier Thawing 200000 - HOMESTEAD = 2, // Homestead 1150000 - DAO_FORK = 3, // DAO Fork 1920000 - TANGERINE = 4, // Tangerine Whistle 2463000 - SPURIOUS_DRAGON = 5, // Spurious Dragon 2675000 - BYZANTIUM = 6, // Byzantium 4370000 - CONSTANTINOPLE = 7, // Constantinople 7280000 is overwritten with PETERSBURG - PETERSBURG = 8, // Petersburg 7280000 - ISTANBUL = 9, // Istanbul 9069000 - MUIR_GLACIER = 10, // Muir Glacier 9200000 - BERLIN = 11, // Berlin 12244000 - LONDON = 12, // London 12965000 - ARROW_GLACIER = 13, // Arrow Glacier 13773000 - GRAY_GLACIER = 14, // Gray Glacier 15050000 - MERGE = 15, // Paris/Merge 15537394 (TTD: 58750000000000000000000) - SHANGHAI = 16, // Shanghai 17034870 (Timestamp: 1681338455) - CANCUN = 17, // Cancun 19426587 (Timestamp: 1710338135) - PRAGUE = 18, // Prague TBD - PRAGUE_EOF = 19, // Prague+EOF TBD + FRONTIER = 0, // Frontier 0 + FRONTIER_THAWING, // Frontier Thawing 200000 + HOMESTEAD, // Homestead 1150000 + DAO_FORK, // DAO Fork 1920000 + TANGERINE, // Tangerine Whistle 2463000 + SPURIOUS_DRAGON, // Spurious Dragon 2675000 + BYZANTIUM, // Byzantium 4370000 + CONSTANTINOPLE, // Constantinople 7280000 is overwritten with PETERSBURG + PETERSBURG, // Petersburg 7280000 + ISTANBUL, // Istanbul 9069000 + MUIR_GLACIER, // Muir Glacier 9200000 + BERLIN, // Berlin 12244000 + LONDON, // London 12965000 + ARROW_GLACIER, // Arrow Glacier 13773000 + GRAY_GLACIER, // Gray Glacier 15050000 + MERGE, // Paris/Merge 15537394 (TTD: 58750000000000000000000) + SHANGHAI, // Shanghai 17034870 (Timestamp: 1681338455) + CANCUN, // Cancun 19426587 (Timestamp: 1710338135) + PRAGUE, // Prague TBD + PRAGUE_EOF, // Prague+EOF TBD #[default] LATEST = u8::MAX, } @@ -43,18 +44,12 @@ impl SpecId { /// Returns `true` if the given specification ID is enabled in this spec. #[inline] pub const fn is_enabled_in(self, other: Self) -> bool { - Self::enabled(self, other) - } - - /// Returns `true` if the given specification ID is enabled in this spec. - #[inline] - pub const fn enabled(our: SpecId, other: SpecId) -> bool { - our as u8 >= other as u8 + self as u8 >= other as u8 } } /// String identifiers for hardforks. -pub mod id { +pub mod name { pub const FRONTIER: &str = "Frontier"; pub const FRONTIER_THAWING: &str = "Frontier Thawing"; pub const HOMESTEAD: &str = "Homestead"; @@ -81,27 +76,27 @@ pub mod id { impl From<&str> for SpecId { fn from(name: &str) -> Self { match name { - id::FRONTIER => Self::FRONTIER, - id::FRONTIER_THAWING => Self::FRONTIER_THAWING, - id::HOMESTEAD => Self::HOMESTEAD, - id::DAO_FORK => Self::DAO_FORK, - id::TANGERINE => Self::TANGERINE, - id::SPURIOUS_DRAGON => Self::SPURIOUS_DRAGON, - id::BYZANTIUM => Self::BYZANTIUM, - id::CONSTANTINOPLE => Self::CONSTANTINOPLE, - id::PETERSBURG => Self::PETERSBURG, - id::ISTANBUL => Self::ISTANBUL, - id::MUIR_GLACIER => Self::MUIR_GLACIER, - id::BERLIN => Self::BERLIN, - id::LONDON => Self::LONDON, - id::ARROW_GLACIER => Self::ARROW_GLACIER, - id::GRAY_GLACIER => Self::GRAY_GLACIER, - id::MERGE => Self::MERGE, - id::SHANGHAI => Self::SHANGHAI, - id::CANCUN => Self::CANCUN, - id::PRAGUE => Self::PRAGUE, - id::PRAGUE_EOF => Self::PRAGUE_EOF, - id::LATEST => Self::LATEST, + name::FRONTIER => Self::FRONTIER, + name::FRONTIER_THAWING => Self::FRONTIER_THAWING, + name::HOMESTEAD => Self::HOMESTEAD, + name::DAO_FORK => Self::DAO_FORK, + name::TANGERINE => Self::TANGERINE, + name::SPURIOUS_DRAGON => Self::SPURIOUS_DRAGON, + name::BYZANTIUM => Self::BYZANTIUM, + name::CONSTANTINOPLE => Self::CONSTANTINOPLE, + name::PETERSBURG => Self::PETERSBURG, + name::ISTANBUL => Self::ISTANBUL, + name::MUIR_GLACIER => Self::MUIR_GLACIER, + name::BERLIN => Self::BERLIN, + name::LONDON => Self::LONDON, + name::ARROW_GLACIER => Self::ARROW_GLACIER, + name::GRAY_GLACIER => Self::GRAY_GLACIER, + name::MERGE => Self::MERGE, + name::SHANGHAI => Self::SHANGHAI, + name::CANCUN => Self::CANCUN, + name::PRAGUE => Self::PRAGUE, + name::PRAGUE_EOF => Self::PRAGUE_EOF, + name::LATEST => Self::LATEST, _ => Self::LATEST, } } @@ -110,173 +105,33 @@ impl From<&str> for SpecId { impl From for &'static str { fn from(spec_id: SpecId) -> Self { match spec_id { - SpecId::FRONTIER => id::FRONTIER, - SpecId::FRONTIER_THAWING => id::FRONTIER_THAWING, - SpecId::HOMESTEAD => id::HOMESTEAD, - SpecId::DAO_FORK => id::DAO_FORK, - SpecId::TANGERINE => id::TANGERINE, - SpecId::SPURIOUS_DRAGON => id::SPURIOUS_DRAGON, - SpecId::BYZANTIUM => id::BYZANTIUM, - SpecId::CONSTANTINOPLE => id::CONSTANTINOPLE, - SpecId::PETERSBURG => id::PETERSBURG, - SpecId::ISTANBUL => id::ISTANBUL, - SpecId::MUIR_GLACIER => id::MUIR_GLACIER, - SpecId::BERLIN => id::BERLIN, - SpecId::LONDON => id::LONDON, - SpecId::ARROW_GLACIER => id::ARROW_GLACIER, - SpecId::GRAY_GLACIER => id::GRAY_GLACIER, - SpecId::MERGE => id::MERGE, - SpecId::SHANGHAI => id::SHANGHAI, - SpecId::CANCUN => id::CANCUN, - SpecId::PRAGUE => id::PRAGUE, - SpecId::PRAGUE_EOF => id::PRAGUE_EOF, - SpecId::LATEST => id::LATEST, + SpecId::FRONTIER => name::FRONTIER, + SpecId::FRONTIER_THAWING => name::FRONTIER_THAWING, + SpecId::HOMESTEAD => name::HOMESTEAD, + SpecId::DAO_FORK => name::DAO_FORK, + SpecId::TANGERINE => name::TANGERINE, + SpecId::SPURIOUS_DRAGON => name::SPURIOUS_DRAGON, + SpecId::BYZANTIUM => name::BYZANTIUM, + SpecId::CONSTANTINOPLE => name::CONSTANTINOPLE, + SpecId::PETERSBURG => name::PETERSBURG, + SpecId::ISTANBUL => name::ISTANBUL, + SpecId::MUIR_GLACIER => name::MUIR_GLACIER, + SpecId::BERLIN => name::BERLIN, + SpecId::LONDON => name::LONDON, + SpecId::ARROW_GLACIER => name::ARROW_GLACIER, + SpecId::GRAY_GLACIER => name::GRAY_GLACIER, + SpecId::MERGE => name::MERGE, + SpecId::SHANGHAI => name::SHANGHAI, + SpecId::CANCUN => name::CANCUN, + SpecId::PRAGUE => name::PRAGUE, + SpecId::PRAGUE_EOF => name::PRAGUE_EOF, + SpecId::LATEST => name::LATEST, } } } -pub trait Spec: Sized + 'static { - /// The specification ID. - const SPEC_ID: SpecId; - - /// Returns `true` if the given specification ID is enabled in this spec. - #[inline] - fn enabled(spec_id: SpecId) -> bool { - SpecId::enabled(Self::SPEC_ID, spec_id) - } -} - -macro_rules! spec { - ($spec_id:ident, $spec_name:ident) => { - #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] - pub struct $spec_name; - - impl Spec for $spec_name { - const SPEC_ID: SpecId = $spec_id; - } - }; -} - -spec!(FRONTIER, FrontierSpec); -// FRONTIER_THAWING no EVM spec change -spec!(HOMESTEAD, HomesteadSpec); -// DAO_FORK no EVM spec change -spec!(TANGERINE, TangerineSpec); -spec!(SPURIOUS_DRAGON, SpuriousDragonSpec); -spec!(BYZANTIUM, ByzantiumSpec); -// CONSTANTINOPLE was overridden with PETERSBURG -spec!(PETERSBURG, PetersburgSpec); -spec!(ISTANBUL, IstanbulSpec); -// MUIR_GLACIER no EVM spec change -spec!(BERLIN, BerlinSpec); -spec!(LONDON, LondonSpec); -// ARROW_GLACIER no EVM spec change -// GRAY_GLACIER no EVM spec change -spec!(MERGE, MergeSpec); -spec!(SHANGHAI, ShanghaiSpec); -spec!(CANCUN, CancunSpec); -spec!(PRAGUE, PragueSpec); -spec!(PRAGUE_EOF, PragueEofSpec); - -spec!(LATEST, LatestSpec); - -#[macro_export] -macro_rules! spec_to_generic { - ($spec_id:expr, $e:expr) => {{ - match $spec_id { - $crate::hardfork::SpecId::FRONTIER | $crate::hardfork::SpecId::FRONTIER_THAWING => { - use $crate::hardfork::FrontierSpec as SPEC; - $e - } - $crate::hardfork::SpecId::HOMESTEAD | $crate::hardfork::SpecId::DAO_FORK => { - use $crate::hardfork::HomesteadSpec as SPEC; - $e - } - $crate::hardfork::SpecId::TANGERINE => { - use $crate::hardfork::TangerineSpec as SPEC; - $e - } - $crate::hardfork::SpecId::SPURIOUS_DRAGON => { - use $crate::hardfork::SpuriousDragonSpec as SPEC; - $e - } - $crate::hardfork::SpecId::BYZANTIUM => { - use $crate::hardfork::ByzantiumSpec as SPEC; - $e - } - $crate::hardfork::SpecId::PETERSBURG | $crate::hardfork::SpecId::CONSTANTINOPLE => { - use $crate::hardfork::PetersburgSpec as SPEC; - $e - } - $crate::hardfork::SpecId::ISTANBUL | $crate::hardfork::SpecId::MUIR_GLACIER => { - use $crate::hardfork::IstanbulSpec as SPEC; - $e - } - $crate::hardfork::SpecId::BERLIN => { - use $crate::hardfork::BerlinSpec as SPEC; - $e - } - $crate::hardfork::SpecId::LONDON - | $crate::hardfork::SpecId::ARROW_GLACIER - | $crate::hardfork::SpecId::GRAY_GLACIER => { - use $crate::hardfork::LondonSpec as SPEC; - $e - } - $crate::hardfork::SpecId::MERGE => { - use $crate::hardfork::MergeSpec as SPEC; - $e - } - $crate::hardfork::SpecId::SHANGHAI => { - use $crate::hardfork::ShanghaiSpec as SPEC; - $e - } - $crate::hardfork::SpecId::CANCUN => { - use $crate::hardfork::CancunSpec as SPEC; - $e - } - $crate::hardfork::SpecId::LATEST => { - use $crate::hardfork::LatestSpec as SPEC; - $e - } - $crate::hardfork::SpecId::PRAGUE => { - use $crate::hardfork::PragueSpec as SPEC; - $e - } - $crate::hardfork::SpecId::PRAGUE_EOF => { - use $crate::hardfork::PragueEofSpec as SPEC; - $e - } - } - }}; -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn spec_to_generic() { - use SpecId::*; - - spec_to_generic!(FRONTIER, assert_eq!(SPEC::SPEC_ID, FRONTIER)); - spec_to_generic!(FRONTIER_THAWING, assert_eq!(SPEC::SPEC_ID, FRONTIER)); - spec_to_generic!(HOMESTEAD, assert_eq!(SPEC::SPEC_ID, HOMESTEAD)); - spec_to_generic!(DAO_FORK, assert_eq!(SPEC::SPEC_ID, HOMESTEAD)); - spec_to_generic!(TANGERINE, assert_eq!(SPEC::SPEC_ID, TANGERINE)); - spec_to_generic!(SPURIOUS_DRAGON, assert_eq!(SPEC::SPEC_ID, SPURIOUS_DRAGON)); - spec_to_generic!(BYZANTIUM, assert_eq!(SPEC::SPEC_ID, BYZANTIUM)); - spec_to_generic!(CONSTANTINOPLE, assert_eq!(SPEC::SPEC_ID, PETERSBURG)); - spec_to_generic!(PETERSBURG, assert_eq!(SPEC::SPEC_ID, PETERSBURG)); - spec_to_generic!(ISTANBUL, assert_eq!(SPEC::SPEC_ID, ISTANBUL)); - spec_to_generic!(MUIR_GLACIER, assert_eq!(SPEC::SPEC_ID, ISTANBUL)); - spec_to_generic!(BERLIN, assert_eq!(SPEC::SPEC_ID, BERLIN)); - spec_to_generic!(LONDON, assert_eq!(SPEC::SPEC_ID, LONDON)); - spec_to_generic!(ARROW_GLACIER, assert_eq!(SPEC::SPEC_ID, LONDON)); - spec_to_generic!(GRAY_GLACIER, assert_eq!(SPEC::SPEC_ID, LONDON)); - spec_to_generic!(MERGE, assert_eq!(SPEC::SPEC_ID, MERGE)); - spec_to_generic!(CANCUN, assert_eq!(SPEC::SPEC_ID, CANCUN)); - spec_to_generic!(PRAGUE, assert_eq!(SPEC::SPEC_ID, PRAGUE)); - spec_to_generic!(PRAGUE_EOF, assert_eq!(SPEC::SPEC_ID, PRAGUE_EOF)); - spec_to_generic!(LATEST, assert_eq!(SPEC::SPEC_ID, LATEST)); +impl core::fmt::Display for SpecId { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{}", <&'static str>::from(*self)) } } diff --git a/crates/state/src/lib.rs b/crates/state/src/lib.rs index 3b19076da1..e22d22fabd 100644 --- a/crates/state/src/lib.rs +++ b/crates/state/src/lib.rs @@ -40,7 +40,7 @@ impl Account { /// Check 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 { - if SpecId::enabled(spec, SpecId::SPURIOUS_DRAGON) { + if SpecId::is_enabled_in(spec, SpecId::SPURIOUS_DRAGON) { self.is_empty() } else { let loaded_not_existing = self.is_loaded_as_not_existing(); diff --git a/crates/statetest-types/src/transaction.rs b/crates/statetest-types/src/transaction.rs index 873f366fd2..241f12a26b 100644 --- a/crates/statetest-types/src/transaction.rs +++ b/crates/statetest-types/src/transaction.rs @@ -1,7 +1,7 @@ use revm::{ + context_interface::transaction::TransactionType, primitives::{Address, Bytes, B256, U256}, specification::eip2930::AccessList, - transaction::TransactionType, }; use serde::{Deserialize, Serialize}; diff --git a/crates/wiring/LICENSE b/crates/wiring/LICENSE deleted file mode 100644 index ad98ff22cc..0000000000 --- a/crates/wiring/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2021-2024 draganrakita - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/crates/wiring/src/default.rs b/crates/wiring/src/default.rs deleted file mode 100644 index 0669599380..0000000000 --- a/crates/wiring/src/default.rs +++ /dev/null @@ -1,311 +0,0 @@ -pub mod block; -pub mod tx; - -use transaction::{Eip4844Tx, TransactionType}; -pub use tx::TxEnv; - -use crate::block::blob::calc_blob_gasprice; -use crate::{Block, EvmWiring, Transaction}; -use core::fmt::Debug; -use core::hash::Hash; -use primitives::{TxKind, U256}; -use specification::constants::MAX_CODE_SIZE; -use std::boxed::Box; - -/// Subtype -pub type EnvWiring = - Env<::Block, ::Transaction>; - -#[derive(Clone, Debug, Default)] -/// EVM environment configuration. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct Env { - /// Configuration of the EVM itself. - pub cfg: CfgEnv, - /// Configuration of the block the transaction is in. - pub block: BlockT, - /// Configuration of the transaction that is being executed. - pub tx: TxT, -} - -impl Env { - /// Create boxed [Env]. - #[inline] - pub fn boxed(cfg: CfgEnv, block: BlockT, tx: TxT) -> Box { - Box::new(Self { cfg, block, tx }) - } - - pub fn effective_gas_price(&self) -> U256 { - let basefee = *self.block.basefee(); - self.tx.effective_gas_price(basefee) - } - - /// Calculates the [EIP-4844] `data_fee` of the transaction. - /// - /// Returns `None` if `Cancun` is not enabled. - /// - /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 - #[inline] - pub fn calc_data_fee(&self) -> Option { - if self.tx.tx_type().into() == TransactionType::Eip4844 { - let blob_gas = U256::from(self.tx.eip4844().total_blob_gas()); - let blob_gas_price = U256::from(self.block.blob_gasprice().unwrap_or_default()); - return Some(blob_gas_price.saturating_mul(blob_gas)); - } - None - } - - /// Calculates the maximum [EIP-4844] `data_fee` of the transaction. - /// - /// This is used for ensuring that the user has at least enough funds to pay the - /// `max_fee_per_blob_gas * total_blob_gas`, on top of regular gas costs. - /// - /// See EIP-4844: - /// - pub fn calc_max_data_fee(&self) -> Option { - if self.tx.tx_type().into() == TransactionType::Eip4844 { - let blob_gas = U256::from(self.tx.eip4844().total_blob_gas()); - let max_blob_fee = U256::from(self.tx.eip4844().max_fee_per_blob_gas()); - return Some(max_blob_fee.saturating_mul(blob_gas)); - } - None - } -} - -impl Env { - /// Resets environment to default values. - #[inline] - pub fn clear(&mut self) { - *self = Self::default(); - } -} - -/// EVM configuration. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[derive(Clone, Debug, Eq, PartialEq)] -#[non_exhaustive] -pub struct CfgEnv { - /// Chain ID of the EVM, it will be compared to the transaction's Chain ID. - /// Chain ID is introduced EIP-155 - pub chain_id: u64, - /// KZG Settings for point evaluation precompile. By default, this is loaded from the ethereum mainnet trusted setup. - #[cfg(any(feature = "c-kzg", feature = "kzg-rs"))] - #[cfg_attr(feature = "serde", serde(skip))] - pub kzg_settings: crate::kzg::EnvKzgSettings, - /// Bytecode that is created with CREATE/CREATE2 is by default analysed and jumptable is created. - /// This is very beneficial for testing and speeds up execution of that bytecode if called multiple times. - /// - /// Default: Analyse - pub perf_analyse_created_bytecodes: AnalysisKind, - /// If some it will effects EIP-170: Contract code size limit. Useful to increase this because of tests. - /// By default it is 0x6000 (~25kb). - pub limit_contract_code_size: Option, - /// Skips the nonce validation against the account's nonce. - pub disable_nonce_check: bool, - /// A hard memory limit in bytes beyond which [crate::result::OutOfGasError::Memory] cannot be resized. - /// - /// In cases where the gas limit may be extraordinarily high, it is recommended to set this to - /// a sane value to prevent memory allocation panics. Defaults to `2^32 - 1` bytes per - /// EIP-1985. - #[cfg(feature = "memory_limit")] - pub memory_limit: u64, - /// Skip balance checks if true. Adds transaction cost to balance to ensure execution doesn't fail. - #[cfg(feature = "optional_balance_check")] - pub disable_balance_check: bool, - /// There are use cases where it's allowed to provide a gas limit that's higher than a block's gas limit. To that - /// end, you can disable the block gas limit validation. - /// By default, it is set to `false`. - #[cfg(feature = "optional_block_gas_limit")] - pub disable_block_gas_limit: bool, - /// EIP-3607 rejects transactions from senders with deployed code. In development, it can be desirable to simulate - /// calls from contracts, which this setting allows. - /// By default, it is set to `false`. - #[cfg(feature = "optional_eip3607")] - pub disable_eip3607: bool, - /// Disables all gas refunds. This is useful when using chains that have gas refunds disabled e.g. Avalanche. - /// Reasoning behind removing gas refunds can be found in EIP-3298. - /// By default, it is set to `false`. - #[cfg(feature = "optional_gas_refund")] - pub disable_gas_refund: bool, - /// Disables base fee checks for EIP-1559 transactions. - /// This is useful for testing method calls with zero gas price. - /// By default, it is set to `false`. - #[cfg(feature = "optional_no_base_fee")] - pub disable_base_fee: bool, -} - -impl CfgEnv { - /// Returns max code size from [`Self::limit_contract_code_size`] if set - /// or default [`MAX_CODE_SIZE`] value. - pub fn max_code_size(&self) -> usize { - self.limit_contract_code_size.unwrap_or(MAX_CODE_SIZE) - } - - pub fn with_chain_id(mut self, chain_id: u64) -> Self { - self.chain_id = chain_id; - self - } - - #[cfg(feature = "optional_eip3607")] - pub fn is_eip3607_disabled(&self) -> bool { - self.disable_eip3607 - } - - #[cfg(not(feature = "optional_eip3607"))] - pub fn is_eip3607_disabled(&self) -> bool { - false - } - - #[cfg(feature = "optional_balance_check")] - pub fn is_balance_check_disabled(&self) -> bool { - self.disable_balance_check - } - - #[cfg(not(feature = "optional_balance_check"))] - pub fn is_balance_check_disabled(&self) -> bool { - false - } - - #[cfg(feature = "optional_gas_refund")] - pub fn is_gas_refund_disabled(&self) -> bool { - self.disable_gas_refund - } - - #[cfg(not(feature = "optional_gas_refund"))] - pub fn is_gas_refund_disabled(&self) -> bool { - false - } - - #[cfg(feature = "optional_no_base_fee")] - pub fn is_base_fee_check_disabled(&self) -> bool { - self.disable_base_fee - } - - #[cfg(not(feature = "optional_no_base_fee"))] - pub fn is_base_fee_check_disabled(&self) -> bool { - false - } - - #[cfg(feature = "optional_block_gas_limit")] - pub fn is_block_gas_limit_disabled(&self) -> bool { - self.disable_block_gas_limit - } - - #[cfg(not(feature = "optional_block_gas_limit"))] - pub fn is_block_gas_limit_disabled(&self) -> bool { - false - } - - pub const fn is_nonce_check_disabled(&self) -> bool { - self.disable_nonce_check - } -} - -impl Default for CfgEnv { - fn default() -> Self { - Self { - chain_id: 1, - perf_analyse_created_bytecodes: AnalysisKind::default(), - limit_contract_code_size: None, - disable_nonce_check: false, - #[cfg(any(feature = "c-kzg", feature = "kzg-rs"))] - kzg_settings: crate::kzg::EnvKzgSettings::Default, - #[cfg(feature = "memory_limit")] - memory_limit: (1 << 32) - 1, - #[cfg(feature = "optional_balance_check")] - disable_balance_check: false, - #[cfg(feature = "optional_block_gas_limit")] - disable_block_gas_limit: false, - #[cfg(feature = "optional_eip3607")] - disable_eip3607: false, - #[cfg(feature = "optional_gas_refund")] - disable_gas_refund: false, - #[cfg(feature = "optional_no_base_fee")] - disable_base_fee: false, - } - } -} - -/// Structure holding block blob excess gas and it calculates blob fee. -/// -/// Incorporated as part of the Cancun upgrade via [EIP-4844]. -/// -/// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct BlobExcessGasAndPrice { - /// The excess blob gas of the block. - pub excess_blob_gas: u64, - /// The calculated blob gas price based on the `excess_blob_gas`, See [calc_blob_gasprice] - pub blob_gasprice: u128, -} - -impl BlobExcessGasAndPrice { - /// Creates a new instance by calculating the blob gas price with [`calc_blob_gasprice`]. - pub fn new(excess_blob_gas: u64) -> Self { - let blob_gasprice = calc_blob_gasprice(excess_blob_gas); - Self { - excess_blob_gas, - blob_gasprice, - } - } -} - -/// Transaction destination -pub type TransactTo = TxKind; - -/// Create scheme. -#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum CreateScheme { - /// Legacy create scheme of `CREATE`. - Create, - /// Create scheme of `CREATE2`. - Create2 { - /// Salt. - salt: U256, - }, -} - -/// What bytecode analysis to perform. -#[derive(Clone, Default, Debug, Eq, PartialEq, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum AnalysisKind { - /// Do not perform bytecode analysis. - Raw, - /// Perform bytecode analysis. - #[default] - Analyse, -} - -#[cfg(test)] -mod tests { - // use super::*; - // use crate::default::block::BlockEnv; - // use specification::hardfork::{FrontierSpec, LatestSpec}; - - // #[test] - // fn test_validate_tx_chain_id() { - // let mut env = Env::::default(); - // env.tx.chain_id = Some(1); - // env.cfg.chain_id = 2; - // assert_eq!( - // env.validate_tx::(), - // Err(InvalidTransaction::InvalidChainId) - // ); - // } - - // #[test] - // fn test_validate_tx_access_list() { - // let mut env = Env::::default(); - // env.tx.access_list = vec![AccessListItem { - // address: Address::ZERO, - // storage_keys: vec![], - // }] - // .into(); - // assert_eq!( - // env.validate_tx::(), - // Err(InvalidTransaction::AccessListNotSupported) - // ); - // } -} diff --git a/crates/wiring/src/evm_wiring.rs b/crates/wiring/src/evm_wiring.rs deleted file mode 100644 index 6ef73d4ae9..0000000000 --- a/crates/wiring/src/evm_wiring.rs +++ /dev/null @@ -1,60 +0,0 @@ -use crate::{result::HaltReason, Block}; -use core::{fmt::Debug, hash::Hash}; -use database_interface::{Database, EmptyDB}; -use specification::hardfork::SpecId; -use transaction::Transaction; - -/// The type that enumerates the chain's hardforks. -pub trait HardforkTrait: Clone + Copy + Default + PartialEq + Eq + Into {} - -impl HardforkTrait for HardforkT where - HardforkT: Clone + Copy + Default + PartialEq + Eq + Into -{ -} - -pub trait HaltReasonTrait: Clone + Debug + PartialEq + Eq + From {} - -impl HaltReasonTrait for HaltReasonT where - HaltReasonT: Clone + Debug + PartialEq + Eq + From -{ -} - -pub trait EvmWiring: Sized { - /// External context type - type ExternalContext: Sized; - - /// Chain context type. - type ChainContext: Sized + Default + Debug; - - /// Database type. - type Database: Database; - - /// The type that contains all block information. - type Block: Block; - - /// The type that contains all transaction information. - type Transaction: Transaction; - - /// The type that enumerates the chain's hardforks. - type Hardfork: HardforkTrait; - - /// Halt reason type. - type HaltReason: HaltReasonTrait; -} - -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct EthereumWiring { - phantom: core::marker::PhantomData<(DB, EXT)>, -} - -impl EvmWiring for EthereumWiring { - type Database = DB; - type ExternalContext = EXT; - type ChainContext = (); - type Block = crate::default::block::BlockEnv; - type Transaction = crate::default::TxEnv; - type Hardfork = SpecId; - type HaltReason = crate::result::HaltReason; -} - -pub type DefaultEthereumWiring = EthereumWiring; diff --git a/crates/wiring/src/kzg.rs b/crates/wiring/src/kzg.rs deleted file mode 100644 index c058760033..0000000000 --- a/crates/wiring/src/kzg.rs +++ /dev/null @@ -1,47 +0,0 @@ -cfg_if::cfg_if! { - if #[cfg(feature = "c-kzg")] { - pub use c_kzg::KzgSettings; - } else if #[cfg(feature = "kzg-rs")] { - pub use kzg_rs::KzgSettings; - } -} - -/// KZG Settings that allow us to specify a custom trusted setup. -/// or use hardcoded default settings. -#[derive(Debug, Clone, Default, PartialEq, Eq)] -pub enum EnvKzgSettings { - /// Default mainnet trusted setup - #[default] - Default, - /// Custom trusted setup. - Custom(std::sync::Arc), -} - -impl EnvKzgSettings { - /// Return set KZG settings. - /// - /// In will initialize the default settings if it is not already loaded. - pub fn get(&self) -> &KzgSettings { - match self { - Self::Default => { - cfg_if::cfg_if! { - if #[cfg(feature = "c-kzg")] { - c_kzg::ethereum_kzg_settings() - } else if #[cfg(feature = "kzg-rs")] { - use once_cell::race::OnceBox; - use std::boxed::Box; - - static DEFAULT : OnceBox = OnceBox::new(); - &DEFAULT.get_or_init(|| { - Box::new(KzgSettings::load_trusted_setup_file() - .expect("failed to load default trusted setup")) - }) - } else { - unimplemented!() - } - } - } - Self::Custom(settings) => settings, - } - } -} diff --git a/crates/wiring/src/lib.rs b/crates/wiring/src/lib.rs deleted file mode 100644 index b6956334dc..0000000000 --- a/crates/wiring/src/lib.rs +++ /dev/null @@ -1,31 +0,0 @@ -//! Optimism-specific constants, types, and helpers. -#![cfg_attr(not(test), warn(unused_crate_dependencies))] -#![cfg_attr(not(feature = "std"), no_std)] - -#[cfg(not(feature = "std"))] -extern crate alloc as std; - -pub mod block; -pub mod default; -pub mod evm_wiring; -pub mod precompile; -pub mod result; - -pub use block::Block; -pub use evm_wiring::{DefaultEthereumWiring, EthereumWiring, EvmWiring, HaltReasonTrait}; -pub use transaction::{Transaction, TransactionType}; - -// KZG - -#[cfg(any(feature = "c-kzg", feature = "kzg-rs"))] -pub mod kzg; - -#[cfg(any(feature = "c-kzg", feature = "kzg-rs"))] -pub use kzg::{EnvKzgSettings, KzgSettings}; - -// silence kzg-rs lint as c-kzg will be used as default if both are enabled. - -#[cfg(all(feature = "c-kzg", feature = "kzg-rs"))] -use kzg_rs as _; -#[cfg(all(feature = "c-kzg", feature = "kzg-rs"))] -use once_cell as _; diff --git a/crates/wiring/src/precompile.rs b/crates/wiring/src/precompile.rs deleted file mode 100644 index 48f219344a..0000000000 --- a/crates/wiring/src/precompile.rs +++ /dev/null @@ -1,250 +0,0 @@ -use crate::default::CfgEnv; -use core::fmt; -use dyn_clone::DynClone; -use primitives::Bytes; -use std::{boxed::Box, string::String, sync::Arc}; - -/// A precompile operation result. -/// -/// Returns either `Ok((gas_used, return_bytes))` or `Err(error)`. -pub type PrecompileResult = Result; - -/// Precompile execution output -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct PrecompileOutput { - /// Gas used by the precompile. - pub gas_used: u64, - /// Output bytes. - pub bytes: Bytes, -} - -impl PrecompileOutput { - /// Returns new precompile output with the given gas used and output bytes. - pub fn new(gas_used: u64, bytes: Bytes) -> Self { - Self { gas_used, bytes } - } -} - -pub type StandardPrecompileFn = fn(&Bytes, u64) -> PrecompileResult; -pub type EnvPrecompileFn = fn(&Bytes, u64, env: &CfgEnv) -> PrecompileResult; - -/// Stateful precompile trait. It is used to create -/// a arc precompile Precompile::Stateful. -pub trait StatefulPrecompile: Sync + Send { - fn call(&self, bytes: &Bytes, gas_limit: u64, env: &CfgEnv) -> PrecompileResult; -} - -/// Mutable stateful precompile trait. It is used to create -/// a boxed precompile in Precompile::StatefulMut. -pub trait StatefulPrecompileMut: DynClone + Send + Sync { - fn call_mut(&mut self, bytes: &Bytes, gas_limit: u64, env: &CfgEnv) -> PrecompileResult; -} - -dyn_clone::clone_trait_object!(StatefulPrecompileMut); - -/// Arc over stateful precompile. -pub type StatefulPrecompileArc = Arc; - -/// Box over mutable stateful precompile -pub type StatefulPrecompileBox = Box; - -/// Precompile and its handlers. -#[derive(Clone)] -pub enum Precompile { - /// Standard simple precompile that takes input and gas limit. - Standard(StandardPrecompileFn), - /// Similar to Standard but takes reference to [`CfgEnv`]. - Env(EnvPrecompileFn), - /// Stateful precompile that is Arc over [`StatefulPrecompile`] trait. - /// It takes a reference to input, gas limit and [`CfgEnv`]. - Stateful(StatefulPrecompileArc), - /// Mutable stateful precompile that is Box over [`StatefulPrecompileMut`] trait. - /// It takes a reference to input, gas limit and [`CfgEnv`]. - StatefulMut(StatefulPrecompileBox), -} - -impl From for Precompile { - fn from(p: StandardPrecompileFn) -> Self { - Precompile::Standard(p) - } -} - -impl From for Precompile { - fn from(p: EnvPrecompileFn) -> Self { - Precompile::Env(p) - } -} - -impl From for Precompile { - fn from(p: StatefulPrecompileArc) -> Self { - Precompile::Stateful(p) - } -} - -impl From for Precompile { - fn from(p: StatefulPrecompileBox) -> Self { - Precompile::StatefulMut(p) - } -} - -impl fmt::Debug for Precompile { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Precompile::Standard(_) => f.write_str("Standard"), - Precompile::Env(_) => f.write_str("Env"), - Precompile::Stateful(_) => f.write_str("Stateful"), - Precompile::StatefulMut(_) => f.write_str("StatefulMut"), - } - } -} - -impl Precompile { - /// Create a new stateful precompile. - pub fn new_stateful(p: P) -> Self { - Self::Stateful(Arc::new(p)) - } - - /// Create a new mutable stateful precompile. - pub fn new_stateful_mut(p: P) -> Self { - Self::StatefulMut(Box::new(p)) - } - - /// Call the precompile with the given input and gas limit and return the result. - pub fn call(&mut self, bytes: &Bytes, gas_limit: u64, env: &CfgEnv) -> PrecompileResult { - match *self { - Precompile::Standard(p) => p(bytes, gas_limit), - Precompile::Env(p) => p(bytes, gas_limit, env), - Precompile::Stateful(ref p) => p.call(bytes, gas_limit, env), - Precompile::StatefulMut(ref mut p) => p.call_mut(bytes, gas_limit, env), - } - } - - /// Call the precompile with the given input and gas limit and return the result. - /// - /// Returns an error if the precompile is mutable. - pub fn call_ref(&self, bytes: &Bytes, gas_limit: u64, env: &CfgEnv) -> PrecompileResult { - match *self { - Precompile::Standard(p) => p(bytes, gas_limit), - Precompile::Env(p) => p(bytes, gas_limit, env), - Precompile::Stateful(ref p) => p.call(bytes, gas_limit, env), - Precompile::StatefulMut(_) => Err(PrecompileErrors::Fatal { - msg: "call_ref on mutable stateful precompile".into(), - }), - } - } -} - -/// Precompile errors. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub enum PrecompileErrors { - Error(PrecompileError), - Fatal { msg: String }, -} - -impl core::error::Error for PrecompileErrors {} - -impl fmt::Display for PrecompileErrors { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Error(e) => e.fmt(f), - Self::Fatal { msg } => f.write_str(msg), - } - } -} - -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub enum PrecompileError { - /// out of gas is the main error. Others are here just for completeness - OutOfGas, - // Blake2 errors - Blake2WrongLength, - Blake2WrongFinalIndicatorFlag, - // Modexp errors - ModexpExpOverflow, - ModexpBaseOverflow, - ModexpModOverflow, - // Bn128 errors - Bn128FieldPointNotAMember, - Bn128AffineGFailedToCreate, - Bn128PairLength, - // Blob errors - /// The input length is not exactly 192 bytes. - BlobInvalidInputLength, - /// The commitment does not match the versioned hash. - BlobMismatchedVersion, - /// The proof verification failed. - BlobVerifyKzgProofFailed, - /// Catch-all variant for other errors. - Other(String), -} - -impl PrecompileError { - /// Returns an other error with the given message. - pub fn other(err: impl Into) -> Self { - Self::Other(err.into()) - } - - /// Returns true if the error is out of gas. - pub fn is_oog(&self) -> bool { - matches!(self, Self::OutOfGas) - } -} - -impl From for PrecompileErrors { - fn from(err: PrecompileError) -> Self { - PrecompileErrors::Error(err) - } -} - -impl core::error::Error for PrecompileError {} - -impl fmt::Display for PrecompileError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let s = match self { - Self::OutOfGas => "out of gas", - Self::Blake2WrongLength => "wrong input length for blake2", - Self::Blake2WrongFinalIndicatorFlag => "wrong final indicator flag for blake2", - Self::ModexpExpOverflow => "modexp exp overflow", - Self::ModexpBaseOverflow => "modexp base overflow", - Self::ModexpModOverflow => "modexp mod overflow", - Self::Bn128FieldPointNotAMember => "field point not a member of bn128 curve", - Self::Bn128AffineGFailedToCreate => "failed to create affine g point for bn128 curve", - Self::Bn128PairLength => "bn128 invalid pair length", - Self::BlobInvalidInputLength => "invalid blob input length", - Self::BlobMismatchedVersion => "mismatched blob version", - Self::BlobVerifyKzgProofFailed => "verifying blob kzg proof failed", - Self::Other(s) => s, - }; - f.write_str(s) - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn stateful_precompile_mut() { - #[derive(Default, Clone)] - struct MyPrecompile {} - - impl StatefulPrecompileMut for MyPrecompile { - fn call_mut( - &mut self, - _bytes: &Bytes, - _gas_limit: u64, - _env: &CfgEnv, - ) -> PrecompileResult { - Err(PrecompileError::OutOfGas.into()) - } - } - - let mut p = Precompile::new_stateful_mut(MyPrecompile::default()); - match &mut p { - Precompile::StatefulMut(p) => { - let _ = p.call_mut(&Bytes::new(), 0, &CfgEnv::default()); - } - _ => panic!("not a state"), - } - } -} diff --git a/crates/wiring/transaction/CHANGELOG.md b/crates/wiring/transaction/CHANGELOG.md deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/crates/wiring/transaction/LICENSE b/crates/wiring/transaction/LICENSE deleted file mode 100644 index ad98ff22cc..0000000000 --- a/crates/wiring/transaction/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2021-2024 draganrakita - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/crates/wiring/transaction/src/lib.rs b/crates/wiring/transaction/src/lib.rs deleted file mode 100644 index f39217df77..0000000000 --- a/crates/wiring/transaction/src/lib.rs +++ /dev/null @@ -1,23 +0,0 @@ -//! Optimism-specific constants, types, and helpers. -#![cfg_attr(not(test), warn(unused_crate_dependencies))] -#![cfg_attr(not(feature = "std"), no_std)] - -mod access_list; -mod common; -pub mod eip1559; -pub mod eip2930; -pub mod eip4844; -pub mod eip7702; -pub mod legacy; -pub mod transaction; -pub mod transaction_type; - -pub use access_list::AccessListTrait; -pub use common::CommonTxFields; -pub use eip1559::{Eip1559CommonTxFields, Eip1559Tx}; -pub use eip2930::Eip2930Tx; -pub use eip4844::Eip4844Tx; -pub use eip7702::Eip7702Tx; -pub use legacy::LegacyTx; -pub use transaction::{Transaction, TransactionError}; -pub use transaction_type::TransactionType; diff --git a/documentation/src/crates/primitives.md b/documentation/src/crates/primitives.md index 49d35c9766..7e91b86cfd 100644 --- a/documentation/src/crates/primitives.md +++ b/documentation/src/crates/primitives.md @@ -15,7 +15,7 @@ It is set up to be compatible with environments that do not include Rust's stand - [specification](./primitives/specifications.md): This module defines types related to Ethereum specifications (also known as hard forks). - [state](./primitives/state.md): This module provides types and functions for managing Ethereum state, including accounts and storage. - [utilities](./primitives/utils.md): This module provides utility functions used in multiple places across the EVM implementation. -- [kzg](./primitives/kzg.md): This module provides types and functions related to KZG commitment, it is empolyed visibly in the Point Evaluation Precompile. +- [kzg](./primitives/kzg.md): This module provides types and functions related to KZG commitment, it is employed visibly in the Point Evaluation Precompile. ### External Crates: diff --git a/documentation/src/crates/revm/builder.md b/documentation/src/crates/revm/builder.md index c7ecefd211..cd9888bd40 100644 --- a/documentation/src/crates/revm/builder.md +++ b/documentation/src/crates/revm/builder.md @@ -127,7 +127,7 @@ impl ContextStatefulPrecompile, ()> for CustomPrecompile { _input: &Bytes, _gas_limit: u64, _context: &mut EvmContext, - _extctx: &mut (), + _extcontext: &mut (), ) -> PrecompileResult { Ok((10, Bytes::new())) } diff --git a/examples/block_traces/src/main.rs b/examples/block_traces/src/main.rs index a02ccfbc34..2aa4cc5780 100644 --- a/examples/block_traces/src/main.rs +++ b/examples/block_traces/src/main.rs @@ -5,19 +5,22 @@ use alloy_eips::{BlockId, BlockNumberOrTag}; use alloy_provider::{network::primitives::BlockTransactions, Provider, ProviderBuilder}; use database::{AlloyDB, CacheDB, StateBuilder}; use indicatif::ProgressBar; -use inspector::{inspector_handle_register, inspectors::TracerEip3155}; +use inspector::{inspectors::TracerEip3155, InspectorContext, InspectorEthFrame, InspectorMainEvm}; use revm::{ database_interface::WrapDatabaseAsync, + handler::{ + EthExecution, EthHandler, EthPostExecution, EthPreExecution, EthPrecompileProvider, + EthValidation, + }, primitives::{TxKind, U256}, - wiring::EthereumWiring, - Evm, + Context, EvmCommit, }; -use std::fs::OpenOptions; use std::io::BufWriter; use std::io::Write; use std::sync::Arc; use std::sync::Mutex; use std::time::Instant; +use std::{fs::OpenOptions, io::stdout}; struct FlushWriter { writer: Arc>>, @@ -70,27 +73,35 @@ async fn main() -> anyhow::Result<()> { let state_db = WrapDatabaseAsync::new(AlloyDB::new(client, prev_id)).unwrap(); let cache_db: CacheDB<_> = CacheDB::new(state_db); let mut state = StateBuilder::new_with_database(cache_db).build(); - let mut evm = Evm::>::builder() - .with_db(&mut state) - .with_external_context(TracerEip3155::new(Box::new(std::io::stdout()))) - .modify_block_env(|b| { - b.number = U256::from(block.header.number); - b.coinbase = block.header.miner; - b.timestamp = U256::from(block.header.timestamp); - - b.difficulty = block.header.difficulty; - b.gas_limit = U256::from(block.header.gas_limit); - b.basefee = block - .header - .base_fee_per_gas - .map(U256::from) - .unwrap_or_default(); - }) - .modify_cfg_env(|c| { - c.chain_id = chain_id; - }) - .append_handler_register(inspector_handle_register) - .build(); + let mut evm = InspectorMainEvm::new( + InspectorContext::new( + Context::builder() + .with_db(&mut state) + .modify_block_chained(|b| { + b.number = U256::from(block.header.number); + b.beneficiary = block.header.miner; + b.timestamp = U256::from(block.header.timestamp); + + b.difficulty = block.header.difficulty; + b.gas_limit = U256::from(block.header.gas_limit); + b.basefee = block + .header + .base_fee_per_gas + .map(U256::from) + .unwrap_or_default(); + }) + .modify_cfg_chained(|c| { + c.chain_id = chain_id; + }), + TracerEip3155::new(Box::new(stdout())), + ), + EthHandler::new( + EthValidation::new(), + EthPreExecution::new(), + EthExecution::<_, _, InspectorEthFrame<_, _, EthPrecompileProvider<_, _>>>::new(), + EthPostExecution::new(), + ), + ); let txs = block.transactions.len(); println!("Found {txs} transactions."); @@ -107,32 +118,29 @@ async fn main() -> anyhow::Result<()> { }; for tx in transactions { - evm = evm - .modify() - .modify_tx_env(|etx| { - etx.caller = tx.from; - etx.gas_limit = tx.gas; - etx.gas_price = U256::from( - tx.gas_price - .unwrap_or(tx.max_fee_per_gas.unwrap_or_default()), - ); - etx.value = tx.value; - etx.data = tx.input.0.into(); - etx.gas_priority_fee = tx.max_priority_fee_per_gas.map(U256::from); - etx.chain_id = Some(chain_id); - etx.nonce = tx.nonce; - if let Some(access_list) = tx.access_list { - etx.access_list = access_list; - } else { - etx.access_list = Default::default(); - } - - etx.transact_to = match tx.to { - Some(to_address) => TxKind::Call(to_address), - None => TxKind::Create, - }; - }) - .build(); + evm.context.inner.modify_tx(|etx| { + etx.caller = tx.from; + etx.gas_limit = tx.gas; + etx.gas_price = U256::from( + tx.gas_price + .unwrap_or(tx.max_fee_per_gas.unwrap_or_default()), + ); + etx.value = tx.value; + etx.data = tx.input.0.into(); + etx.gas_priority_fee = tx.max_priority_fee_per_gas.map(U256::from); + etx.chain_id = Some(chain_id); + etx.nonce = tx.nonce; + if let Some(access_list) = tx.access_list { + etx.access_list = access_list; + } else { + etx.access_list = Default::default(); + } + + etx.transact_to = match tx.to { + Some(to_address) => TxKind::Call(to_address), + None => TxKind::Create, + }; + }); // Construct the file writer to write the trace to let tx_number = tx.transaction_index.unwrap_or_default(); @@ -148,8 +156,11 @@ async fn main() -> anyhow::Result<()> { let writer = FlushWriter::new(Arc::clone(&inner)); // Inspect and commit the transaction to the EVM - evm.context.external.set_writer(Box::new(writer)); - if let Err(error) = evm.transact_commit() { + evm.context.inspector.set_writer(Box::new(writer)); + + let res = evm.exec_commit(); + + if let Err(error) = res { println!("Got error: {:?}", error); } diff --git a/examples/contract_deployment/src/main.rs b/examples/contract_deployment/src/main.rs index 5d46984da1..cee79d8f2c 100644 --- a/examples/contract_deployment/src/main.rs +++ b/examples/contract_deployment/src/main.rs @@ -2,15 +2,15 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] use anyhow::{anyhow, bail}; -use database::InMemoryDB; +use database::CacheDB; use revm::{ bytecode::opcode, + context::Context, + context_interface::result::{ExecutionResult, Output}, + database_interface::EmptyDB, + handler::EthHandler, primitives::{hex, Bytes, TxKind, U256}, - wiring::{ - result::{ExecutionResult, Output}, - EthereumWiring, - }, - Evm, + EvmCommit, MainEvm, }; /// Load number parameter and set to storage with slot 0 @@ -48,18 +48,18 @@ const RUNTIME_BYTECODE: &[u8] = &[opcode::PUSH0, opcode::SLOAD]; fn main() -> anyhow::Result<()> { let param = 0x42; let bytecode: Bytes = [INIT_CODE, RET, RUNTIME_BYTECODE, &[param]].concat().into(); - let mut evm: Evm<'_, EthereumWiring> = - Evm::>::builder() - .with_default_db() - .with_default_ext_ctx() - .modify_tx_env(|tx| { + let mut evm = MainEvm::new( + Context::builder() + .modify_tx_chained(|tx| { tx.transact_to = TxKind::Create; tx.data = bytecode.clone(); }) - .build(); + .with_db(CacheDB::::default()), + EthHandler::default(), + ); println!("bytecode: {}", hex::encode(bytecode)); - let ref_tx = evm.transact_commit()?; + let ref_tx = evm.exec_commit()?; let ExecutionResult::Success { output: Output::Create(_, Some(address)), .. @@ -69,14 +69,11 @@ fn main() -> anyhow::Result<()> { }; println!("Created contract at {address}"); - evm = evm - .modify() - .modify_tx_env(|tx| { - tx.transact_to = TxKind::Call(address); - tx.data = Default::default(); - tx.nonce += 1; - }) - .build(); + evm.context.modify_tx(|tx| { + tx.transact_to = TxKind::Call(address); + tx.data = Default::default(); + tx.nonce += 1; + }); let result = evm.transact()?; let Some(storage0) = result diff --git a/examples/custom_opcodes/src/lib.rs b/examples/custom_opcodes/src/lib.rs index f514d38c7c..3ec91d1fa2 100644 --- a/examples/custom_opcodes/src/lib.rs +++ b/examples/custom_opcodes/src/lib.rs @@ -238,15 +238,15 @@ pub fn make_custom_instruction_table< SPEC: CustomOpcodeSpec, >() -> InstructionTable { // custom opcode chain can reuse mainnet instructions - let mut table = make_instruction_table::(); + let mut table = make_instruction_table::(); table[0x0c] = custom_opcode_handler::; table } -fn custom_opcode_handler( - interpreter: &mut Interpreter, +fn custom_opcode_handler( + interpreter: &mut Interpreter, _host: &mut H, ) { // opcode has access to the chain-specific spec diff --git a/examples/database_components/Cargo.toml b/examples/database_components/Cargo.toml index 03139b79ab..5e1fe4bd61 100644 --- a/examples/database_components/Cargo.toml +++ b/examples/database_components/Cargo.toml @@ -26,4 +26,4 @@ all = "warn" revm.workspace = true # mics -auto_impl = "1.2" +auto_impl.workspace = true diff --git a/examples/database_components/src/lib.rs b/examples/database_components/src/lib.rs index 982882ddc3..16fc50aed8 100644 --- a/examples/database_components/src/lib.rs +++ b/examples/database_components/src/lib.rs @@ -1,4 +1,4 @@ -//! Optimism-specific constants, types, and helpers. +//! Database component example. #![cfg_attr(not(test), warn(unused_crate_dependencies))] //! Database that is split on State and BlockHash traits. @@ -9,7 +9,7 @@ pub use block_hash::{BlockHash, BlockHashRef}; pub use state::{State, StateRef}; use revm::{ - database_interface::{Database, DatabaseCommit, DatabaseRef}, + database_interface::{DBErrorMarker, Database, DatabaseCommit, DatabaseRef}, primitives::{Address, HashMap, B256, U256}, state::{Account, AccountInfo, Bytecode}, }; @@ -26,6 +26,8 @@ pub enum DatabaseComponentError { BlockHash(BHE), } +impl DBErrorMarker for DatabaseComponentError {} + impl Database for DatabaseComponents { type Error = DatabaseComponentError; diff --git a/examples/database_ref/Cargo.toml b/examples/database_ref/Cargo.toml deleted file mode 100644 index 508b00add8..0000000000 --- a/examples/database_ref/Cargo.toml +++ /dev/null @@ -1,30 +0,0 @@ -[package] -name = "example-database-ref" -version = "0.0.0" -publish = false -authors.workspace = true -edition.workspace = true -keywords.workspace = true -license.workspace = true -repository.workspace = true -readme.workspace = true - -[package.metadata.docs.rs] -all-features = true -rustdoc-args = ["--cfg", "docsrs"] - -[lints.rust] -unreachable_pub = "warn" -unused_must_use = "deny" -rust_2018_idioms = "deny" - -[lints.rustdoc] -all = "warn" - -[dependencies] -revm.workspace = true -database.workspace = true -inspector = { workspace = true, features = ["std", "serde-json"] } - -# mics -anyhow = "1.0.89" diff --git a/examples/database_ref/src/main.rs b/examples/database_ref/src/main.rs deleted file mode 100644 index e540da7e02..0000000000 --- a/examples/database_ref/src/main.rs +++ /dev/null @@ -1,91 +0,0 @@ -//! Optimism-specific constants, types, and helpers. -#![cfg_attr(not(test), warn(unused_crate_dependencies))] - -use core::error::Error; -use core::fmt::Debug; -use database::CacheDB; -use inspector::{ - inspector_handle_register, - inspectors::{NoOpInspector, TracerEip3155}, -}; -use revm::{ - database_interface::{EmptyDB, WrapDatabaseRef}, - handler::register::HandleRegister, - wiring::{ - result::{HaltReason, ResultAndState}, - EthereumWiring, - }, - DatabaseCommit, DatabaseRef, Evm, -}; - -trait DatabaseRefDebugError: DatabaseRef { - type DBError: std::fmt::Debug + Error + Send + Sync + 'static; -} - -impl DatabaseRefDebugError for DB -where - DB: DatabaseRef, - DBError: std::fmt::Debug + Error + Send + Sync + 'static, -{ - type DBError = DBError; -} - -fn run_transaction( - db: DB, - ext: EXT, - register_handles_fn: HandleRegister, EXT>>, -) -> anyhow::Result<(ResultAndState, DB)> { - let mut evm = Evm::>::builder() - .with_db(WrapDatabaseRef(db)) - .with_external_context(ext) - .append_handler_register(register_handles_fn) - .build(); - - let result = evm.transact()?; - Ok((result, evm.into_context().evm.inner.db.0)) -} - -fn run_transaction_and_commit_with_ext( - db: DB, - ext: EXT, - register_handles_fn: HandleRegister, EXT>>, -) -> anyhow::Result<()> { - // To circumvent borrow checker issues, we need to move the database into the - // transaction and return it after the transaction is done. - let (ResultAndState { state: changes, .. }, mut db) = - { run_transaction(db, ext, register_handles_fn)? }; - - db.commit(changes); - - Ok(()) -} - -fn run_transaction_and_commit(db: &mut CacheDB) -> anyhow::Result<()> { - let ResultAndState { state: changes, .. } = { - let rdb = &*db; - - let mut evm = Evm::>::builder() - .with_db(WrapDatabaseRef(rdb)) - .with_external_context(NoOpInspector) - .append_handler_register(inspector_handle_register) - .build(); - - evm.transact()? - }; - - // No compiler error because there is no lifetime parameter for the `HandleRegister` function - db.commit(changes); - - Ok(()) -} - -fn main() -> anyhow::Result<()> { - let mut cache_db = CacheDB::new(EmptyDB::default()); - - let mut tracer = TracerEip3155::new(Box::new(std::io::stdout())); - - run_transaction_and_commit_with_ext(&mut cache_db, &mut tracer, inspector_handle_register)?; - run_transaction_and_commit(&mut cache_db)?; - - Ok(()) -} diff --git a/examples/uniswap_get_reserves/src/main.rs b/examples/uniswap_get_reserves/src/main.rs index 04caa38cb6..733c8ec3e0 100644 --- a/examples/uniswap_get_reserves/src/main.rs +++ b/examples/uniswap_get_reserves/src/main.rs @@ -7,15 +7,15 @@ use alloy_sol_types::sol; use alloy_sol_types::SolCall; use database::{AlloyDB, CacheDB}; use revm::database_interface::WrapDatabaseAsync; +use revm::handler::EthHandler; +use revm::Context; +use revm::EvmExec; use revm::{ + context_interface::result::{ExecutionResult, Output}, database_interface::DatabaseRef, database_interface::EmptyDB, primitives::{address, TxKind, U256}, - wiring::{ - result::{ExecutionResult, Output}, - EthereumWiring, - }, - Evm, + MainEvm, }; #[tokio::main] @@ -71,24 +71,25 @@ async fn main() -> anyhow::Result<()> { .unwrap(); // initialise an empty (default) EVM - let mut evm = Evm::, ()>>::builder() - .with_db(cache_db) - .with_default_ext_ctx() - .modify_tx_env(|tx| { - // fill in missing bits of env struct - // change that to whatever caller you want to be - tx.caller = address!("0000000000000000000000000000000000000000"); - // account you want to transact with - tx.transact_to = TxKind::Call(pool_address); - // calldata formed via abigen - tx.data = encoded.into(); - // transaction value in wei - tx.value = U256::from(0); - }) - .build(); + let mut evm = MainEvm::new( + Context::builder() + .with_db(cache_db) + .modify_tx_chained(|tx| { + // fill in missing bits of env struct + // change that to whatever caller you want to be + tx.caller = address!("0000000000000000000000000000000000000000"); + // account you want to transact with + tx.transact_to = TxKind::Call(pool_address); + // calldata formed via abigen + tx.data = encoded.into(); + // transaction value in wei + tx.value = U256::from(0); + }), + EthHandler::default(), + ); // execute transaction without writing to the DB - let ref_tx = evm.transact().unwrap(); + let ref_tx = evm.exec().unwrap(); // select ExecutionResult struct let result = ref_tx.result; diff --git a/examples/uniswap_v2_usdc_swap/src/main.rs b/examples/uniswap_v2_usdc_swap/src/main.rs index ccf7c9695a..882fbe9b52 100644 --- a/examples/uniswap_v2_usdc_swap/src/main.rs +++ b/examples/uniswap_v2_usdc_swap/src/main.rs @@ -9,14 +9,12 @@ use anyhow::{anyhow, Result}; use database::{AlloyDB, CacheDB}; use reqwest::Client; use revm::{ + context_interface::result::{ExecutionResult, Output}, database_interface::WrapDatabaseAsync, + handler::EthHandler, primitives::{address, keccak256, Address, Bytes, TxKind, U256}, state::AccountInfo, - wiring::{ - result::{ExecutionResult, Output}, - EthereumWiring, - }, - Evm, + Context, EvmCommit, EvmExec, MainEvm, }; use std::ops::Div; @@ -99,19 +97,20 @@ fn balance_of(token: Address, address: Address, alloy_db: &mut AlloyCacheDB) -> let encoded = balanceOfCall { account: address }.abi_encode(); - let mut evm = Evm::>::builder() - .with_db(alloy_db) - .with_default_ext_ctx() - .modify_tx_env(|tx| { - // 0x1 because calling USDC proxy from zero address fails - tx.caller = address!("0000000000000000000000000000000000000001"); - tx.transact_to = TxKind::Call(token); - tx.data = encoded.into(); - tx.value = U256::from(0); - }) - .build(); - - let ref_tx = evm.transact().unwrap(); + let mut evm = MainEvm::new( + Context::builder() + .with_db(alloy_db) + .modify_tx_chained(|tx| { + // 0x1 because calling USDC proxy from zero address fails + tx.caller = address!("0000000000000000000000000000000000000001"); + tx.transact_to = TxKind::Call(token); + tx.data = encoded.into(); + tx.value = U256::from(0); + }), + EthHandler::default(), + ); + + let ref_tx = evm.exec().unwrap(); let result = ref_tx.result; let value = match result { @@ -145,16 +144,17 @@ async fn get_amount_out( } .abi_encode(); - let mut evm = Evm::>::builder() - .with_db(cache_db) - .with_default_ext_ctx() - .modify_tx_env(|tx| { - tx.caller = address!("0000000000000000000000000000000000000000"); - tx.transact_to = TxKind::Call(uniswap_v2_router); - tx.data = encoded.into(); - tx.value = U256::from(0); - }) - .build(); + let mut evm = MainEvm::new( + Context::builder() + .with_db(cache_db) + .modify_tx_chained(|tx| { + tx.caller = address!("0000000000000000000000000000000000000000"); + tx.transact_to = TxKind::Call(uniswap_v2_router); + tx.data = encoded.into(); + tx.value = U256::from(0); + }), + EthHandler::default(), + ); let ref_tx = evm.transact().unwrap(); let result = ref_tx.result; @@ -179,16 +179,17 @@ fn get_reserves(pair_address: Address, cache_db: &mut AlloyCacheDB) -> Result<(U let encoded = getReservesCall {}.abi_encode(); - let mut evm = Evm::>::builder() - .with_db(cache_db) - .with_default_ext_ctx() - .modify_tx_env(|tx| { - tx.caller = address!("0000000000000000000000000000000000000000"); - tx.transact_to = TxKind::Call(pair_address); - tx.data = encoded.into(); - tx.value = U256::from(0); - }) - .build(); + let mut evm = MainEvm::new( + Context::builder() + .with_db(cache_db) + .modify_tx_chained(|tx| { + tx.caller = address!("0000000000000000000000000000000000000000"); + tx.transact_to = TxKind::Call(pair_address); + tx.data = encoded.into(); + tx.value = U256::from(0); + }), + EthHandler::default(), + ); let ref_tx = evm.transact().unwrap(); let result = ref_tx.result; @@ -229,19 +230,20 @@ fn swap( } .abi_encode(); - let mut evm = Evm::>::builder() - .with_db(cache_db) - .with_default_ext_ctx() - .modify_tx_env(|tx| { - tx.caller = from; - tx.transact_to = TxKind::Call(pool_address); - tx.data = encoded.into(); - tx.value = U256::from(0); - tx.nonce = 1; - }) - .build(); - - let ref_tx = evm.transact_commit().unwrap(); + let mut evm = MainEvm::new( + Context::builder() + .with_db(cache_db) + .modify_tx_chained(|tx| { + tx.caller = from; + tx.transact_to = TxKind::Call(pool_address); + tx.data = encoded.into(); + tx.value = U256::from(0); + tx.nonce = 1; + }), + EthHandler::default(), + ); + + let ref_tx = evm.exec_commit().unwrap(); match ref_tx { ExecutionResult::Success { .. } => {} @@ -264,18 +266,19 @@ fn transfer( let encoded = transferCall { to, amount }.abi_encode(); - let mut evm = Evm::>::builder() - .with_db(cache_db) - .with_default_ext_ctx() - .modify_tx_env(|tx| { - tx.caller = from; - tx.transact_to = TxKind::Call(token); - tx.data = encoded.into(); - tx.value = U256::from(0); - }) - .build(); - - let ref_tx = evm.transact_commit().unwrap(); + let mut evm = MainEvm::new( + Context::builder() + .with_db(cache_db) + .modify_tx_chained(|tx| { + tx.caller = from; + tx.transact_to = TxKind::Call(token); + tx.data = encoded.into(); + tx.value = U256::from(0); + }), + EthHandler::default(), + ); + + let ref_tx = evm.exec_commit().unwrap(); let success: bool = match ref_tx { ExecutionResult::Success { output: Output::Call(value), diff --git a/graph.png b/graph.png new file mode 100644 index 0000000000000000000000000000000000000000..4456fe8b8bad10ac54d4af8204f014a3a87820bb GIT binary patch literal 334539 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 &=y58u31c{MdO>eQ%+bvE zCRuEyf)Y#q(!G&mfKY|tVfuEzhYsQzIGdtU6G-QnT?GZX!dY|Hv5#G-0yxg%TeCIP z=u2i0k&g%=ax}@oxsg?;i};~O+y5kciD*)gZn(0OPiAXQhBpC7Db$wdp%eVE3l;NN z((-GreaOo*bO=mK0?-7JxEt=>rrv;I-O<#A%D_88Bkqx1)~qAO4rHPkK*!c^RVuQb z7M9QuJ#vBXWN~}`?cI?lt!G6~ky$O?mNeieGPM-3T8DSvFtf`QsTVQI6@8-RmER`9 zbG_nYMWMR!??Lt_=#CZVTRRv%Mv^SC93EoXj~|*<<*`~iPv~{45;Aij{gp9EnV5u_4bIh#EAOI=9-nB!pnn-_fa+UIT|4PO?A_w6AWUalW`RR+F81bMAwL0Be zLs4CJy*ceoKXwvZD9?W~gwcQ{Oc2tEN#W9+dN#N3HW_v~!iAIt)>7(l| za^OeRI~m;_4LWz0rkFA84Uxiqb@WuCF~$KT9A_9?+gTtJ;*Bk^NZ}6|q*BR>$ve{c znaV2Nj?-a1+}GMQlJ_q#8Asvqhci-Z0mR!)k%f7BZBlnDvf9jV=mnhQwGs ze$fn>I5^SrQE+R@7SWqZ3T95w#FFukP%AdHA9;Y(L(j2K)5xhCRh%4u2KZyY zm>0-;ciKet{PG(I84EF4p##sjxdIIYfX`K0dZCUjO8K>^RGVXcv}zN+i)uUu`N_m` zLDki3Q>L~U;T>M;0dgn6aRR>Rt9ovxwRD9A2_00Aq-cC&qs9&fTlUkZ-}Px~O(^;6 zl-2hBPB1UI-Dmmw)?Uto6X|$PJ{4{5@J&snAd=Efz17wqPOy^OGCHn|AGbfM@A(Ze zW*DE)v5PuMY3)QUfEfZBQkN#5wTn)!3ipax#)w2Jd1ky#M~oPu{C2N=SBQ4E6AU)+ zU7^;%Xu5ZEg?yWS{jz~0+O!$Jm;YQ|Rk46bE;A#cHRFh?WS+$zo5FM9I*)Y8o_89? zTO^&!)_Zmyrx9#0daWltDK0LHaoSDpWIAq2A7LFf!agL3*}w7iIXLxU173-IAMuOG$|8bvC0yq`TamGS z|9)0M68Ld$Xe$y37<64qCp$UifAFFZkuf(qyDghU2gHpTJ7iNYs`f%&3XF5l9Y4Kp513~IyMw6K5CRlQ z2A42>6O(a8?hPEY;u=&8tc61^qZXbOFSqqP`{=$L^G(pO>=ruCGwINw1K3PG1~;e6 zSKz2BhUD;KME~65Sk&GONRKSovkll&buu>A4UwEglGL1uGXxRkK}XZ$IibZ9&z}nbu zZ~B}%mDmJ2(yKRZBDb3@Nf_|{$Pf^=4HSH}ufVOUujF^O1+m})G1%1Z@Eeek`CuTE z0{k_{PixYfVRR#GO6HndALJg0C4^0sfAp1821q?1RJkBZQMZ3-eTV;^L zV%X8w3MSnL$a-o>kV1^;YTn4o-qm`4NxcHLWYC#+Vq6CIZ}^TA=8nF5=bZbq{v_%; zwi6D0fP`Coh!JDEczy=TiKM#yS8akZJ`ZIqASSJJ90vSAtUyk;kz2~ttIM`4Ljg=m zGT(Ip!OH-{7Na~A$8EI+MMTlP)31VkXvUha;fQp8uuSXYHz+XB7@A8xOxY{cPJry@ zyiY0MfRj0gK_nzs?D&Wi)`)OYb(?XN!N}O{6DZ6iz6edcwb*u3mAr<$#pFh|uuzg^ z(hW3cf;z~zMhC~Mz&Ld5WA;7zF!R2E+_-p%O$F_1?m+WG%5|C-$zfRG-Rb$EmWo$c zvXn9{G&LI$#ej+Ou8gm!O|1Ge4#kcD_k5ApSH7k!c;X#PVItZXK&Xwd!=ZEO#ZKu7cZ*hW{?no@xmuA8 zE)||P^k6fb3@?c{m$D8{G;ZCY!~N7@7P(I-j!+7k^2~Ii(qP4Du`r1|n3SZDUr54r7$}F`6y0X{5oysD3jXmO?_jNK*;j zvVKk6(mUrq&aE786r3_@s9CeBxct<<|Dj;xND;GJXohHmvM$ z?AJAUZ@Cba@CGp57k>{W#gDJA!sxl(sP^d)HZe36IzBdlWqJ2vKIuH!^BU?`(z-Jg zo{fpQ+`nyDhI1f&LjpG4%ccO6-WOC5>P{hYb1Qn;UtZP`OU5-|B)t5=!-un$-sR3n z%oaok2Qg!ZUU!K}oXLy6wL=Q*KjP)qg}z&KnKWFDWO2OJ&{5S`_n1EQ3G-rkm1|AU z5Lug{KodXhU;%OLLEgfcN!%+xlw;s^OhjmX)mpXys_ISPb}u#R)M>c5pDgZJ-iucF z5vS!F_fs=$7eWBQS5_R)(cAaN7HbQ|P57t4)?2o5?Zd@vl4;xy!m)LBdz?(lfBbM? zdiQBYPEH60_M5{Kx{Nzu1EjCrX7rSSKA&x1a!wl*$^wU}39eRA7n&<8>nU!E6j6U8 zxbtRNp2Vm68}V9=54XF4Vk*`G`Wv@FO=zWf!|P#}PUoqcbV-TJi7d{HJZZFQosLx52cPB{C1_%MU>+OKK{zg|XJxLct|$NYY1 zGE&Sl_pV@mV%o3YnV&j;?L2z=Yimp8HaJ_dTyA2Br`o}Z@~9~{Yf_^kBb5WM>l4Y1 z2X?JZX5!>JYu3XLrR{pt2ti`Yc(wvw|BZ|czoj3aY!RC&Gm{Eu2EQXP!SgIg4k~1inqRc{XicrSSpEw!A`Za-yhY z32MmrRSWMZ^&$pl&mXfXKV|aA2wTTjzhsw- z7u>HJ1@I$HwC{a`)AlDeqfjE6 zWD7iv)lZkw*ds?C;PNOA3gXnjdNV#4&{gzvw;3>@7_e2VwsIItfh3ZT85jJjf_R-k zRi@C`MUn{~xV2h`h-CY3!~_eHpC5ZAK@=UoI{eNOW;c}{%x>ZOZc=Y@kE>#jR|S8e z$)9IQYg32d?Vh%$;ml9d-sCY#c=_@rn|0emKnUeMzlL~G{XSN8dF>GEWp_^c?%1`f zp<-S0&zz<+PyIxGhf)`1{{Lx4_L&*lcvcF zUo=7S!Mjm{PD|crW$-xv_g7AT=aqkU3XVz|d^|eZVNvS8S!;44s`7bRHaf>&to(m2 zz}T@3%a$b^1C0u~cYPBzsvD$Od-T=!#ItM*}&i<4|M#8n8hLcu8z zt&8Xz&FDGZU?!*t)}?VLA1lKJ7fQq7*qUqZE-q<~gSKS6UThGVGo@m+iS5iga5pvC zTn0zHu9$cFVHZ?C2!01%8C^~58a38eTj5Fv*#LkZ5Ji_qc`-*A5sgz%xhqWD@_ zMC|1i$U{oc9Fn0@?N_2AUSXoWlJ10oeocz8C{$2fJ*gB3s3w8``00f&lTc|*8EQ3b z-aO1n)nLr$DD2-MeSvZJ_1G#65OP(fw=#3$3Ip-xbCEu+>2)f>`TOycU)MO?e{|BT zG?9;@YA}8@XhAa&o$52ommE8O+?9JRe(}^k^N(68qY)*{JS|+%I09gZ`hcaS+WeyQ zSFaA2+A!t1L)_y0Il5VQQ`D%bF7?srV=!m!o{z2p=g+5o(RH6e9D&F4ar%lwz}rKV zN=Gs(Z3He;jrZW#{P8nqrsNu*{gW!+hh#7&!YqF3@1aON81+d!Xfv+MR47056Z~Q( zG9Z%uIhZQC;&Ekc5_$XP&9bk}PV8Su4JB;>OMHT}q|M~Uu=DYjUGMZNx@g72ZjQuv z6~KNY$2h^3YRmcZ#ggEB`6zXJ1E>$_Fe|;nonlBxXvVEVhRS92vCs9|SrcF7@5STf zZ!mgRqj{|PzH!@`AE%&`eH&7{8;lpyp{&DE3l(&na@hhQAM|iJAcSOZMjVaqFQ4(= zUnTeb5#B?V$A>it78-(ju|IyPzOFt(d(-j)z1tf~P^-pMMEzD_sYXU`rs zc5M993gG7(<{gV%Wu;Bw> z=$mN- zDQaD5dG@ly%=em~2O&+|f%uykGX8FRf0FOs+MjX9K7xO43ix>Ce%t*oo}du~N!Djq z6?d$@LTrGjob)#8L5T1jxsFDjMwdN5VZJrL8bj5%!F-ULrMeD_n*nV=oZem-8?&f` z0-59KgqfBM=A~bUA2i(FKH;UQ=vavtWzNp{TQb-=!s_wp?|)J~%(Jo9NIF!E(_hW% zGqr;oQ#q))pDbN{ET2HV;O=H#JBIgceU{B7{<1}7=GDzWJR%&UYw>tEpyf^$*UI+K z=M6XUfP{Y}gfub)HoLbLTgI4BN3o(N*hW)??Y&^yp-yN)mEEXp&b56+A~))YzCkNB ztP&RRzXanZ%l--@2f}gFrtr!w%;{J(?9p6Dq4d!wh)%c;;hUT>C95Dme?KV5IopDs z(=Xbs-*O`xFNV#`2Ufx#-|=IKx3@l1pO$vrZ^<6@jy-#huOkvzZn;Rq+6q2>(VI6T zD9HEs-Lz-VvQ8e066sRbAgBg8e zmW@@=a4}j@v;ar@n|blyWROS11-3jhR(r$Ksd$%z%8G2?KtT!F6+Xm`>(~9s@vVSp zzEK$!!BinyZ)O_!9Mck&X3e^AORInCLJh# z+&wU02jk?kx>1`ryBWphHnfg5eB+S!(Au^@@OaLi`q3qZU36JLt6buCDecmgE3J4* z$!=%eIcD-?q}R1fx%#sM{F6JU3<807y?uP#nHSBO%a?oua|urGX1)ru`&P+r${P)? z&Aec?{omgziUBwhfUcJ8wOecO7iId`w2~XIAj>E#wlnXeCT>cNrcMb}^zma=Zh(K5XhDVAWb^bg?cQF>EXWfm z3tA}*2&_jP{1R#O#9Y@p%fllfQXg2WD<))pO>Wnok$1!hI4ktIHjQ>C5a%xZWCLU4 z5Za!+6+>`l&-(gAHXh%HRl2sq*JEA>OJ!Cs8Zc&vOH_8ullC&2p*Xy#Z5aGZv*+}4 z(cNF}qrBd)FKHSw-~>~=&b-|C?}P0|f9o|VjzK|3r1?3kUa>n(A7;mJ-1f(DO`Hw{ zcpQS;-jZ7z-d!_(N`fDQ@IEId_C1vM@ZqV4U8f8m9+mZ-^yd8M zaNGRG3RoPfMnSErf4w{K@zW*)Q;9|~lIH#U%|Tq)DcRc2G6rWFGf}KAd+1|^p^+-i z^%i6brU;Io%m@Z2GB~@HFn^!pGgV zEZB!EUkP`SI?%~!gdrrnDOY@q(U7gDwrq5P<)8B^c<9q$F1BFHAOl|U15{h+i=8%R zcx77cj(t2HAFl@31W6bg($2kzfy6on%(4)ld$*R?HZhHV*w;uKnt&3*XW;tec?UoV zp}%d|XQxDxzVWquy#3A(LO@2TMCTgDNNDO*71*d^Lc`UEXW%J)QsGluYQq zR=^tghLACibXe2w7PL{Gq-XY#8=E;bL=b^rn|2t2$7REi{PDE+_u^c5kC|MW&G6iw zT;99LO{~AgmNmpG+{)hDJK^R%HhwmNDd>kcY?S8h9^Je9A?g!v1xQh?K*FdD`dVXg zIKnmD3nK&Z2cuF^OWDJwSu^$mh?$-Ku_(hBeQ5|~01e8mIMT;R(y-NB$ITa6Ya}~d zfD7unk*zB6&@HF{ST56u2X$f9g~zaN7cM-#ZNiquv?;JE*0)fC+&}QEq29(q@=wP4 zdi6IRuv@dNNMb@L7ladISk;;d=O=2#wqi`#^QU>ZYaB45#I{%p^LkUZpuFo+` z?!|fnDKORUJ&g^%7&nd5wF#l}Kgx$d{pZf9QxV55zanOtpi=lNWW)8u|I8?Qqk~)d zia~NRncKR&MKN$S9dM=B+Nz~eJcPWwk0T8kl*4#v#Pl0chPL7ezVz!$UFxu9r!S4@ zdoWtVhz7g?d6Tfr=-r;6Hj=sF|MP73w(@|^0|!I+57GiHTNrif)bq%v?YH0Mq{F2^ zxHc<3SOEmw21eI1ldO?zb2!XsQ7SYaKoDXE4#9O8gB4lSxo_Xp_sI-usM^m@NpHas zEJC9SAB(<`s;lMxNQ!K62_a&5K9v(*i^B-)Q}LS`K*RUOUO>A zy_VDR$&W(#QAhK6w{cyCZ0W?=;2HH4YHH_CX1Vb1W|bRQOu_w_`wj{I;+a)%o$>Xc zG6T!)ajex)Y8J(?xs&a3++AHWoOeFX$~t~8zyv82<@WXb84=sFFfb)Jz;t&@^mPUT zRhHYkLvxgtndRmiYxf>Cv=v1^z=y~(l7@^|PA51EGXu_}xMneqZ1MM~m;^HO1LkUu zL9Gefkk&HjqW33R+f$6Et!iIt*DejIJWVJ*RDQluc9HeN*jc z#BDPm$%9j8F{-WO88>iw>zIRy+)*MuZq z7_2vh)=>A(>rJ^-C1`;(sf=Bo0WwU!zre#oNipL>WP9R~tcehpa{fR`AqWZ-5{CU9 ziEuP4k6jz)xgGUW_<0%IZv_VdqEPke9Pz8wixU#e}~WlJ9;3e=J~24&jUJ)F1T zt7QR2f*LZmd_{$7KR66$sZ_A)Y9!PQV)d^M+fE%Xp|FTo-96D^3E*=PsxjHniv|AT z!nArdOT^fK!o#2_B>AKa^T^>~FZ+{HNx!I30$x@{B^bOl=^eD)07zo}<;w$8`}NUp zz$=qp_Q1h|0%hadl+bRwM{nl!Lh~_9ZO+d?eki>4nh+=BW08@5d>lFED{87iiMicN zIc|hZ4Vro}Xn{%Aj=95J^WD#O?EHCyR~Z0vp}&f?5Lf1 zLf-J1>Ep^eOrAKg{+>I>GJ~uMXp+?+@3YWw0~k(%u5FJBezwFCnaJB4s$hEra&7_v zPyuDg8kw5n2tb5@&wW;ECu0kY$w}nB%$!ed9v(sbo?uTSz={EvUAkLs*+$bQ{@!KZ zzKNeX)dc0V!0-J1a5r_WFg%i(;y+8() zwWh!z0}~@vxUcor%pYx65Sile2k>H+hsUVk);ovEbt4^PMH|Q}1nxbo!CAnQC}`7| zdGg~QzWFm_`gE}%0`6?0H{;F&DbJi_Ei9X>8gb~;kPK6{1T!qfw`_OmJdOH-)-o^x zX;PhC4XzMH0-!Z)7T&RGto8#KM zyo*X^(8mg=&U4(YP;O90T232guaRjOZs<5~!2&Ublo-JYsJCY71z2Kjd*XEfATC7z zl;2;>L3TLXpXshJzU|zLYDo;Q(2H*r8kebQIH7Ycga^tHOMsLeKi^W4z-6E(Zn!OO z4nrYfzWFo_id0Oe1fn{T0W^g0vvlY}HK1wq92uqkh;$7DwaIM93`QS|t1G?X<5++} zl$Y0fkryudlIcd`9omk~PVG-&A~zKnCA zJEbt_ZT1pOug+Q|E6UKjf}7RlYcZo{T-T}6#K(wQPzm*tEvZO08d1mgTK3T)D{Cwj z44|E`2I%Ze%@)pp^&$SgvW<;jgkEQBT9~cB z!cMz2ww4J%Hd?a81YJ;py|$z|&L>KWVBsIxwg?xE^B1#ojHLjdWi2}ST7p{(%WdpU zB>)Kk%=lbpNxOSGhvI*ER(HH7=c6MfJi}M`&2Hw+Iv?dDN$7|7kwK;amhH;4{aI*d zQ(-fSX&!-$;%sbwp168R+Wl(vk^v$7t4w zNHvGPFzbW`gpZN9p+W4r?qiT3gsU}pWC9)5FMAvLn{X~{)Kxz{Tk&Z8uD^cZXty&> zY(zm)g`~ypE&Temxur5k$d3w9%qfJ%&3KYWLSOP9_51AlM}5QiW7;_FHlL^rMQxc> zEjt1^0%F`)$UH0V{rtO!`CD5FbEr;Mu&?IR${XePAy>AmzBFK6b$^)`!iq|#K_FwD zx6^-3-^VS!VKX|Pe<*JLoKl!Oo9*TfMsg&|SZVj&Qb!`FbmlzMs7?R$RfXgr{^K~W z2>VyH}?LvwgU(5g8pSdZcF13(Q-?}!I$1hq97RV zEuE*HPGtu>Lww32&L1X%W9Jb_Q>|<`^*hI2Kt_{u#=|(7U|89l8`)J>Cm%UwPk{w` zy_w+#t0!C2!2w?wQj`DBu+0=Do=lQa&{IO(ytekZw!52KeZ|(eo~_}*K!(;sh065X zxc!+^;%P|@?fK6|`w!X2;jwk@{5Dq8t`^!&AiyD`$myF;8TcEV->~Y(=Y2Qs3OF)? z3E2G6V<@z`ZaOwKNv17emgH`B_1?m zqe%LJk1|Li_)P>?R;~~g^*uv3S*|8F1t_(!d5N<9y=vu3{3csN8c?^pd_4Bc{JXP} zA*GTuT^QZ)(Zz%j*xS{7!VU;+GJHaAAi9fdxBTmEY{8nzRx%+4Mw1x_0|ePuifY+2 zJ8C1#dsR_2Q14j>XR*CJn-QtFv#qN);u)Zm5Z#~)7IW3&W<&COL(r+vG~yNGf@H0_ zIR_$@s^#Wqnmv^DDcvsp_jsa9w{{CsR~E)M@?Qw&^%O#I>T#mwTEJYwb=602-j=Y^ zgaZp>grGmwrt3`M#NC{+R3mqr0AX~)@CMSwK#dT_WBK=Et}0L7&HT`_2u92-lR8TY z*QGQMmrOhXLs_PSclF6ck0I z+w%=E2UODE4$6p6R71sqfy!#W+tJ1~==y7>@K>29lnQ7 zV0q`?_*t0V^8e=oG><%c)^1qIe(Uk$n=3wy8NP}2y>+86R>cE=rj1&y*`(aCJf>w+WUv0RP}h_KQ8U1}Fw|=rBqK zS9&ico4mgO%LRiF&OU%yaP1*KRVr2wwr;k1`=mgy6v0Y;-_mS8CjvAIS~~q z>(ng&Us6ae)C)IuWY%VZe*#9(W##2ZKPf+XO$cU&2y#8=V8OqvV-t~H3?j{kg=i0Gs~&YW@gFMxTWmC=8bNGcyTQvrJ-!yr5#Mq&OTxLn-8W& z58AQM6=2r-ghw!-BN*7az}wSi(Ft_nqljI746Jbw5b>x;lxCcXQGpZv;6H2Hj90HZ zaK(#K&xj(NI_TQ#sMhCLqJ*f3iD}*&ny0ni4ASJ*kc)U^ONqMyTehe`brPgQJ$9AZ zq402f|AMMtzx2_pBZkph(@O;-pUT?LFIG;!`>A$lP4@Xcn$Lr`I}E(tGQelF(}P3j zx?XJ^(Bs(WqXy4A7TDT$+@cx2!{A_$zk2s+b57h$8BuWeYjfv6p<(fJ+)wW-x%_J3 zAMbxx&B%-D)c5*-6oP{R%Oo!ApKU62AW( zM38mAr3QzV5X6T?xoM;Nt-6kj zes5AOTPQ2P-yfQ4KK=Jdq}*cpB!p6E|L-gZ*34!|iGXvmSo!jVhW>La3j7M~m3%;G z!y~A$?hoq!zAGkhi+y}HUAWMXF~~n}enTsPKa$nweIQ8$U2i+;cv12&E65O(`rlp~ zUV(4OCMfoBK+3TUc6z3KxvFZF6#S4ZsD%Z0qjo0b{>oW~eky~y6H|mShTW{x{nOKk zXucl@hd4ERb210-JxrR@C zc~zb0wi#JuknBFx-+H(gF1oym`M-68idX&oqK4KFG_bntJ6+TWFmz!Q(;SGPizq#P z+^t$EV8CiHm^gOpr(ikad4#7%;~jl6D5P{RLZc4lOkZ{Ycytd=1*5 z8^ng_;mxJONc1O%bn%hBr>?{x zrN`F0A`M?&f1DXaEGee)1Q6)`lA27Uc2t|K#$~nJJEjYbQhr>&htw~_spp8u|9eh; z>_1rBYNU4n+h70~*Hx`-F0Y)N1#iX-@!M#okJ9X+ZK2>YsX-J7{|ak3m0E!}G>)Uo;HV{U^3i#V&! zsMFG3=`95g!j1*lY|yR2HW|qaq?hf#cU-Ts@ArbJ;zaL2T!@Z3KT2}Q;UQz|T5tP7 z&T%W~Z;O4eT)85mSRszD-L|#1R%_x8Uz1w)-kt4zMM2d@$~*8UeyPsUHf{gzTobXjMo#C3V$hVu;smrlKUZM$BwF6>L( zynTp+*%ICUKTnM`=I*v5i@Hl%XLBkH7QiPSnEi|yA<{C&#`z*R8q_ zJ}urP%==}C8$7?;%s!AUaoH#qSo-ZUsC-Fy;0D(%u^A!OSd4c7abNXNv|Ku-ODmED;7A6Ahm0P88~!TM z*#M>`uBl&!9g_w0US6fnho*VcE)xTVAM|_xF zdhK8444*>?`$U<=hWiTT&@;hvPTp|&@3&1TY4!B=Ee4K?m75EVO;$3B->yopLne+) zk(AQmvzzEbNYP@*^$ZEe@D83?NDl7Tf)&Ng!MQl=+a zlxuNsnWMYMAK%Z6nD$GC@DNy~K0JPWlYh5{5{Kad6tWb7BGjy;Jx_{}x%yC-$$W|2 z+}u|hUr!xg%~EyiP@OIOUVtXL)4fX#TvJjOeq_TY=eqKn+PWnN2d~zo^V1%>ZV(M7 zW%VWwRqgq=k9}#vWVJei=JQ&88WxT=I(fMCLkeGC!UBU@S;u_mmeX9~5YUnwOIx7b z4G#`nPpa)n&j~nH$ZPQ&*LelF`LAz-tH?8zDGfCg2L`QF=aEFCEbm;n;_?(K=bj|3 z$n)p_jTpLT`*zF!EEHZHZyv;(UYJ{NEHM+6m@NJf43Fe-&Q#a89jsT61q(2a9X+r~ zZXgqlU-z=g=iuErquVE71{E>J6cf#94sttb{nFoWuRINgE>EEG|F0rc<`UB!$>v^YA_Gcy zjkk|?AVTmnk}gh>ikvXg7@XI2N$+$r9V?6`tst2#NFsuU92mtx-vWPv?7sQAj z2lSh-mk!NCJxJK{oqY8~$}(0^V3-39Qp{E{GwQ0lWiGd%kQoNG<#k#x+w57fG(_Tg zOScb2B$Mi78+&AU^odi8#S@9R1vflrVX=jZ2^WC~wT<8!xFx_Dk9 zwhmfRrXbu%b?aUTRH#+nOtMaqir@6<@*HY4Zn!rzjU z{B7++Jf>b|Kc>}#+xxrZxdwSge5Zjo#R={0?Ai2iD#R|gJN3q5_AF=Lo1D|bIpu&_ zNdtc*B#b1dr@Qp(HUE(Tj&Cif#d1=d$x{rbm76pX)SgrMVX9{85$ZlF!0XxB?=Lit z@J%A$GD|EbAHQ3CxdX8NaA;KCtgP#Z!4wr28|B3@5f6bnhx?I51E_bEn>7=kZNLVb z?DQrzU{m(lNv7k04X zaSa(vc9k}GoD|Aisri{&i=KG>SI6!XHG1B zEZOzK_fLfuWbQy>Z1UDyUxE0U4vlD@bnGI&oj_-;F}fNP{6&jOlkL_Fh9midt*0XlI)s^8A zFm|c0khYUC1XC-!*ya7d%bcv3l954fKk`E6g_QQEU$ljoA)FuCo_a;M8iOur)z>3E z<#KFXx>IvCS~YUuJXK6?FZ%TiYMr{e&tMJ^lsz?w@j`r_(EA)+C4<>e5QRjqyRbW@ zC57)vW#C$HvD$;HOR_@y(x}AHq1_RLi}@eWhREZpx$rcRwf z->7=Uqg+yPQoS|yb+9S#v-Zv>Yyr+9zFL7Z4JGIFQ6tAOGNzt;KJ%b695V+6*qU@qGc~72zU^NCIw0u*qClBx%9Cu+=h%SzWiwXpokHRbcU@U-tmA}5;n9A|c zvxP^G$DBHqj@q*!Mw1O1HInrRKo%IgtcN=vn|%m(01#m@K{V@pyg>+AL?qV8^_c{? zq7{tpl&}l4GVcC|*C`sgN|Z4foL^ke3LV38VfM-u;SqY;blF?!HP;IDI+``SCCC3) z%}~AjnKYrTHL8r9Qm=4N{E-wVNOjT)qODM__SndZjpU^DRXrQ|Z(vyv*CZY6n6}pa zw@&p9{6i^5U3%{$_8d>^_ewTX&~xF6sHi~GULq8~%Oc5z6+WnfWQz4?8q zTTE=Mw4po7C# zvfyLoN;bf#rOwc^wtsRVC3&-3-tCnfIg+Tziapzme^6F%F9_a+*xpGgwNvwVWqBuj z%tqHW9(@PZ-kZtek#E5L0fdIVuM_eb*#Z?rd0IzjcIkD5!tv*-c`kl zY)VtjFNQJu_zq>_aLw)UY_o`uB^%v;e0gzacX z0JG+29zT4zfwSS&(8@am*hCn6`}W;>>P^64sp5J-+DH^DsG2r$?%=;X8`ylPdh6C! zsJP`SEnoig)y$!A*hJ_`o_zhW-ewU$XlX5a{NC|NULI`zHZmt+bR}!mxW$KJV~u1+ z$(LbY(BXNz8Tml{9L8aQ;U~f1o-=8#HFSTW!tH zp)jPs-$-paPU>@krkPMCU)JqxmDCxT1VD&u!3uO-5<+C-B<*v8*K`YbLz$B^gTOJ< z{`u#?(yflT2y8+lSKu6MkK(B5eaXJ@NA_RFHHjJtDtUdDdDHY+7dFwPQ09z;#Ci^} z0-HtDD*6~R&u3{5FRf_ppT3^TcJPtCMQx$T%DO>PhfW)3P$cF(G6jd|Ag1J;j!0g9 zr|;#eJY}6BYqY7nuQRe)x6&9W1XsYCKR=zR zr!c!OV(Kf*5yJW6+M2bZ+FjZslv$z$rAGElf4iN4UZ54OF*JUP0Vyaq_c*-nbY^IMy|;fFRz`6Q- z@_w%89#p#O&T3qgMH>$vezjm7BFXEPvzwg1wd*Cf)WTkL9R(4CQ5*R!YY5$4%^OwHfY>v)2!^hl$|w<_@dLEL``#W(BS=x<`fC4aj%BK^k6a+ zu(b3u`pzS4F0iK9g@=cy?;5ZYskfSR)i8#nO_AXoH%p4Fk?+G%vy=GHv2BsKS&p3!*YAVb^q>Wt^w$9P?2H3XJ5>CA8T4G#sOj(+^Z{b& zKX`P~73&<1$m^xuRIosv0ciZMxBJ@DMF}%e_)d&QPrqr0%D(-OHJj-`7S9>-2o$q1 z%vYfZ)B6b=ZaY$a-v3fJTd87Q$ibthc#&sWS&Qm-jF$KfpGPq=`WIc9n5iBAOgOjtWPb20*Z zONKVWmk8Z@|4ijEHHWopWp&=KnyW$5sT36KV6&w|yX#t+O^9@g%F^z6hq;KuQ6tjd zy3C({>*xFC+*9_Sgv2gcw|0F$Q6IM-+?H7J=3{mAfd2gjLCu0n1O}3?4EjL|UcBu0 z1!!E56#{wv^y;#fLk#kkH1=YAREWi@3(fW4e^bnIH3hwyEXW{CylQj9s{<~M`s)}{^0 z%Ja(v(HHcFn%At$o6}tNj*HG^vRxSGJZ5yW5Mn9ST9>AROxaDF7EH+6fo*n6zKL)h zDK{>q8Yj?=2h1C)FXz4~FKDMb+C3^kCHxapVF;m?cb zw8*>8e-jd{(C#oHIbA*NSmk)uM3HpF`x;2l(#fgn$^ziDGQapT5; z&Zqlp8ZeUG03I(jQZ6!Wv1*m4^;5q09zRYjf8WhNU2nI4(O8gPb{$^_ z*hQc%~YM2I^7XSnvwnR~km(mU$mt8gDO5;_Wylx$n} zw8@f#3IG#owo={Q{i9d43rGLig+(36!M-b>ouqgJ+m+&yWE2>+kC(1@*4&6vSeZ8{ zLS0kC$cqvG&jm0IFAg-GIJ7H@B)*2jdNoyE?ORppcP$Dqtp?c7vy0?qTSV7WKC(+Y-}n^n@k}7)2%P=)@FdPKUoq)S@6{T zo^9mGci>qnu_Rt zNWyHC@))k!bEb&H#HnG#67pb2wz(rg94YWU?L zpPJZ+4wF{#cZvTDpExBNhjf0j;26+7U4W7$6mW~32AkP}KBCZ;9z!;U0b~#{-oc+x z<6_E_ICJLf^20q14cF84!$8_XJ0;#|e3WcXPGMnTTJCqlb;nt_L{E{{Ht)*o;mw7I4lkD_On5U0O-8=c{-t5mx?m>& zrfIL2z7c*&K|wp-{nAPOto~$^l2Awm9UUI{_ZkzPHdpZ>Q?C1Yu9+Y|%3Xh50?wfF z+SK*X^YacQA67M#6zK;=ZZ3>)&BXUgMdA5kM!huy&dYWLLBF|-3GsZQvU#fyhA5V`;JW^{u)wDQu~h$Lm{PHW+PhUyIP6So_ZJ`^$8 z_X%u1eBl2P^&aqC?|=Wll#F(vsiBgR#_60AA|kXkL@J{_v?wYgDDI0OW!x}2Sf=_!2*iI~pKhd8&=#Ikw%!ZG4!i_cF6C?~$>ki3k?gHyTsV(n9JMDU zHGQ#vO1BP^_Q@h3_+yb7k{%a+^AR{2z=5fS;kzO|nd@#37J~A-k5tMcTj;K%bMx;Q zaaNtg_%_1jPSf9(&U3o|N9m&&4SEoH5Ts)8{8Y|_e1Zn)){U>@Q>F3e)yf)~g6y<2 zXDsH=*I|ygB~i}Uw|3Vj_mj<1H0YM~A_*xG;$*@_O)6cn9UzNb+Qe65c$Om~ga}Cn zZ*Kp6_{0evt#_TB9L?Tf!U}>aMFq_aAOD-6FC>@<>BSRsMXYi4|b@;Bj9v^TpBvU)lx@amN-8ieAriaJeQg8=>wokLvLo@)alJBMU zCL|**zz`EZn%!_1vFo&kG$FmTB94=wR{Z}TLWa{`)D+$e(S%D&M0KvhvaPgb!#+mcu2WcK zLOCQT3vW?>vC#t5;h@&9($2M0=YCrOx#1Bto))?y2puj#df$@D!5x~jtxBxjj9-3_ z?(B8hekCTQ!sz$$+&6R@VS}mKnx9MXSk0`Ml^XEUZGRmI*lbBzJZ2Q4Uf;UUo2_r> zJ!@rhe7rwIRw-vLspranh=(%jdV1hi-K$~{BH=~pPolvsGOTB+pC4{NfqV|GSgiM?U%(YKvm#8=+=MZyRbcR9)GwzwZ| zfpSN(D$FT;gLvuuF=Plfs}mb=j}EUi^vID0>t>Hc`9uVcMD55F&Z%d5&cl!JJ?I6D z9YB|iuNd*cNo*8tFP~1F^*~f56m)JSn_GsZqjD326+V@=)|;K+>)enEnorq}PLU=9 zT{5l4rHeJty>ynVE2Ec}SV;>B`D(DP$6|UL-2*nLLWP z9G_W^9HMQzc2xKq(M7czQ|HbC0`UmsAA%~_7`DtBCG7|}BsWl&@bSU*yB@>AQTDnZ z$?4qgTN*kT(d+9Qt`ALUlEbM4=@g^Z6{`!mTFzJm8CQ7+sB<7QVQPkUo}U)dR;ga{AX zUuj+Yt|^NbF1$h$C2}e8WTJ}QNQ)#M5=h*Ha$+DMv&;2cw^XP=ZXzpLS^d>zK=1m? zprW#$fZcvz+)1UAjFVs;m|&IBy}G(z%lTNf zTJW)kEiK!K=Nb2cV#VF(Gek%!aGdewJ9|2No&7J?U=ETxO~Vuwus`Fb%rUTNS01^O zhMwqT=zU;yR(cO0<xD%%6YL)33hvC?kAql$BOn z_Qe7jxKNOaB?sU_@$1(Qla&XuVp*)yL|P0V?>=a4KCDb5fpr-Oe7|^Pd1o|&h&E|z zhOIzR0j+X!-b|ih?9z^RY;Zw<)RF~_qLy(5g*soTSgr?y2d^Z1%x=|Q3TPj$A;cYL zzWDunYr`?&D0<|YFxf1shDSFmrllspC`!&|=!8E4%;0tO)Ov^f2xGmIjO z(G8;OfBCY{Ueo^DrcmUzW@`c}P7*Q&ZW}f&%VH@?Ca{(WpqMjWwG34_)3?e*2?ze^ zu1jkPHSn|I8`l#pf_=-dAK+`w5U@fZb3gVUIFOZ=I)2H0KF5kdRO^?4l+h!}Ekx1OJ@4b*rW;eSPzxJ#0Shn+&^@ zmoFZmqrOyF6A)_@C*mP1_F1S+rP>q>n*ro_0KBEU$I@7#d$K@n#Iqik^!{HGCfMjG zAP!PR-J2g;kJ1M19w$eGKGK5J$4`7}SO=FUiUi?7j+cHjL{2B|3^kyf)h8k_BNsz= z_1+-^r5I%_%|HGp0xhr48$JPQ8yFc?u5NZ1tDk?ryoyW9OVSN9$k4Mx&Zcc@+O5B4 z__0a~A1&xVu{nhO{)#>A%GJI_cW#@_UQ$_MgdSYl8fi~Db|kqtp2x0$=pencRElQ= zbzxulmuB<%AZ&%W32-Bm#gvX$h@8A(D&m^Ymm23GY8njp_X5fU#G13-_-ko`C50y# zlJoreO`8T*yfh4WSw^RO1}qH0M7ip^rUN{k!Uk-w8uK)- z@fT9zpoy~XMh8bqc0kw_H=2l?Kf@;A1;~;m2fvF}-=z@2#j@{~Q zwT7r4zzgw5Cqp- z@6o?V2ST}_Vr*wRF+_`JEo~z8q?9iF5Fs`JD}|+{cHbIVtwqojb{8!@GP^}DY$}|b znyJ&P1YWrCbC^=Irh&O7ZncD=5Exhh%pSdZHJLZ>%?`gFOQ+HiA;R%XZww%=4%tF) z4DURWN`_`siV)qt^&+@Io6nf>^Xe~n*tXzU-=Kss)qHHsB`Mm5QyT ze8O#sOk7rJvSbSC?ApGC@0dX26ay-$gUol?oBiBH);IY~9#sb=Z0t&_Mmf*X*tp;)Y677!L~CJu;v%1IG%A zX`qEshH8!-J9jQcfG4k1)SO%pAjrmCB-#2==JkqvURMwWr`S)vP@YBJyS_wZ+pKhKqO2^~@Jx45s7nmEc%>>< z=c%z-ycMhk@Pf+h#f_(NY$)|y7<%^3Kk>^7uuGgyBrplvrdy{c>rvvF%D)k&;S|AW zIg(VFCX$rE@MYxvoD?Gv1j|^mGXY%IYrOe+zURI_aM}Si{OY6EHOw^U&K-2)CsTWG z-w*R5vmUe+8^Eqh!L3B5Pf5*iX6^N%qf9GNXh)_mmF-gU=XfC~9A!6$xw(_?-2ski z&1LQah8xC=PN0zjvw9$Ti*vmpPQXo;@7sF@mP!szgx^4lVrh28knw*=`wH{AsjM#r zUt__8SX7e2Njur3TF3x@kOs!(H4!@`CAu>EzKazRG9gU!7-4=%0bq>X|*0(IyT7yd-VhoaS>Hk0z_g!ROUR@`UttlSo&q4IZH6ZHinarG3?r~?iF3|r-HFiK^KTf zlEEC*;fMI`*3cCWYqX!D}sH*XZp5IG-y?pVm|BKrZ#Jlrq%nC3KVo{$pMT?4^>B!3hXnnU6uVjy_$5Czyx-MnZ;j zMLfpbnhKApgKVs7M$>SQH8b-+aP;Wy3;3TqX8SU4+e|@k$dKscrQ5{ha|iPVh`wT` zIXKuDPB_Nd**tH~oKc2NA8kB7^ZYjMM;3vUAbO13Xb*w!647P&wk+pC_Pmq+RD_S| zzj&t)J{8f7+ z1K>`%mX(*EV)`uW_Wk>Pv{D|Sf_D&OmAjaK>X!l?ou;dFAn?xPdu`F5GdaKo#Jhjf zz^iIl`TyZZ(v)95`;wR0T;Dv_<~q=sC(ZeK7NS8jEwqoM{TP|+hVS<%51Q*81kUiBxoz(XtRgBA2gbW}_9UJ~L~c)S$PZL9`?>bzW0kxw zeH4=U*?EHv%2aAbY}OwHnMx99ZJJ(E&t}M*D3(}!*a!}jU$-I4HFe%cNH3Oq#ArAH zto0Z%LW|0Uht#0Du|8hHgbNK(3G^q0ncGA9P0>qG8s^!a8w38#_u(F0q2dx)S$q^I zt5_3dMajW}i2A#?KcnYm{@t8~aa_7zpSqAPP=Ng#SQOGdxkEC|GT@aAwu{CD3j$KR z=<7M7k~5fz6JZ;^As#R6;LFVTowBDExZG~byRx!SV&MP4zvHc}Rf9xk2!bklA{^g0 z%>@wz+!)D)i>k4ai7Egg7?m*~1}n!g2PK?N0~? zFf4MQ?gfvs0B&P36pPI4@lS2677u2lGB@6pry|dGJNOyDSSBK;L)*&?7B z7{=k0lx*}2A}ytQ^Ko}WQG#AT=yDLZHe<%AUSLK|B$;S5(PTHK+uMlpLEPU#tbhPy zW&sI70LA;Z$UAG`#bvILNKkOpZvpBoF)*M6bNg%5U(ZoE37H1Q0e!a3K9cTTxc~97 zj*S#}N>c1ox?7V`BW)&5e3!E(Q=DX0mNuoqquiU0|CFKv(D>N?*Xc84J{ci&>pNFk za>}-D-MRtJLbyWPi0V`JjlSONQDAz_d5}!oBjgfa89*l5Me}1fSzXn;L-UPSM*Jhc zlPoUAQGlxZGcsa5k~Ad}L4cz53@6Uztvoz*3hK2k`uK5JCC+nC*&(x{dylLw!flb&MPe@QzFNvSkhrDU_W+|4E)<ILMv_)C}e^$FKNQzT34APa1)ijSsbh0!Fpxg+ohDQwh~Dbe;#*H8=kQ=S65 zSqbPhwP%LD^sS89ZE$njVc^1x7gUgOR%!krp1>J8(=ST)SHN5v);kQZ6JPlqT zr<)Qb9IUGyfREdnX+~L{DK2HZ5scHe$ZPHBYJc2Oz6OmW)4iZjG&8G|be8>}79g|0 z-^?~M58WaXSb_?Y&@Ms-lUbpUF)C37U|YaI(8Mm)W3EACQ1)+O>wzSfPkH(IEH8q0 zZ=7eFfjDO~YCKscos_uZZ-7&URYrQG1X&i&WU6-M={x*%1iGs5hyNv~tLY8Pdg)s( zSRVWL+!3f@s>E#WPf=;9tSAJF!gG(efv4QY#;R*&nnSE7xLt4*TRiq@mOVeKC%y#$ zaNpdn2ABAeC1iL9REi44(y*m12px##C~BS4maVM^EU@6wA=h2H@*m^#=8_um7?Qe(yJ)yT+vUn^2zK>R;w&W8KJ~) zOSauJE7?QqP%bY0(n(R!(TxOwCINQJm|<(TJLb0ggqVeejh*!;_*ghLWYVd4sGq2r zk=*_4X;-=&vA3aqvzhC=MyzK5t-S-!1K^5_8Q)J4s=F_OR7~#3>aNrd%Q8Ll+ z-Y~}gn{xP(rBJ?ZMnO$iOTZCrY3g{YKc-r}7$u)hGcIWR_ev#S_MUQN?#dCoUr zr~ysy{GtIt6yS}-FKUE5;6e0U=Bf23K z0+Y33avevdKzfeoy0(YsufsVCJX#EfUYuLmm}gCeP7z%)@e3;`1To{^vJg$Ix9L(F zQAGqJhZEal4hUF_GZ$PeCb%N!tEZ`I5(~r&j8evcoqfaNF1o*KiE<1vc_@-AuG>~s zn~Lq=O>+Q!M33A+VBDLyd)KZaIN}DKK=3Y7O#-AN7;7(jsHfVLcOnEMrj+Ss&P6m1 zfFK4mTz5ojZ`O4_j6hL19?rr>p7r#1^V2E+64KH%WqMYiIiHX(;Tn{1-p@LdF~nnu zftV};qVKOksrvszp=7}~c|w{M*ecnRCWGy-(oQ8b2@yYsWP5+&dNElH--93?A`*&> z3Nr86icx@K-1dlWK$N|;b}rsXZa`%v6GRO8M1&kb_$&(~7hU>HF~EZm{gbpwWI$YS zm`rI!`;9MYHVhY5bpfbxuHY<2yz5mpPc;cFgQO#)D~xeafbJ1!sKw#n7VTxk0Oiz~0(r^MZE~eN3 z@z>T0UP8z?SJwp*T1&Y+$quK_84m0tMlGOFqxeRGNQC`t>C|2iN$l0Rf7E;*FM0sc z@2;(U+b~z;;em(#qgE+CKR#05o$bj{AK4PuY6v= z3+v@*+(lsR(Iw3FA{{AMnIi8{5(hTwnHLWteqz&=kkNRFp$T|WzvZ$~t7F4|*DKc< zANaffqPa1>dhAIE{yt*+6W8Z8^H`sJM?vCX`~r`^ACsb>}miM%N(2&x!`TE*i5 z3vK(`DJ&mw2I9H{^*>mRXraoiU#2nhLDQ$^F*u@aOI8${F_9y$7mgSqa(Ot;e)2^D zMzTQT3)k-{c?CiK^;7yQ87?J}dT1)fNr}aWBWbWM(YnCRRy^sXff1uN4w1}m_Vj5B z$Uub@Ub~?T??QUo_%mm=lUaW*rA|6YHQdN`1N{fuG3(20RpyQwF`5#q_^ZIjW$%vMm{7nepE%GTt-DL@TlaU$kCg*89?*S_()I} zBr=r4H-CmcgPP;L4Utl*fb!ux*T6OYS`gJoL!3 zH$H5(X;MBn_T-YKOM_7D3iM0?|Gzm&=~ZK@dL;m}x8RjC7gtKAs(!n7xwX~L{>u*uqhoEG&R^*@dBekJv?y-ia&hqv8~+5;m2J7gO)wF{s}Lhm+Hzy3@h7$&=E|0 zT&klP9!zGwnv)aSP?`-;^-eXPGNm0zTiXkEMu4Bh*}j%>g*6^($U_zM{XkjZ5Oe8n9MHMu^_N zozL)?3d9pSB$0?9(rpStfYj}B{!m+B4*){1>aRWG|D76wxssSRl7P9?62cXn@9fYQ z+a`2_05B?tlDue1mdMN@PY|kmuV{gPGWcR^ey!yn9jIIPDBJpU94ScJMm5qIVZueLk^&Nhj-ywP~ zKx~#(EPi)iQVU`Pif)%R#w@f%+d-!nshb1f7-O`zFx=04< zt7ES~2@D&R7W`>$R_2kzhh?3CI8jo;v;3>~AotGa%xJ?wWjmO78;Cvy)R~OFyKZNtzRy%&`}@8k$Y)3z{?V zSa@C#6+kapo+WcCiGlfjukJv%OuXvQv7@kKAm2J#p*_(=iZGfeMfQ9Fj3u)rU52j; z;x3e@fh{o`pxmGb@jfLEBL6Y#6qX;nY?Eob6qqUH(4M={GKJHUNaP(d|DW!H-IoUj zCD=3ZEr#X~A3p|G_zO7((3~YY4B;E8+!2|DEqztPvneILh;a{Tv_Xd%W%3@Y$g9kh z3%kgfQ@gU)G@3LgiWgH+9sCHsZ!*JAJwpEUTsKg34@2b6L2a`TKikBC-w9df9MXE#TXla7prP zO~QG3WMJ#;q6>Un)!1RW=P_BDSJ&ysjM@asR8% zDsFnZ3JcxbEQ#deK7}F_lCYFDTA+OwZhg=CM5=l>!5Cd8FOPKIn^TU-M;F`}jLVMO z*mjdvgj=#6Ktt%aWx<{WV4?d{Ua8SB>@F{WcCgX+Co^ZF^}WEXaP z-DJ#@@@nQq@&#yJD8d7YUs5t5`ic6e%z93d70HeVi~&+Y4lR3rM)mmdWD6$a1iasYVs_+=0LI#wC)TVpeO14la+&8WnQ+WS# z)256vnGDcEo3!$`$@Uff^!3H_5j6vy99?GcMBmNuyRxx!7Zwh(Z83PedW!Trd?W+E zI%9Ag+$F>@4ERTiOKRmFTEPQpQ=0PVD1m8V5=`*DRU%a@zWgqeNA%O?PVc|$a=#Jb zYDulfAZFjGd(U&?2H`Gcm>H3v>)(B9me{57BRA7ndO4*gT!&p^YiY~XtE=u!VSwgR z(Mt1E@Jj*MHiGcLrm1l@l6qclADt6~1o=VpvV@@PG%`4Bu|=~Lc*wTMo)4&d#88w8 zkYc78pp_Ra82FN0DDG=hhg;Fqcc)hB~7dXeq{RJI~$FFO!UszWmiq1TGa6 zMxwXjP9X*@^L};g#0gnnO|e{Qlj2VT$YL5xmgm73HS#+B>ozuu*IfURK4h14ARtuuqw7ts54_Ob~9z7{R-mncsy3=Rx3ZPH>z?0MiAB% zI4&wKt`%oBFY4R`&@9HGSP?950MA-yqAO6v|9b=UsHkvyYP|{HbnS)OvY2G&)6ZbQ zMf*=3`%iARPVn9NJsCyf3`?)Cd(<)LpuH%vzJFiCxOOCOV&U@TA-n9K)L#_o01X8Y ziD%0Re7UzS=HEa&n^OL;bC*%gRmc6_?8Fc0Dy_M=n5nmKAI%Yrp|5(4hYa{x_U_7JcUh}M zsi?34M!!4LK`>7|Hkl_Od&_h+ca|roh9gc8s2c+s;^xE1G}cAR2!CJR{>U$*w{0+r z0Hl0;Vpg-FyE{8k9x5!)Xuig~$4o3Y>EszWZb^=j5C+fD?b7{_Sfk$F{6HawDWmBW zeJhl@@iN7Vh!zO^V>JC{$hOIs5Q|HNi*Td|;xLM1IXA$OY80v zeaF7Jk6*ml45H${tkI*Z9BB$RlyhXjCFiCk|IcpjBx_Uk5BW)RkWb?Vi#2c z$5xbETH9(nBCshcD|=S+g&KxWumQ{u_^E>K%2k~GA3nT6aX$&uFR&43#z2@5R#xB_ z9IC7%TQK9h5p@%>L6&1FY@l_7aT6>W>_com0i?{RWKp1t8yx*P1)1NV)Xn_F?((O>ls!}Jn2U_HU8Ib~AL{i4OrzCktZlIcO$$+%~6F1si}+3t*xWL7k;xo+C9TlA%enXGel85Sx!q zVd)r(n#~v&dd}`}$YgwW)-!|$WEWJvPZLGkN+<$Zhh zJa??@%CCK8c;Y2^uEO#OZP)PPnrujaeiYeNQw5WC=A(fX1u8gos(k6YGn8bl3j6S5 zDBzS8fP>mB9oiY#>EpNWs4=VGyB`V~h29cO7i+1Fln^p{m$=2$;0Rc%-K^g^NNb%*lLOmEsiIE<;E5$xl5&2_G zV~2Ka+YVxY?OvSc8O7QkowW25>yDQuIA@VoOxu>+r!MP)(-zDTB^5U*hJbam&>boF8yYg@2uAYjAK*yW0ZbSQU4YC+id2 zTt#Ih$cArig;k_+*G!qOHnZxk9TQ834=#bZu=gqvV?^~ zJ783Q8AL%6F8@!e>?(3N71Z;CUf;eh(0I##|AEc(oH;$wWwheFi?Ie@1Ck=kNa<)~ zcpqZmQ+CLbD(DJd)>Qt8Nq&EBqGy~@Z!dvuC}3omTMk8b-6dZvhZzD>9z;_Q?o|pT zUz~H{VTh%b)%7b^6aYvp0eF$TP+-a?0d!^>Vc`$KxYzIJ1c%33n1Pw-uLtz?3FCo1u@+X?clRCOs-!!G%L@?g})S)iy6BuVcEm@(^ zyYGIZU4~?z%F7t(Z(0AXDLsD645&vv~9}^8$Q#`E(`UU83v_e(qQ5 zn#`)xkxw?kXc?vtVx+ z<=C0DG+tX{;_GOjQE;M_wEUi*DX;QBy_=vKNgr?pD@Iuk0XL7Y#hv^rkQQc;H!!{| zV}9Q=`#@2&Kta<$)W3KkG&c-QUTXE|>=#m#=h;?oq;SC)=6zY1Uz>xjm@tKp)YKz2 z7B0Trw>gdAAQHli8f2;YLIv_||M2DPjWHK$|WRLmR?%7Z{mmftCv_@4CqGFwI}K;Rl8hoCf+ z5A*?YoOs66S!cnt?@du1r*qo9_fD&rZJ!)Q?dpV(3L2(0o}#V21SY?G;2D-4YvQ8@SXBGHV7X}ciCn`?>}NeVa_pGsFFkrbs%KM} zSR@jCz{i|gA9M}w9-E9OO*UO!@Z#B)%G9k~q^*%x|JtW(C)6bexL zAF9cDcXD!4V`5^SVYR2DbtoSH)k9~+L5Zn+k%@lqmq8Z7cJY#(6q#u3~)D_rFE z%-Q1L=y(TSWd5+f{#w0uFGc#Pf`WqKY4fLZ7oO2feWY(&vvOs{dZ&7N-QN=)qrl0m z`dLHczNIka?&m`HFJV-O>j_+O-lk!v!7!iEAuZ)wf2T^r!S(dBrE92w9LJ5@tkt1| zwPBbw#dhW3P9=r=mmY zm^xkj)S-5Z$rxlOP%15f)gDEw3w7(Iy_j zXdy~Dc&9*0tv){UIyP?#K!JWonMxib=npXkfc2N6tB$2)tXuNKgGUV}w155mm=Y&4 zJaqd80wW3rLr34Jx9{I?B93>IUYfvXlifxYxz;WyEq*yy!6IahZ<83uiUFcPS+`Cb zX!P#gyU^uNR-OuooAJnZfSIvF6r~@oFZb5$yf!y4$p&g;6y;i(?#zkR-0!-DvN$O-8MmzLhx@x_JRR;!`3!j!B%a zH1D z{m1{#HFroxGMUW_N2fExi4^8OEOwi| zQ&X={07dBskiT6ko?0t@wgdsisvlAGRE_<7OKZ(x?EoUI$k1iFii;qpBwV8Z6j7;E zp~4RIj~p^+&`97WBxDri%`sNue*~n3mc^Tk~XsF+om9 z0pbGr0e-kw-@c)Qi@>YSU+=EJa1v*iAd)3lj)i2Lk5+k6P1I=SxzcX7zq(Q-TCi67 z!EEbK16=g%JdC@UiB}Z;rs_-QH-19yiqk@NQP_QbyO|8y0;$S$1LM#4gXhkjhSCT; zK(PA;abujNZny?P3&|22%CN^(91Gshw5Pyt><4Me9i<%^^?u!>&K)`wksl4%>FsnT z$aN3^c#%S^@!D%Y+Hcg&eZ$41uViVm8UEp3p2sylhH;U6+1#Os~L8 z?C{vUY%SwTqT(wm8i}TJ{GLBm!8Fc!e(74K+vUgbEVZF~BbbC4NmqcCM$v>p*F@aJ zt+{sV)}{sq?Y{PQSzmPOlwx?6w$`Yk@$4ldEs56$IOaxjs>-ZzZE$mfmRN3cyu7fG z&ow4Cjuy_CZl6}Q?Ok)*Sq=amY0F-Q!SZ(R*DnlVLxMps4j3W`OtY_ZduUWf8|L-t zXtbt32(b+9xxn_&gd|fY;|qI&pTGN1vk8;R>&b9)t2qEbIC%Z{rT&I* z2s>5Fuho=q9+njb(XINXrQ+;piZw=wpa>;OP($%iMjc*fP9GNtAVAT}g%(d6Rvb{> ztoZO)Kfyuws2*DeZoub>SKE!7H$_PBu)QFFh*W9BFm;F&nGR-eNppNO!LDn+-5DtO zaYOge#z4%jK~2T43YebV%G)A)eU&j2-UR^42teY-(h-Qqi@K}f0A0GmCrWKFU%9H=mUNv5T0 zD?dL!5k&$0fj;!0*FSv!D{l)^9I@1){IOWL@bHyZb8Fe-UQ}G%2s1>%!y|jPW?Q!^ zLT>cer3K3~v-oe~jJe^CN$+<#vikfB^{?35LG7Af($edDZPlrS%a^riMHE+_N}w&J zN`P$w_vpi84E{EEx>ahz#pC;Zf0p$T{|1usyZM~C#6(nUgP?lEy;HsFSrCd}lz>2w zhUo##*at3tMDi6l55bI#p&kZVQ6UUu^tGopE(Nd_MudT!)Gq2(JwQaE{=|$906i)u zX1rlrOWU~pq)l*mgkfFi&Qp;|=S3OlFtAIr%zS#AGR+|`SF#OE=AEf8;1_9$f>99=YSy->2vDPpGINV17;@+__X(~<|bE5 zAgrg7PV}=+J$Kc9e5KTkraD2X84*R87@^w`6Ks03|8OgpK^?@L3;>T!c6~hD-E_y; zGa(AY+=d!khHUBFt*uIQsbFMm6c{sztWDPA9~wc@$qojgWl}PJs!?Dl4w*FTf#jC> zcr#tkVSYz|Y*N4 zzAQE2{P~XfO30EfAc(bXA~xY0^#5QKC6Ti^ZX0j+KJ}^ONwNBvWQu!@q)hJZ)8fW zOmx>Xai+0Dm^PY>!B_3mKPwFXP6k1uH8}Ouqi1fN*`QeHy+FC}pCPSvUh3OHwrL3R z%iiOGu5H?BX+@ybYVf%d73)ClhYe1aw2Z7_nF%}F^nV2$Nt!xEChHq!e|~A>7zkBt z&XEAa3)SrJ`@3p7%W0_1EOjTOOuA-6^2X9jrZj0WtY@#-f9}wQ3wtu^R$dX5Y{7yM zSq20=!n}eE#Zx%TqG)6|0F^Fuo)o0n^H1ZJA*x-0EUA;3S1h~aJZ7ab$)1a+Z|8B9 zZ5v?2)xUqdBDxQE>1*e4$*OXFsy43fH1u+ycrc03k!FESRp+I`xH5B)ai@T-8cNJE zn=>3qbXmJAGM%%I21|T(o+~o4>EDNE!AaubvnPFvIO*~##l#il7+SY=>(<>LTwW^% z3UKpqEA2+OAb<%)wQ7tdBOOno(Wt%|B0xZ4!%q=u^1YM$4J0PXoGPt^ERzD|dtOp8 zgb*pv7DTaNEJxN_^YG>R$i_S%M~E9dtvrDRRnZD0gLT3^#278>`x!u2ihz-RL*MG~ zuShImFE)4NL8#o$5r+b};5o1Zz%=5?kBB?u$&P zG!nwT;Tgnt=NS}@*E`II{BIyQ3g&1d*z}zL9~)@0E~JXQBE1l43D-Uk-`4NWqW=(< z0W!v8fo|3lUMk}FK!^j`W(M${)YDdR0%1qG6nxmiP_y!kN#XL+k8zDm?H( z?D4`)jCt246c~9?Udza@)sh-MebV0+{18Y})O=oOcnE4NZ=D#juyPlK8r9*XnKLmJ zaax623pF(`F-h-8WTOHibx^eT?#M6JwZ^9<96(rUKTNDp)wdWDC|1lVtX; z@Vl2MP^1s@F1lba<)O7ov~M($K@dAXsiR=e`4#oB!NP`Gtj%oWF)Tg*=+o zW`wnJQJRS6sT=qY6rm35RK;@%i%S_5%c!qgFE107z?P92QQXF^;MC{{8PgFf$0(Cm ze3oqVJi=)~-ME6r7~dah`%-Emq~wyv(6)&73QLVL`?!yUUGwwp#&4}%KG2mMMfWHh zOGU6l&6c{ZYB9zXAXRg~gecfFYP!}U}DQk2!HE)QLrT zspoW{J7qHvi3V+o-nKrAt0VX`vL6dF2!YH_k$+L|a4B8+4I+~!Rw$@=`@f|_H=~WO z{&{&Ayb6WDe+wv#hp|ld&!IzQm9=?QEp8WuH5aeXKb<-^r!5b==+~FbKHE2YQqGH= zAL7?=snB&JTmK|szxA+W7>=&Z92kY}W7n|dUGW%$-;+Hhz-K+skI-r)mHKRBo=e67 zc(_s!QYuLIQ`tpAB4R537CtQDcoKazZfv<)%u5hoHQ|7Y^jAk`bWzhzoXH&?O^!Ar z-^Zk6)6p=}FA7mHxZ(DL&o*oq1SYYudUKxrhR#<5ltdj*0~5UN@asd{*o(xeXB40b zGG|0I@k>IcIygvxLv?x$NR}qc+|+s&=%f7=Txx{}yJS87cijInKiV|S-LAnLR1kzs zcW;(%$2x#1!B~&Y1J2?hG<Gnk;G_7xe?8kA!Y2;%1sRI1Xu zG=|wg{-g3+wQX2}MaYA6f5i1yOb3EI%9i4ZDu1Q@7tb8@hPevYVc-e3TKs zP)jQF5e@~1{g6~bNhP~u0k}8g{Ycm~BePM_%L;7D3f}xC&3@IJ5Iq5)^?p$K;+z|x zxtO`|I!K)|_=sNC7y({NxGM7cDAFpK*8=cC@CoUmG*1 zq}x`%6{1hs$*8Ot4qyVRZN-nJ?r@Ut?uTFktKH)9nqbP6*M! zv9;D9m%r~Z5}>QO1YaMtA*ipDK3^>Dpta1k8_-#cEdK<5p9habx>4|O)QMzslZ zRSIqQ?xjYzZmq~POl~rWY`K@wIGfg1hXjkPvzJz23ce`Kh2<427)|%OQvnz0FvO4*4kyocQ`^U` zmf5djkdVlj3As`|^!vmsd-L;Yl3-wPOO{Ql7Ob4PRP)C=Mh@=n=%DT=G6F;{QZsO> z(RB*7C}vz`3t#53V@woo920xIX5o56D4ApDi*}TwNJFV8QuH@(Dm;9`eu@JOLn2vB zAvZVxi;l8aSTpX0j8yUV8iQMS=N|q1$}K^-P=-Yr3>jBcV8K}VbzYQscc;Jd4F{df z9he-qkl{%AwJbdQuV%=1qhFTi2_Q+{5UWFm0GNe{iX81j#8%>VHo%E$uU4^54IpIY z*CMk#TQBnt6YQungO*RUBUm!qWB1xK4%t#=)`CAKlp8p#{QP3LP%UE0550~sFqD=B zVN6~F`J)xp*G>$Xu=u#qFbor4I7g(Q*kX~P@yb?%6p-yy^btvkKI#uUey-@2nAMSW)}sNg)u;sS`rXhBfJW>4{wf{ms8^u zy?YmuSzqV4zraFF)x~}sXpMx62bQ&GzTZK7kR|BnR1b<(7KGgebv` z!N>>uQy6tb~kME z_{S4;TyPfw(3pRsIC7DqWyi>N?b~OA$p|0^mXz_bq7GV#>n{EnFhJ6Pb4g^*FfaG; zL_7vI+-o;@>JuRDNRE7u2GQt=#6EiGLOz?+o6NP(O+MU!P5~_hR`O|I?RCBKtO>S}c-GTKzR_7%Le=HH)#M8KXS~+WSlZ))ZnB zP58ecWeH&$yY*oDDH-o(fB^UR7>_fpkXkiHZvsO{tdsSUl&`U~U$3G*X0BTBG43m~ zRht$pney<^9-0Z}&?Te7Qu^=fJknJ89ms?Er`$S*kP8o31&$d#RMVvvB1x+Z3popWR)>Mj8wzG4|6E58m1;c1U-xtSIXpCR5R~P6fHZAlho(`gNMV}1EAb#Z33^SdkPsMkn zgx*yEDnZ-Y-l0&Ga)Fizx5W-bSfJlPI)c=3>hD0d#GD;WmR9{Nhq57#v}i$&J4R$u zV%=$l)*PhpLD82YKAZS;M!CTc4^FVpEi7#5>FEjbI0yGbnv9t|RYnm9Gb`7iU@@4G z6sCbvPJE*m>FG9_hqwsLOGX&M)dY6uH*WmZTD8UhX#sA?@;ga$bXC$# zh-WK4?$K44_DBpTvc16aNZtdJ9WgCNR`k}=in*7+90o^}k!9BE?K%9i9fuNJ%%fk$ z?f4*c|GlBMlmzP!ji1V2oIYV0M2d{wKiw^HfHo|uJh z*WvVhe54h~m=ebL)0Rgs2kXir7kDAK%{D}3BI|W>S#^R&w{(@C8{B+>i$0nh(%)Zq zA9+u(>-;PcVRO(xoSCCrE>!NaWg8ie<jFSYvCDJe}}Q)>UF1`JSFr=z~oio?$qmn zpQnixw~we}(=Z@##^QuMxrgi9^+7Xd#R>Fw;2#wHBK#5bhqobn(a9Rv@Qfu1BLW#o z#|n}1e63+n`%k+uu!`WuidP4CJ&`CI67Lc6g)%=UF3@1l%x8SPzpUNriW0!wMdEahCQf+qnq}!nbs2CHXcy(y zAnY4YxP_skQesIb_Ba8Vn9xXzL3t!j&?l0uDq6`Kw6hbFNV%kFDWuCKV4Gho9`_(z zswvzxwVm$TVrL{x^-G^RQ<Q)r~N3@Sy50- ztv=Jq5jYV)L<{;rM52T6I0MSsaOAm<0^FKZcc8LTVXq{Oj(F36kcmFc>i&jx-QXx1fc$m;ZUBBE z2pW8UibzEMG?`40OrB)|ya!{H?9wQDYN%e1nk|TuGR85tg?MUBoA&pESc^6rU6nF5 z9^p-y3rjq)cU>zpdM5BBh;@Ux#UMba%0@ zfr694L4(5}WU|aefOHu`L|`fd<;;tKgji>v_l8WK5+JlX!A0?j+Lj>V20OYt0BUfJg9kN#`ZoQ%AqX=Z$h(W$ zYKyp#H>oX@zTGbvm{}__bTXnxhEv${oG^${GCVr$s0JMAM4R*ANC!0@(wx>@nAN&! zE%#55dhw28QXxz9w)EV*!RS7RG)+Q4?ul&S)bgFhdx(L3@SaqX287DW)t|&bgql|7 zw>beatIOsi8Du#2Ve(YZD!d~_?!?bdMNmjN{BQVzMw4n6(Ls=iWgk`Z7A=BZtLJz= zx1ebk;DE^ku7d@xg>>ZANB?eGbx`4N_*lW0i1=|;9Uyc^(*^SZWM`eshcjU*^c9DG z_&)Z>N}3n1%Te8-YZ#8|h+u*djnelyYWEf|U24Ij#We}+;#JwMS3MRU(vz75Cb?^W zen?^F<3p_vtmmJ@hP4}a(~*9kS?ZA#j{kM;ouX=WjENSMNBc~CaL^1Ji+zBMQxHmu zC<0`WG&U^om^ta){*i`-Z!SYxJSM`k0L(an@Egxx_)dW5y9QC?ib#|KU%CM5yy;I4 z9RBS3$8L=BZpzMAWCIz?Do6X6fKCuP#pi<0*&x`l?o#=MOv^eMAx7J@X#@zGJo|Dd zCQ*d96)lrtzeh^kv|0|D03F~07kn}aO?02)CJwPp8}gDm;hQr$ii29 zsWD{|1=l15X%ikeKKojb6GnRA0p-7Z_eE;0qPriJ&1|WGCH65zjhB+whXq_y#RBtx zr@Z830}v(ZvuvR4I&Ft!_6y2VaOyl8L$$1V%Onf82CkqwbSq^>4*X<7G6A{k2? zznLwI|0z!BRZFQGS-_2rxp0~+YY>SJ12D7FLKCV38DPO8N}L`^hHnGYeSiZAt`c`K zpkw4%m9r$ec&Awp6K~}`dSuDTKs3DU{lfTljNtfZoUAtq@I4jqDog_~)k9%rdqi0Z zMTzcbsx6u5gOC&si9Tacht27}_nZfTrOC!JQjJV~G)(lb{M#D4dErqwhaQJdZW1#A z3g1A~j2{axcLw?CfKi9+-sSn=zdf5r4Ry!5P;+_(=8DT{QiTjISW`AhdVa3Y~-ZS`#FFVo428 zN%b)bv>qqEdb@^Rl{!hP@yKz%w}`zdCjc(-%ggGoXe&j?4|1Q)j-^3(oswO(fhtp; zCrzr2r(%aB(0qP1cOQ|NOz~%TkX$C*H&&ffox9s57a| zk)b{SePm;|byd>?)psaC5u7yRyx1#O{^mVwQ=g9%Q)0&C8q_1&W^=3+s<7~ymu|n)TN#gC5Qk&%C-6Z1ft_VgXn>5!l+@2O%(vHR> z=6ENgbqlHZx*#88v7LrU)v?*QXLbv0 zcRaJ~}hJ^wSV+*By@#$#Hn#Ne^4MUh8gPfm)Fda!+rMbJOI`&OWSF5?(BGbi@DcMuHuZe zr8!h@oPp9qalR$cF4ZiZre#=dY~~LRgbY(FSMBH~+3QV{PjcF7I3|>teMR-t_F_HO zZ`x6d7{k>`k=HmUsx4Z~gHE4p6}1E#&K)0Iv#5&SKS}9E20`6eec9<)a_x`4$G^=y zDfa((t6q8e^75^e9!h4d)g6Kd*uh8&od#MwV70ngOWJ;+A!&hbvs=|Z({}^{Jr@dA zC@pKPFEQv#9q`ORv*ALQjSgZT&m{iXu{->n(C~1*ioZTQIu=4&L@_8nBhnuNOVIvT zR8>uT8hbIo75b>xpg~)4fnrOzgx3GFwEzLv1`;PQG;OcTVyP3BXv0KG_yTHOis4nAYm%C@UPu{p%7#ptzK{ro}RM0(23N@NG_ z?y@o+)VeaTvZGqK#+#ai^E~qC9XrGofm0O58zJLmd@GADGitdOXldDgI9bL&H^Qc~O}yME!6?y{rkDwrpkj!@kec z+{RMEU}AmjVN_iyHM6|sMva?_2{`kAWfWkcCn`=1wPkx%Yqx|kIiwx?R97YRbPPQN z60JZLWD^*l{WYi56?l)EGqL<|KtOrkfo7POw>D}L4m{rimrAUvZ<7k7HI5P5{a%RR!Yr9xaJF#yAMZvbdA1AMM&>yz( z4KW!ftxa~5t~oA3B34{{6;!}r)oa;qAk-D?tS}!W^MR{xcrJdhzY(3QY(-!=8=-;& zfscWv>r6p8IlXGz?dW_rx4Ya-8=f;52=6kQS~^tEH~5b^E-qJTD{>z_IzSb`znM~Q zZQl2=-SJMKomAqb&|6qz%)DfFHz(&RIDsrl~bd-n&G zgD0ja*-f4$JuoT>ybviF^qI{zEbV2I{l$wHa7Nn=&hFpDx{KSKk>Q@7_dT>#H%B+s z5h@yxa(h9u(6CWa1tT>+CvTMvY4YVkf4Sl7zs*RNNfG|u0?1Od8=7S%05>CR^zB6Q zg%qmv?OUDy9_ADt=GPxD!oZqy-bA16Zfa6r>rNwcop&Qja8axyuLjb0$2Z`7;Tr6E zLXo;~gt%MI&dyhcz49JKu{$?7eA^52`ezH`n47l*U;zxGQ6GXHb#Mne-FAeXw^R?Z z+zG7`5g1$eXhe|LkPva2T`;YbP6fbZ6Rlp(HwnwU_Q9-BAnZ#2!GeZ)dy3=S5}*O9 z%a5g~sHv?>96!~j{gFGTDLwJ>utvtgtZp;9s@I%0S#i5;S>5AbLNB8$&qpHyu|4f( zsFLNAWQQT0{#tB-RUi-cCS2lqq{RRpI}LC5IX2^LKbQlxmrchkhKVv`4>F4pthFpZ zBdojq^8O4kBG4@X=e?^QO}ET&D*jM=ZOx=Ncj+8icoDj=QEwIV;K?R_iS0)re-~}k z^5#L>h}j2c)_!XyVUdaxKZFg`TKNf<&0yAapY#c!v=_13{rg)O$AhD`=Bez41ZEoc zMqy#>-+I%x#m3Io{s8}znHt-nx!!gSN+Qu?Q76$yCIWn{D%d%_`cDoOZFo<>(#@2) zE2skhm{kw2f^udf&_nLIm2=4Qo2klx*5>UgeYI!U@<{$aK{pe}Z`bXV`3k*M1sY#O zn{;ZxGy+FpUybfQ1mHaLM;(Qc6z{8 z%I(F<2`l;&lSQ9*cUK8WIPx_sjNNx=TzbwhQ_Vsl+hgw|5fLC7vOl*X{-ed#OYJkk z#yb2lSB2_NtR2Nx5ZXapg~{)<4dhaK)T!DZey<<&kz+~u-3p>Bi7PInm?d*hO)AN# z{Wcbi_B@+HxT&s@mc!?<)E&VOG6Q=D2deTZQ>sQ^!s;w!fNn*nPbAw$M?3~me8#KWi zz$uO->TV{z&~XeVBp-f=cyTG9bvL8uQ3FyB4h?-OKhD=8@C1vHE1tuZpWzZ6? zNL~QQqrttl(aBTiQl)`&Og-W0c;(dyKOQ9~qZE8p@+*tu1m)(wFE#i<9XEFF2qy6` z_q@-vn7G&Qmq|x~ytG@-C!-D8erra+gA1#dAWhB9&rg)F`uyxz39AdApXuACO&b`~ zJg9`pTC2PPKn*?KU*RZsWE~E8HdfBtpReo9?BLilp+7^=lAOktL#m+NyF1c1^#KKh zc#u^vlu$Lf*>wDah0w*Em$$srd2#%u)9LAbOikm6c{W;>E*|W)D9wb5qJ)$pnTbfEl!!ty zRYKqIwDC>*?zU{;R60rcVdha!siXkyv_Jq;xF?dyW z9(b!ica_G!Baf2N7%qYT+e!+G%CBFqA@d~NI|Hw7sd?SObH&KY)DcGb>XGrzK2P!z zr+l6)9J)AZ{WW666gm$u%1nMG&*Z?3G066DoDSgAACeODhNkD|)0_+VaME-p(EBxl ztVCy8lzn6F_47M^3g2vs4ytH8z0{Xce63-$Y7z$5a+2|wqB!*1^wGM=S)zI2=vHOA zbi=nDnniO}#a;g7>=QlzO=Eq$Ror<0V2j}@{)X06Hn)jU64F^})`xx#tdn_QzGw-g zk+J^I?!Y8Sm#$NE+=IdvZ(;77KmkjaW-SfbEG`sG!v83#_ri1T&&G`!u_~b#N)f%- zd5M^BW^^7qh~mOZXV1)NyK7y1yzvk_z}Y|hUn_uifl;Jy{ZxnHB8fL-FrkOS<_dTL zshx*?(!+RKse?l}jj|WNin4{FHYZN|37ahmH1i4A?aPi2P0bz@!QzJ<5HJ}`oFo|a zP)l~!ZmRuo9=Dg7Dc6;~@1&<|E311`Q7(e85~mVQa$8Qar@ARsPYcFJc$|H1uPnVM zz_TZ7;ud#O-p!07HS)d%2W4DEGOfINR3chGM!j}fst2RR_3EGz+E6=s@1*$RI!eia z<@t(X);#!qE0Qv$MxZmM5TYTm!Ly{i+n@?^m@E>(c;Gd!0Beo26k=HZb;2l`Hs|WK zYYi!ZC~9daYu+ZxJG7tX0uf5E(i}46&SR4)psM(rQGgBgT)Y`P}!h4^e_eU79;u2rx{e)&3 z0p+9_IgFEcVo3^e9G?0^av`G4nP34FiJQLXEsZ*w4$IDup%S~LSER4DnX`dP7Rb7V z#N*|UPs*$XnuE9pWBaMBS}H>esC^C$*X0!fsYQ`@_PsIe?U@-hYyJK2{b`}yc*_J{ zDdR&{$9%qcR_2uPd9-25;NLUc&s(J~D=+U5z5c&#R#?#ThmlQ1uErO5NiPAMGL+Sb zWx1#KFbqUcd&7}4&KlSj1Qlx!jr^uCC;i-TCv*|HI0OwUNFS zCn4EHDv69j*0K=$dlrryKVB8Nl(Z6w%OA^Z88qK)Y98fww_8c8osy!QFk?30G(Ez# z2M_jAhdI-GP-V-eHJm8I2*b9A8DW$))!D~pbOt!pgRnRhH(0GJ0)UgIvA0O!QNH1Z zNEn4dpwlzfud#!vQ5t!+CyEcM{V9hTwpT!qN!#0dSxY?`j{bD>(yR%x{cM$!Q#d~y zZ^p9yd**gsYbtOv4&Xa>G`V%F*gv?tqRO7?yH&1aC?@ohp3d*#KL(%?Z(3ihnE*=` zQ(Kk1c{7v+Up%LN_B$fg=5C)udq#k!`np1428pl~G!wn@UVIwESwf7o4B-fM?B@t8 z<+`&gS8@4d@C+E2(khF9OUE(HrEE41o;~yC=(ra{`gA3Y%Gx}XdS6}#THU%Enm!Bh zP%|i-f@-MOUVs1nZFfG-E74YIsX42r1rznqFyx%J3XvyE%~!Q$Gmsl^;hbHzJI^+L z*`vN6R4{&;YONI9pJ^xbD;u>ZJWCn`H zMVulTk((Nzx7U?S$j&0|bM6CAY&sA25M5|%Wo5A_ z5p4{cU1a+>7nRQk^W!&WNxU={u%CRjJG4K70FV_HgEp*wZ{avR_J3M{r+!?4hxAt{ zYLKJm=&RV0eOI}-FgdO9oXsh8gj{o>6^JB)W)pmPkykl4(~69gUW+L*?~x9>E!z+d z9C$po^d{Q1hak!q2755iwE2u4<1c@4xiumtJv=r4(g>#V(Lo-8AUbQE>3x373u*yW zj>Uwwbn+#@?YME{ZjFllxxwN{G{bv0pG^XBlohL-NP?2=bu~CJKu}LGj@ukLFd~h! za+_3e z`Up5@CceN#EM1&5UU46%f$N1jlB$aHm}AsX zyLIm#geQzI?m$AT#PvICs3m8PDy^-m>I^akoH?aJJIXSi_jQ`WULEG1X}<$XkV-@F z4e;oFK|yy=NM+~dmV$jEz&lK-%z)<=)csz*zR2f7&Fm=$1P7rP;arnGz935fjB6TN zv?*9ugQ)eQDP>9{)xYbB&>O5b5o=RQ4p52h-11+g_5?8l^cgh3f!!Bnzaz-n+I}l= z*DIPC_jwT2F|WR!tJR73rKMR6CLhFq0ok)(@Icn4j+=jRwNNe$MM|5A!|SB6UR7!< z+PN?WVSXeU{=|Rkk!XaGLWvTIXVF>#8@%QVNkNPVShk{=Z)~>yQM6Gna@1%}7Iar) z&Z>r^-|$_b4KA!Ka;NJwpsV3nY*=bEub}_D&e2fWjIs35Fh%cw`&-54qPinEQo$N8 zq*WS@^yixI5$5oykEXp}XS|UK4bqItw!}u-(GN(^-FVfW45Tn{8JJ__p>&Vcs4&l% znaRvKSie*^ixz2fMW}-~f|g|mGnXt}C?@8Zoc2W-2ty#|OQ=yKTC-WA4h%LQ=_44IEu2&-Vp0BjW*l-d}Y6-({cW@v;fe$95&2k+D`N_h1z`eW&lXcPs3^NvJUSFeLw9Ws14`mXJK($wES?7_Um zHxJ(-!-MO4()J0|h+WfG1X9yf_4k_4nIx?7)X4@06puKW9Ov~!`I@Zoe$u#%^5sh5 z>B~_oiHkJR*T}$q%&q)X7IgU@_9D5DEyHfuV+O%92UK5qi)g_x%HTlT@yGm815O2f z{{zRgX{fz-mzwEMUD9JUR&{#nYT9u^p1gV`bQ7iCbu1Oc42?pdHYofB zN@It&(^cFCZ{E4H7#fX{U26arYD-}TMvuN=mNhuY*~qL5hq5_h)R&w;G59C#lC*b& zz%z4fdaz!p&O4zS=-Xi}KFa_lo1&3kFhS**a-#F7m7$g))3Kx3T-p+eW|UI)Gdl=0 zPD^M^%pCxOf@(_+U?;S60HijwHv`$+MgI{{eWcX;&1Q#i07*LUMe%FmxPwEN-s>V@ z0+ViB`Rf!kL|E4Ly6@Sur$l%D1XIR%`P)4(Xl1pgRoN)_E`ST+)OX=#m2IT{Kvdj$ z#0uc-c)1mv$+9Q0J^~mq?V;4?0)lycg&egOLJv)6dF7yXS0OGO|3Rn>UnjW zFm~)EOBHnywo)cRy&LQQ?=AR16VXZ)M@DsD3-Dx@pQM3cMx9;{Cf3H$@jm@X{FyVM zXgM+L+YPMb`F>RlvKo>(OF;L1@D|BA8pJ-dq7tSh3(~h?SZ9uJ3+3|Q&v$$>Ufn-5 zYzG)H7Hc;2TW_Dlb|xo)SQ_#}W-XuE$G;V=6)urR7o2PXPYSC$-6pq>A0LbqZ|cEM zzu?_;k?$2;xD-CTK<(Ck!A1rB2??3SMd43mD-#^-8RycOfaZB9&BUPK+BFZ9S5tee zJ$du2>v^vPm<0xS9M5@eun2jdp{mU5IkzIOEy*8D{U(u|(En!+i>*|jb{Z70-KOJ& zE#tT_dq4bcP$ z8a5n4edK>2v;Xem0h{)iJS)5e3MEdnpnZpiFEc(7V{~+lDPnFX^q7-(b?hN^Z^Nlx zOTW8Pp~-R%E+4Tw|n3grn@6y{g);Dq0z-wW_#5kkB-q{DAc{X9CGByZMsKL)->g8@5OXu zb^Vjj)RM~#tsAs>olTZIgmj|wp81Sh5Dm#_HyL@LF^Y(3^nwS!tT1&vnVZ4ZIT(#q=Hz^-#3hRFFpxNi}BXBy(e`(jIO**g&EJ@~*CrNka&GC;S z5KBLe1vv@IImC%P@-CgV^tEiUAE;jvE!WjNhT3yQQ8gLV3kKIXMd zZ_pvmIOq0bifwk|0Z@7r(E>xj0hui?_dg!20#q4+1tt4|^x!SJEAC^5fh$TKd?pRf zxwlm|STt6`FL>e|Y$OCPgF(xQwFqQ*HQphAgy;K0iz$qsQXAlh*;G-Hv)e=(Lj=); z4scwgWnQ2Ni)?K@fysDq7(*cb&wcdB28>nG0LZK}1jm0J0A+0y&DikeIXn4aQsEk< zsFiYJt}zZ;cxl=)R#8Dyo^NiV!o<%nqTl9MNfb^_<6|5d-X%yDO*mirXKdL3ZGqTY zfw}TV>$_TQy|naxS2RDnIAkCmN*9LBXd`%zRHTgP6E~T0rIqjo>-~Z;oZ$)MJ^%xm zycKgo5QwdMU?Xo=VOd}`W7@C2F8_Ae`Q0r?u*M9VEPp_^?9LoF_cWLp}$DaFS z@&PD;&Crn?1Sz^HqL`q%jqnfvU=)EQ`O>9V@8ADKx%=Aaz<@d8AcLNUS);j&*6J!n zv8oj4MC&0U5f3X4m+FV*8&a>-(EBTklN5RMNDV*}7qouDlIdpA7ytdgFKM@`gqGrIV*r z$9n4~esf$k$v6i0%RyHs_XZNE zLN$2|8clkR4A=z*mVBOnJdEcN13KI&U}$5`4vq9ZIt7vU(8P($9uxxgPm! z93zwlX@E-#xzoC#wi6{td#*Rd_H-iKEfrFn#6q&X=)!48WRqZz0pXXM1 z@e6a;ZWJG{0E&4WOKyg;ay!Q3q`A^nCZ? z()ssyEhsms@Z0tqu$~jlLqk;lCB4wRrlv;hF)LpDy$H`fo&!pR43Gy3(0#s^aZEA! zq1_a%85lP7V{@(s$M7IUbvqT6KAl5sPBx9C}@=_%6|SnPQ|H5cDN;G5nJl79XA6`zn0 zPM`AUmC@%>mf=~X7H^Zf<=0_itTS2$p_yTm4BkJj`119giDz2JQgR_pt6}peM7-(= zytkGDR_C47FYD-TI6y)Z_7!}h5th#&WgQ$o)HM$*fONQUF(dhmi$4@50&=+^lxf2N z)xby1F|C1u;zBop#7+HCrVi|!1>#tk7f|9bcCbiuNKXnPli}Xbzq#;req@BCMT**Y^c^lJpH3BFASv|*gwX=?{Tz|E|>sy@bG01BXcZ% z{>(?OpLc2PX}fygyuh`m(hV7`2oOE2$ZW=M43G-W&d#i7mWDT-$*pI8^s1wIMbNMMeH}IO4n9pDvfm~LL9mN z4ZjM&=rgdoShNz_esE@3Ow2CyD?pu)dj0@VBEtlq20am#5O#hsmMzRw zQ5t|1iN7V~umbX&6TO|I(!tAUxRi`y zWKB>~Gc#c}RV^UUIGZdxs|UTe@V)fKBmY!$96KAvpd) z25&Tqa{v6GJW83tRwDpy8R!iR49r=D6yBO%wAk~I;Tu{Iubn&hsto3^FiIH4ErC;O z0*XgPImf!+@e|6cj6lhbilw%`3w{z0!45fX|r)yGvL= zYKM#GU5@h!t28a@poCWJ40kf=IA9iT9RhL-(FpA!(}Y}i(fm>!MEdsI9~h{=t#ON| z7y<(%`Hh+Bg(6%|plBamTitlVU4u~PHcN{PYpgNvTqxCJNJ@c-+)3N?USf|^muGHL zV7p|A)7Q5~7?IteVrHr*f^!I6xtp=UY))<48m$hp7=dWLl)96|YO`X6)UEuGQ2wgz z;i%!fD3QkyNXaQ#478O^areOYVH$lrAe*awv%iae(c@@y!YIvUPmFO^jeMr;)Xf_~ zg9f_zS0T0xrPTTP_0cTbiytYz#D@(@qsAkb+nv#QQl?&mMVnA#Mi;t>k1zVUJ8LW7 zl$1~x&0M%JG~=VeD5zlBgx9z4G0XXzj21L%*pMlNd4uLHnLFIG)VM+Y`n_jvIiW)q+unX>riumD<9x7c9-6Rbbnn|cb|_!13O@3qfFkk2**hG zkS+B>Hh(wwOD&|JAm47vIZP9iMLoa|;PDERs8u$~`z0I>qEK(7=wR6ckL1e~A2`>$ z$EP~bi8@t&@u%zOV8WG1MkX= z5*1=DM;eZ!ii3!l)MMt#D*WZB`x9WzPei1c04I=%B}I`s4&+^A%;`8kUbHyi8yD|9 z(byvl2c^jc@WpWw+4=I$Ypro~TN;3saNq%V3M4HxAw z#YyoxtGui|b=qWlSFzshI;|eK5Cn<(vH+z|SJl)6_som)Avt^|w*#pTwzpqwZ*TE& z?Sh{s8#Y{wS;vo>!2=+)%LoyMIy57;mt^D4V%;Dgrx*gX3^_`wVg;IN*1dbasQChm zB4#(I-UK!?P}SYr4jT4XzeFc;^S+q3XI<-l?-{h|#u3+~-_2UJDg{&=M{BkFi7PC? zf!aSZ{Jn`@V>VR}A)bA$C=3COAMpNwHyLzOV@)MxtU(vCm-1Lx@0(^^K8UgB*bEfibtniVX#v9kA85-MeQTioHaaOd$@6vG&$Cy#}`F z02P$`?K*Wrz`y{Du!cgeQ{Viw&poTbnEo1WcLm=%GzBp^t2}zP z&Q!{T5m&N3{&VC<|God(lW`^Y;CVPjRljw~K4MqQ@j7TIuU)%JXnjDxzA*18FJSqT z7%!3o1rEwF_HmtfFgGQ255s{F6A-}lT|*QBUIq}!ODI%Dz((4*xuml+{20ergD}Ia zEXzyi!vHuT_IIfnBuT=H1q6`SNX-Q@MS%$$U-!tn?CgE5{qJkL&w}5&b=S+b=#~GZ za%Ch5XhYsHUqn7uq0~0SMtVQJmQ+to@ReCjf6+}YWTTBc>d*n@pnxp9x1(C&O z8#fB9lUf#nePoxVh>sa9e66`iKr2H+0O@Q~4!onM(^F9bchV}u#iyl z3^c27b}{&IY!LpU|5==b^kg09l%8K_R(>&E#c>XcHvE9I9>T22{dgOC+$ zZerZ5HR>5|9}pgMTyB3&tfjV6M9K^gWSg{PeZYYO-?B5kL0*Ra^L!LrwQh5ir?Kux z`L~5%)r*jd1Xf8w?y>a=*$v_`OM*}SXh zl#*7Ual~$l*e{F%GEveDmDv}kx{6q0cS3u0PdHen7}%yhZ^WIAb-#b%;@Pdbw#AA? zo%`boLxfuP_@oNKBnsD)gjeI>g+Ut4YolSR1R|fPJfvZf3F1}p#YL7N%SS*fWPcz} z@I*vC$M#-VGcp8j#)LkUTIQTXegK94L-bZWo?aslP1}#~8-8U8ZFj;5Bj^UD$9-?X zs!>#H0>$#Q@lW1|ZhGjG$f@n*;kRg^0(d)o^;<(3^`-r;{Qf;)%)PP8Xa(Zh2hIg* zwJlh}`w8ae7QcUQd8|BOE+#MFILDt>HB(ev2p)$6$DHp4ItEfgojPSg33wtxIUm+r zIB<^pzi|6ll6XA`2Rx}{IF|4>$fb+6;W|on0mZP$yaL5a{P7PqL!_fhs|usojVe6v zqYsUjdP%eFVnCsd<*i?$zb8P*&@}N}l%delpXe3k{lHC8QSrzr-ZlUJTORl7ih}uT zb|Ie2`;^2X6oLSHZW@RMIWkaXf>N`NjHRh8xwo_XrsgO~sKxGag{I{4LB3IM$=ji+<}KI%tdhCDC7T`sOH78W)P75)LzFZ2pI{jg*T zJQEtyetZ#S^@d@}Wad`X!g-e4sn^ifrBe#=&=*7d)BP$UA7wC^2|XZ=5%SXz+MahD zF%6w*SNSewkEg8>aDhgE?Qb)`$3r-X^C(n4d+UxfsX}RUL#Y$Y0;sC?lP}|FRC(qo zUala?W@_2*?v>~TmoKgem|O&uJ=HV0twn3^lOzLj&a09V4+QU2HR|6^ePunZ_yz$a zVo@fGRx$IMMl}tX+kj&g!LfXOGS+0ONzMpL`UR24jupRobDf+BjT0-UoD?*YlXLt* zztYGNz-s}6=HGI^{>I~fS^#MHY7%v%?-x@J5TgJV|CH{hx44##?3slax$f4`Qxy2^KqbayZvhseF`xLs%P}1aRdVe4h5w&)R=A zJb2Q*9_EYS#L=Hqua-+zoeJ^AAncy^N&J>PUpK}91RXy*!POV}092$XE0TI$dHOV2 zCn!{XMX;@%UG??OA2UpiJslziUFW;W8D$j|>NO&w=7 zBfokHxie*5rB2e9h?O0aNdu4wJfQQlyOk4-chDv9#x)+hG%T{ALb}!1aVL_)(?zmB=sFOytnr#Q+PqS2dAv3Gg4+JF=6ypDyz071 z@!^Vq-vVYqowuF^V^I&-osxXe0<7I_NhnDx|3_uxJ=N=ak4iWrG_w zQmM5c$3kd(U!Ex?@BtGV84@fnv_6<5@&784SRgLxExV2HXtM5-DCdB>|?}p<-=AZlfuFo$$*$rGq+gmKWB4 zO|!6BMHLnIJ6LV%5jb{+vJo0$;=SI6@tbjSLTWAQYO4GLmzrhZuc$d}*#0q-<7qa} zkaAJCT=P5wQ&iF2TSd?CY1Qa|qmgz?X}J?@r^#>=$csz?LRv^5K7)v5Dz z$sTLc{%!K}@cBj#-)Nmp`^iPFKDC?amM#s<;u$?6c?Dlmux9;xS@cgUX% zP7MO?{9o8=_40D2H}$j-K$IBftT#i7Z>w_JOa?E3tVts>{YP6e^JmKr4Yk*`Vxes~ zpA%f~CJ$2ZeK=j31X_Rx+~qNA-m6`TT{+|%B9;q$Fwj}4DJYR%Rs_%Xyfi{-R`HR8 z2WKb8mU?Ce5!ikmuiHY51&B`A!_^R~206i?{5eKuH->0xrZZTAyO0r`EO98=xwIuU zmgBiFW70cn1V`l|^x4U@RIUkZPKS7CCp)Bga){z^LH!(?*-~50my$#loUz-r|3(`s z(Ry@GB=8q)x<_##!ri0iRCHKGBxm zaNn3)7EIb|LLd&c7>qiUwy*N9(+y>Ef8E;no>(8!!wjQPCV}-k@97OVMly~w>~L5F z{cI9r0!QuN(yA|UEC-zjffvi5P&HFU_xiP^3(H)STy7&FMN^&-Fqx{^hF8gwp9Xaf z%NC=oLRVt>z1<)OAcTaiZ|Y3VZm0$Mh#(862izt6HV?*S8qxC=gc}qmR0}s5MO_DA z3SKDxRY18P@XT`5x_|yWRiCVfWQ=Gooc7OELL|f@;y66ULq*3Be5laCXqzOL-^^Gl6m< zeJi^WLVzR#^HzR9iRVo39`79E!ME5$(oJ(ovRo7==Ka%5<JEq2_vFdyCrx$Miw+WXJgUe8F9LvE5mM2)h_Zy=A}us= z^!>G3(ecN}YZ8$=cIqT5TH?%BZm;F{H5v{T0~RCcb%I@DT+hJRc;9;)>QIVGL^gZi zX~b=hg~TXF85Wga{=48C7eqnKlqf_*X2uPCLKl?t=%PzB8||rnc5*;lhc+B`v<}EzUrWm!B@@00Y0@R$BkrGFl;1 z-4vY1n~zL$ovd`;#)?^%oNBPSUoBGY7NJEi*YheA(wG>W6NPrTgH2>yYLM%uQwIo@ZV&IW@kkb_^R%J_rZiO9C(sG6y= zE~yfutUmvBaP>hY!U@nKc7a7qtGs}QXMa%8&+AjyQYFii?pM$(zx(A65&SJD*FH2z zOl*$dLRpt5pp2u#-`{$G@iM&3W`@`McJ=p1Mbu{cp`o4>!CYxAvuHK!nhZ;Nhs9arxHP!US;^r{WO<^CDoOnJpYUZjPa)NN};c z9HB6`dL26OjDA@jA zAgdq5tpLaMGj`cMo9d6a-{nE-_6)r)hMX!+4C$_@(+2MbO+LgPM5kxiYv!3; zJh8P$!W<~j^GVvbSY@oC@nLs*jwgq(T{}TuKUI0vlGalb_b$R93?WG5IitUBA)FbE`PLH}@WZVEgam1%^QHvryQNit3FNL={TOE%u49}~z??qQ0&dKS z52qM{N763qSO+NgE5v8Dfe%v$6j!z*x<&LvTYMn#^E~PXDt%jOOu@y97iT)N&BL?5 zY0#)u?zG#IcmHH6Pl=d0@X3OP@oLbSU6%5}ur<7MLip8u<(9{`SZKc>d32?@+LG!B&X+ z-T_u2c0lCG7cQW_m`*r}^lfmX^qx1yBQn%L5j3aFp6UfRkbkgTGcL2AGs$zTIs-iS z@J5kUyjV*5jWLY}ulsk@H)4v+12Wd4{^m60vohAk%KK@e;p-L>9@r_bsN=Ohcj0n3 zcp>&@-r+Ec?Hv4){R}a`(#FIY>y&2phfjg={UaF{Os5R5~u2T59{7HXPnDfB^zTFsH*Gc@=+WQ#Y zNe*-}iQC&cv)>Q?)#85t1P(Os>C$DQ)>2cF9Zp{+eXk}mD2Za}3LBQ?+QPcX_E>q5 z05qber1dvYJ@u52RC4l4wHA{HOX#~8jGg*NI;f0<;M)xrhm>ozNIjP46J%-uP`f1*s=rjy6M=={kI|1Af(n)IG*EYR;UC z$Gr$#3t4&q9Aji`+>^;Wp3y=(yD0$K(Q{4(EY2$tqgnII8~!H#0q_~99-kyrD}X^~ zKw?DcSyqmW^TEHRg3Q#~o}YlXH~_SHn_fI^X08I>J|m~d7CF+mCJQSi%O$Uh=$JA; zeN*&|97v<~#%h_U>~`0AK|jj%jo3C#f0 zn1+-!CwduOTH`?Jg>K^L^Y;O}v8SIl?bP&V9}&GjB4G^eR5CwXIW_4S5Pw{^C#%|WSax;^0({z7wgS-zpHEVY3-#bH3wx~^M}?` zy=Eu1-)FEd!Sj$`bCaY)L+{wNA7BTRZB4E@%k`*93OGRCO5513)sK^)n>MOiwP8% zJBysGzq&Z!0mVLD5oG}aDEzGLdmCaE1r$w$I~s4`_e+b5Mq!cz9;3f`^Y~un*s0;$ z@9vkVHHY~qju5t{SC)wx6;pV+pi^d+j3@kHS;Mi^sgRIaG zNtb5ia3EJz6pY6Z)tdOlue7dIG5pEM?Qn{OP-H&sN9U7D{&?`B6Y(JZIjJ^b`vEqa z*9i?2VfS6SDAcuA$DqgDQGG5ZW6-~{$v)=eBNgIg5u-x&6uPj)gOu@VRQBUAcXNacI?f@ZgcVI zLvz98@~*iLWb+OSaKyP5r*$$mOH*;;PI~N&J?gqf%~{4J4wt5;Nplu4qj&M~4(zBm z4&Ix_uiV|KzJ{j{e5=?PaSdqkGT2O!7d6uY+0jcXCMixAU*^bIgG2>=KOX`p4)d^* zbaij&%JiHZWuAC!W5@EcGAC7^;++WzPB;xRuExsL{C<=fKZJV!ia76kix}DIrwD z&u^X^tUZ(hvf!lysu6^tiu|I|G3_(s1I#B=n7{4j6W~1U|7DGaMkD!p+0?6ibGm>N=i@_Sj>~MG^@HzENJEgY9;sW(U3<3lb8Xwg>uP*A~qe* z*a)jfFyQQ^X+X|Lkn@rz-R$g=7U(*J`GEv#OF-JrE0Fz-q|hL&ctN%rDu`1m(hb5g zpGFox)%&?o33j~0D9C)6luAf$g6%{;0ZD5cHlAIzHtnR{XTzQiRGPzI9)o)KzP2%9 z&F`P<`E{~qYOQ`T#gx(f`FA(QKvybsA3L@)h=#)NUzq4GkDv*Od!mWTz-K{*F!w!Tc{qY@QT;NyT-E_?ShwH-B+lz5n3BJ7`Q$e|Umz zVwfFgVtGk^Hx^IHzLg8O2NUsewDR7B-D_~mFs(fLEiVQLt-jZvD5DpFmoYOP;|)pN zR-xO^)(A)iWyU;?_S;XU;K9a3zll5Ws=Rv9?zv5zRN&E_X$;Jg6aL`*!HGB;vABk~4NKQKi@r#n#Bq>CwN%nFv z_X7bIWBG=LWg_!7HyLMo=AVagD4BVuNMjeXKVXu%`5}ewT$bJ*t)18+C9|FN-DE9e zRSE-))+<&_NA;pBIDz~eg}tDVd-?0Fb#8358rH)@WTbq>HfXYCLWktz#kmCh>EY3u z&PErh^K{4tZ6L23v~XVZwjx0rEDPD;aQY&JF9CKH!xnb01@X~8b^Q|7#X%Nkj&Lk^ z{w8Z}L)wt;ymksiUlgIMRCknx@}ouh$6IejJ;UyjzP%Y6Bd1@ke43G=^BFH5KV;>8 zEO}}9(0DpUd`|@yl(x(@8so`hsqZEu?W&&m{OzZ_TB*8??;|=hlnGODS2}T2HSV0E zLeD;Z^niG7LhsGHoPPJ#tsl|Wje2-6_uw}-rn|>phD7$hd9#EWL<0`lx;;KScXn5} zfu4GnVohPA2%4qJuJpnBfR%S^E4sP<^i${#EB0pdN4637RM-iCS>X)+!6VVr%WU!m zbxT_^6Cg5l@8^t*Ee|jeBdIvhs2-3i*l*-Dyeu`T`rRuZM|W zUbvrDlmk-s;KTF=S>zvqLEi)zw0IpnY-!7PT7BN@+g8kp6&Awcn$g8#iboFEjv@dt5%uQ9`L&7X zhQPpgRS>&-urqDOc?sUAjT7sO8Bksyr&<8G1X`Bs3-+Dze2gDQV3qm$@-!TcK+)eeB3R*~XBi z#P;z^xV#Scvh%>Yi01Vmkz|k##30#sl8d2+LpZ3UScuS~76E@U_Ule$VH`@b5!p}n zS@REtn#zqov8F8+fuw^~D9GX>R?8k^;=K+${PEFPJ&u{)S#8U?@mqj31of(|@%gcu z$sUieupY>N*&2MqK5aegTNUbRN1GTXp3`)w>2$HzGeoI?w7;+4PxpTr5o_M4Ll2K~ z#a(6ZUE9CAe80-;V;~q6uGEg5fN#Ud zZQ-6rj&ueWt9;T07?fxK`fe?p^H%;VWYczLKk1i?07|hpyD`@4_KpS47*HsrXpWS2 zKw&R)1mFuhy_0@x+Hk-W+yDiNsJ&Q=HIcs4V*HzL6|*eTs)F-q%UZv)t5f`+7JxNJ z0AvEWB1PZw>V5;g%QM;Vh9X-Qwa^D)ydx9y1e`Wva>)f}j8QzTMT3RZ&{F@{vqe;^ z(q2OXDuA@by;2;<50z6Z3v4K8N8Qdg@>ZeWN@jlI4+dBzj!3|@Eiq*`t$Oz!X0thq zPPnh_Inx3-RoKhB^!Lxvzk9fRCrxQR5~hH6s;^@?P^emWL%b5Wt+j90UXUPkx_|lu z*7?yiu2hY9fyHdSm%FR-f?NDbJEe+C#}GW&@a0ZO4TPd2fSDKW;Fb0|G{IGi`E(hR zAu}%GE5)b`D2R$$hqCqe^;}#c)~BsuMF$+$EuQ$EL|x-;!|Iz+4b zG}S`{k3A)0;9k#+GCNpZNl&jwT;5RL-O2mMV3>RCFk~By*oebD;49S%B8LI0 zjK_~<2exHaHhIL@ZG?aWMF$`YE0CPhBfs7`+# z{E4pt&93{uj6Qw3LV2Xix#tg_c3N<|_JOtDSm_w3Vy?3*gaOHnk^Fm~5T~tfhcl*Csz6NVsf{ti-H)dQ7MG6G80k>#P0mO zwAUFv)Q+cHXiu7St@G%4gRY(bwy{u!vDso;iEGpVa`Hj?Ac@?|&HUZg;Zs31iLI_! zCW6FX19eFDow~U`O}NbFCb?TbfQDk{Q!Bjw?+sn;zh4F9Mqb#dST;+3hK!zMF)>lx zDZ8kuirp{_b;>sx%5TpoaJu%GB`zh_vIvk+afY$mxCq06%Pt)G^{r}xKNybP!jj24 zakRFN`+}>>(g9%8%rY%T{DTT!=t>)W?&qsIteFc+#Q>RKfX#-u0fCn9z)XU|?bG$n z<BO&>@{3>&>dfnx8h(_IBTpeOZlB#bs{eApTE;YZOv5q&CY9AgHJXm z=VIiS392@4^9(|<0-F%FL$0AV`*r!WIY$f}m2%kw=!aXqi;k-pzpgCQrJP?kar%Yi zfk{&6DExMiGF~;AuDPM8ioD%v4I4t>d_j#B|BnH-Yu++E51EA?HZq(Yw63- zYY`QsrG9wB>C=ux%XXg8qdS4bR)Zo5#DkQFWfkWi7fzj>H4-(ru;Aq#gSR7w zrd)(g#Vj@LTV>&vS`Cb&N;`+#hbN#31qs~#69YDf4R<-tLhMz2W9rq9GQA-8$l=2` z&`EmX?uOHAFVLI5=+OG`9a6GnlbO{03X8@0ma#gPhwTLb*$0;C$GGvH9|dVXv|3El ze$gL)JVKp)EArV9n)hJtz%nbWp&4`0Fdx167z}6na|PQX&?&|SjY8>yuYRH!F12af zwkP{hE%o0G=jH1&`ZN`LlYX_exES&uoD-_bDe7*3T$sJEp<9co$nHeV;>T278+g&; zii)~|ESw6dXK^B*_3o+o1J`%so~%@T-GZuj(v&GX>C;vc+He-^U2h_t3LwblNCe}1 zQIYnIqZ;H-Od@`xIty(1fJl>I!Qi?BMU3(r%_tj`QwU z$R9;Td2;>;{zDU%pjIRtFcos|Nd6})M4!|T6RbAYpTPEENHpf8^r@=~YetbD#p#^c zDaTE*G|41U$CS8Mm*23e1cYUxuI{VYwd=4c5};WA8Ng-1;c^N<0TU=0q}<|cr)Jw< z&?u~3%L>>lRI$uWR2tncf4kh?9xGoD*f$#>Hgf)}F>U*VGH{H!TSL0>mrMi*>M8gg zXp9akJ9zik!4V3_mTxbf$$MkiQ`Xv1$2zGVf5i54ZMtJ{@|x5tx;d;zeR*ahe~&K- zq()&jxieWZla9bVPi9Tz;`3->qpscR8qBhePP99IgcRnnAfD5zDk;Eav97_iX)JQT zecb=fSeCwsHi`Sxa-`)N^h@g?^g4fe7JctE(U(Cq@#}VM->pwa*&->73?Q7P{<1($ zudITG){(L_ch%FjQ}QQjGRLOCH>R|-c4!6cC-`9EH#)KCR{No|`Q-&0<2M0uZtAu` z7VMIAQ_ma9@TN%X69LdR`hRseU9S5l`oXHC?jcHqaMSCwWE=i#X&r? zWk;f03a&)?93C6mF$t4L3SQYiTCq`mz<~M+N3}NJs=92!$p(gPj3>%Mhdtc5MSO>f z;7q;50L<$dybud>fbVVtXDdvEsbv#H?+1L|6dz~R*gxNo=3drbykV2W&@pFqRzA+j zNvZl39cs6%?m3D8-~DHN+N*cOPEKk!(^6BUEegn_mpE)@t?kJ#Q)kZHPB9L@)RHJD zrZ%cx)(4RR5nfw&?dnVYjLh?3OM_!tH>EJ;KOtO_s!~DWzlw*IO8-?nsnx~JDE}7m zs)H=AMHSqmQh~`2?O@n(v7MdJ3u2H^vGIJ6b@AtlFW0V35`QQnQp|@{wBK}rDbDxm zKc2@hD#pWDzR8{oJA3={@78nmJ0dB${qAWX%PaXwaW*L|sovtYb*uIfC4K*e5yR9Q z&NVmB8|(P2U2lS;f*aMIbCAdUugB|eXFjASuSeW$ZGTZT^kV7XIr|QJM#|qb8K&Dq zH^@->sYi8&j}F{DsM!EWTox4}Ttxzt;nLec0hqWY-x8zwe_XbjQY(R;p}^1LR&r0b z@;M2w+N?+b=sY*&x+XC}6p)~O#KPIK!Ufp;Nx`ejJ{)ku;^^__vha?mb!N@HuY_&1 zKlLb{vTpqdo`GSdtXl$5=*jM4D1W;z&KUAHN;MPQ7DdtOWa~^|!d1+hb2dHcV!J)q zui}0xC8Z01vSpUtdHBTop_xi2cjGwcfkrES%|(&0h}%Ahx_Y^Z^u2o!hjI?R>Dftx zD^+(`v&Z3TYZ?wJLD5?Rc1r@6y>YT#2-QtvwAq_qm2(%d;mPEw6y)_i`FiJ=%n6i< zqO@dS@3xEoOc_Z7b*~0=skwNXUqA@Bx<20hZnLXAMnR>&ymO1Y!^R)*eA#@)zpFKJ zo-PcbrR6}Ja|8Bo0(E{h>Si4oT}+o@FEZdjlYMn7>y0<>aghE1nDfQ5nWn;j7#Mh$ ztHp$8Ds88r6vM7WYfrjgg-^fYx^mLJ!yD?O=9P6MLWZ!1dnM~Wot=l}#;etX;r)yM z3YVBFBmD(6$!l5IhZfHveAEz|>&@rIEIbG@fOXEQ>Q9SOJG2yyoOygWt5=9cF)HeZ zj~t;JoiuCCrimsd?GP_YWwNR6S5-A-RpIDf@8{DFb!QjyC%rIEJw;+Df=^5RqS3@j zh$|n&X3naf#rp&HtDH0IVa7(;dZ8vkF~lM_j@IdwPs}MOw@KKA2;l$>NlhZT06Xpg zTe+TljPDnH?<3WjFvPNDiF=FsI;4C`3{fhbdZ?(hw8`MXgT?bIx6nWQ*>a{*g@R&$ z>Ysv?nu4q?(KGy!T9fjs{ZqW=uuTDD6qhH!j9l3@kRwjFvkNm31 z%EvGe-@%_#bx92H=#*q01j?)T?tR>!%b0xo>h^};zI_A1yh^&h;F}?4aU!mxfh;C= z>wjJBxv*?{rZ*s#uBmB2dB>OtmU}WUd<7H(>1RyL1k5oIeI+D};2G$-ERm(IL<*U2 zU^OJ=y_CWX5Xme*Dhn^nd}I_BYVF#;)x=iVu&rIY#6anz!IU(QGF)0I>WPV{jm3-% z_lb6lbXC7?HwGw(+t~WAzm$NAcq)3lKbie;Xq4$LEdp)qrr<``Y&)xsD4mm&o7<~* zZ;zuV*X`iIiZUFuFBygPr7hiP^O2_i4MsEc;NCjCp>YY=hUCv#wb7Nd*b7x3@^|1< z6*cX7(ez)fRL^J}MBmb5;J{sktyQW~FTltc1Ha?a{0Vp9D$_u+b%-On58wJ}itT_3Hwe$f(Ewi;d?z(+4YDEJ4nRFKFx8QlnnFQ}`yX2$795FYlqVgv zB4!d8WrxQ4bY!@2E7(W&BlYGcu(xyoGLMpaw_b7CV%eO>c{Ecul{-YtW{xi=X7CLl z)FGH`%DNkWvEm8_L=R^3rNY|KK7 zA+Oo%2UF2){FmdsXtms7&vG@*^nFyCi$5PBcpD|#_1qC)dOR6G`i@pdBsN|gN|fLy zXE1)YHoTb1RMH=HeLVM+|Bi=YR}ft}T*y^JS=x$R2(+V!B9nbjuYW|P%=xcYBEvWM zuU5K|yQZ)ILWBb{$pg5Z7;p6+C!hL!7d*u~R7z{vZnsay1ncn0ps{L9ns0zuYX!X2^{jfJFi&VzO_#;=GtpEiaHn``A## z)b%iLv%KKsS(!+Ysph?5|E^urPqkB_z>AKFnMAPR$1wiv#dOSqrV+ZpgulTMlQ{K?j#0d6 zE_Hvl0K$r?GdIe)UgfL8A|o4O&nxcJxklozjMk+oF*>G2$nh~FXpiOiJhJw53(Hh$ zFBKEG=Yd!6`D)@Lpx9&A=^4Zwqa0?VyeenT#wtaH(q|peE;qCdJT(O*^&B>AT{!uk zY&bjpR%TNdl=qMO*p)w&^;g7xL8pLAXF3_WFXc8%USSO54DJ)xpF}x?AGGD_5UJv9 zRvW<6%6bQa>9rpVXi*fdR6PY@z7fl&XvDT~CT z);4b*=fiI(Go2>gig5m^Eq6yX%tw>pB`}2m=gf<4+Wf5V$zKfj$Z=xWkO27T5X%-p zig(CoZ_yhLT@d>Hjry3Y!;G-f8FOOG5liA-YC~lz!6gS29 zWCro;T7Y*Et}`9BG+L(dTy=S_rv0B&gH{t*9+CXQ?aPC zhUppMiy=E@=CL6&G-S6oRFIyS zOk;yVH1-_}qh3FuzLTXfTmP9mNim(@JvwBrZ0Z!=Nla+y(nsCZS#>JPOuFz@ZBNE> zVm~oTur=gGgP#^21gRyj3wQZ$Z?A49>Oyd8&c(3QB<5+y6U zXIC%g1c)gpB|6I8yDmX9^^k$#MU=r@Dk9Gb$dLjPFB7$EHYCVeJHQ5aN}^ZxwY9ZZ z$*%`+!3A1k3>^jNF3Ja3(;bPIrGWd`jwIkdY@R-N5B{ zGyj&fY(7W=6=f4zK~Gc|-4?Xya|7syt!Rq$yKL-pcIuH37uS+r1^ZWBYE((6h(*Dl z*vozJfOfhnZj+6e0a=A<%$?w|f#suuXWH8Kk=TQMWLMzU#SYzQd1-)N5SKye+1+qDK(7yeW8Vp4ap8(gts#e?V1cTwP&&WuYLG=97)Cqn*!9h{kE z3s(Ham?i4QJt%`KH&@V+a$)#^t(b2R@V{`Fp{mL4yMIxOS?Y&9TgZ$aL*kOMa*duj z6kqeAUzjvEnbke{6XS0{&XQYWx=gb=XV-?}pA3DwTAGvKt&pXPJ-%*B-^1*iF3c{~ z^h9KRrR%lcL=00I;C8;%7p9ITQ$lb$$xRIypL+#@BW~-^;v@@HXp=A`{=MCcLWT~Yv;Rn{sHZN(MD0*MnuU#!#GPZ;wirPuN z{iK=7l{Ae|sjRH8DL$#Pp2nCZMJ!zU?W)Vv*UdpZkeO!dH2T&Uf{}8<4Ib{&qoDR_ zFf@UOj_CTR3_Ps0c?^h`*`dMlXyyUA#UlB?y25c7+A;ZB2n;e2%<(esxb!aj8}f+E zf{=?&MTd(c$)$b!#t`Mu8L9}Em?7U05MX+0Q2~u{NsEEsDXWmOi(3K1Zy}F;R{6g) zVJw&$Ol}+rAl0bhtK$J9jOc5CUEK-Y|COQtauOXtq=R0+>c=;tK!1>S%Dk97g@q_u zEV-OMQi8yzIX?E@+n)3hTZ1#MHuZVnp`J=*Ehx)ZFY$Ei?m_9X`>d(cbZ$=@?pKZR zr;myatE>js14e8~5*B-b%0#H2W>C2j6N6HFL`pRufC{7-FyK$R9(G7=P0E(=o{W_- z=0t|kXKAYgI?$A{rj!Pu6d7AO3~_j zTx_f!-vH@SO+q&df9hOYbbFuaZUp3Tz>=LRg`;Z>?KpcBLxeI8GGX3p|x# zf;yI*=ZgaqHCt_;wnw|}yRG@{*0Fz3mG3Cl{ZO4j91i)nqr7|@ts&yRZ77(1lA5+f z`TYMHOw=giQi@&3cK9@|m#la~`BX$urHNyVYr&}}CL}7$H06^GP*GjS9buaot?Rbv zwb|;38Wj;{jW5wMSY|B_B7T7KPO{LBe=7?{wrsg~bkM;6X#uoAE6mKTxV?N6gpoG3 zzjF2T^=%k-+;w^*P_-mOW@;)E$A)ujNw2c;p5tnEW??<6D&{AIRz_QKFL(1HM)d>; zNKJIxK(|Nw+73nuHYxb+ro6zm(_702gI zA|kev#bf%~P&b1c>CyU%hcdO5`RJ$!U?m+zZy%pdR<$k&bEjY=o$~q~P?Zqbn;?L8 z#pN5Rj$7AS{5}AM;6*f@N~WYPmDw4RhbKa%FPj~YiOo16RUnY^jW5W5(7VvAdg!PE zgHGve?jj5$5quHFW^j0;MUa7k%jz|vk0|GxYa(qhou^&z3eUMU3x}C+{lhzjN3X5H zEAWrEidAaRftu;Fv$`{qPhF26WdlaAve0s~X>2G;m5luS&XnO?psUdQvY8og^${(; z92;|*878qalhTBUne6*zCio3Nip3u!^W$)baA_(KFPy*7WPUaoeea<|Ui3-Vb0M}H zD?D+n1J9C16L*xM7e8_sDS+JwmP0T8qc82{;MD5+pkShGh4|(pUY#1ih2+a_+kg3x z6OKUm60k=*=-XySyB}cU$FpD{z~}L==zCj_#1hz?$k656i*V8D=)YEiB2Ui!Fa@ktv}n(tTZ(=0qwmxB z)LZ!&yg=DkXzu$RdHWTSIsw8ld~W((2t)ALU3jNAg$%sP4Kp9S%P@Ec|B7$}AyU1y zTrFoKDpVqNJwS$oNcwVDJ?jJgK_#lgT+ea4Wse(g{kphOH-Y$RZ&)(D(DoMxs0U^y zN8=#E!$*l0_X<{%oHn5t$F$5$pq)dx*%+A#HMK4UN`<{fzX9+>>zp;HhNVBC`q!lt zC(npH4FBi{Ecikdn>?qye^P$1;ZOMu9A|6nWrpyH4%YinVc!n-keys(rP)WKQS>gt zQU08Y@@;;wtTW&cXU#>g_#ioL2Y{ zCE*G#-dXA3b~NVY5fF%1NF-(8T)q6rp+jLT4yMqhpRu-W^#>ga1Z)ObZwI_G5s2we z5#HO|79A1Ai9Tr%Oj>Js>m?jR8AKYpIos*4h}UroC_oC{HG@avz3MPKWAZiV!*T|t zTY_v*?(`G|<UNk#o<7|Q z5aap|2PPM+d78{CbdD%bB3*1ltqnQ3gL%FoNgv_*S27$zt*@E1L|>2;7Tv&uHDftJ zBUuH_+gPRAj8;iTTqvooyZFyKMqy8E0{qiVU4sY)P9mG(4OEiS%cVAXT=NdW6IK2_ z-w!WkF`-m}CeL#m-~~kRUNov{Azal@Ye&aDh(d~Ky<%ddL=fg+EB^9S2*%H2x7bSVOr%8dIeZQ882Iamc=A#zP{aEwYjF-0m^J6ton ze4D`@77TI3KZ!9!^2SILZ?D3ZDjuS-T zrY|jQ?AItm8HxWA0HSa*j*b;AjiT*psvcomQ?3>=kcI4jc*^TuC^ivG3}Js4XZ-<% z^-a7v-`!NIC(2KtfmLW7q;14;`)yjbyIXtpYSD;GZ)BhOXF4G}TRMD5za{@2)DT?g zD&O=wbmRzW@&A}Q^SGSXuI*oj$ebp2iYBCx47X$xiON`pR6Q8>+0;ACQ_-eXAYu zKyD1iKAx6ly?pt%uE)9(Lpg-f6(jyIZ*U13Ly%Ec{jWX!3s)j;k?s-6|Jdhk2zBCaC6RC(Q`26Z0Nt>cAtY)DEtw*8uLb4shGfop`LYZK2t{+(LC6FqLbsbk?bc zc<4qJl3LkgZYG$5)+`J~J~(_KM*JZB-lqoN`OGq=gPEB~j7nBQed#e>K7sL@+qY{7 z2tt-s8~F_n*YHE$8ieGg5>2aH(#KL79bq3nED}G_DOcSvwn|NJ~zx&{PKw||f6cMiCQ zYo^~Ha#bi9Ikb7chGA_G1(0Q$(~1$z)aT5^e<|2CjDWwhW(Kttl$QuV(I4v4;it7G zgC}H=<%fH@xQt6LynLWmkcK38@n~R%mhl8LsWAWNfLu{@u*>Gh(5{s6UGZFqLhZI2 ze${>c>W-gHX?n zNiy%MheE%s%vEKtxlcp>F}2q0uP$)Wbd0dy=q5TC6l~AoTRH|6(rNiL6-Jsl#OxW1i(z7EGr5$w_oNA+rGUrH73bh9Qn_e zP9R${@}d3n<5hhSk@Onz0Me;iyMACGsWuz4)UqyD>H5oeM=4@HUoDX5_ zct%5r50U@fzoYuj7vLd*Y2Jmh&Rx64o;@3tI%7B#(68^+rXW6HfQWdghUlmVl-T_YLa6$mJUs?8@6wh2-)kkF%6aT;NZQQ;#Le zy-YvV`ZdW<^vxVWsb-`t2oIB-k^)z`G|lOv7wnI?M$TRE5O$>E%^P7GMYFPfkQ6nE zJ)nohE0vMc7O&l=zN(UZ0X7x8T;zKVcwkh63!`3sR@0Cia{cLbeC%K!akflFiH;;s1*wl~fqN0F;YnFF7shO(B9kUHkKvqV@95#V35Iv;~A4Pt~fq6Y(1oENJiH?)$M`Bzo^pT(wek9G{Cl;jx zS)uEJqEomDuC^>61^hqo)e2E*NbjC~8x0ZW3U@}}{$sSA!!}*~2h0gN=vyyyt6Oj$ zc`^XJ3?7_STx{R*Z}gcAZbT_d?{xbe%Y{V2V4&}R#Z>8+gFL_^ZRk-1(tpAL5y8)u zv9~WpeLqem1O?iZI3wYXM0_UV(=-vHGcqd^UR(+wieA5d8eGu^?-yL7l>YxnU+U3i zhs|eQUqDavw~&PX4?Y9Ph8F9Dvea+c8nDUh$61{KTkY8kA}|@6LOK|CE|B6_HxSMY z68;%G?_4-*0R)2P!H)d&KV4h$uSL?1>@o+*uIWp<(kf~15=8{$-3_3khqI~d76 z;}Lw-!cI1t4o{rgm^QBv1I2;c>2`(hTm3?5B7Cf`VT zC)VfO2YL99O1r!wfmV*%BW)_#UJNww@LcuEUo>-cG(`aX&)mz7iM|6XP zG;Yt{RLcIgojPqoNV`$f*vQd0{a%AWya<|nefPK^W_UR2j=zX?!(e%V(*%KLMAvMv zzHGpKWiJXptZDSgCF0vOzmtG{Z;|DZ8!_Nip#-a%Qmq!Yl3)hD5C<;Nzi@`u)cy2d zbu^gSEfnoFukM(DIq*vEz~~$8lzc4I8!{vd7m@BgNTYpw*(ASa3a+w(=wvz@KMYmr~Ub{}8&of{m1%uO> zMV%$1a3I&J_X zc3`YzO;(m=(#n5(#%XD5AS2XX3^YyH)Ro>I^BL}Nbaxp5<t1(lEa(@r}}(M0dSsl1;v)Jx?40ov>ZBxQc~jOu;j{wZX|L6uynch z{kc34O_c$|uk^145i?E;>bQ1!nv71MU9PRKZ!E(*)MNAv%9k8NezXBns>%4D<7l2q zCqgWI{+YXc8#K8M=S?OMeUAhan$jST7R1Nv}&6X>IthF>sA}gAf1XI!gy0 zL(eeMV%p0AlHth!w8J?hGF&c=a}Ig;!|_Mz5BA^x_n$H-0j6WP=pwOoE|d50w=Blk z(({4$j_b+goUCRL8F`o69P4y~M4>%!NlW_84Ht(fbE)qvCz@ZPAwdVZ;Xh?j-}?g0 z@PJ**mw}o(0ORIsSb6~!43&GzEP6mQ51c)##d9VaIwd)`M|JvUQ6D8nihkUZpVhKA zj7?{V0UtgK|EWWwRR*;9!0@!}H2BBbO<7CcXz|3>5Dl|0U222?Cw}owqs%?HS^@a} z*!_8!*>ig97jfIOx3>@YUCJJwD(j79_E?126DCwyv~JgMPHLb*4@)O?O--w@V6jfe2fN5%%|@}9&L&+B|jv;`R`dd3<@Wo zm-16umRq+%nU_vD*5*SgN-Tgd?EQ70MPFp_jJA>pJxC|t?bbRDWbaBBL?=oDwKxzm zdj+ESw`*1#YZ^F?m5HQu^S@uHj0#QA#I~k@e-4}2OigX;xM^NEjc`_kb~iXfL_~|- z0$CHX+O)YTguqr}!qM^1WePyqh!3eLX=&=hA@Mw9iLZEyHT2+c6&=8C`@4=~! z)#h(zAulJyi2r)Ek?X-mr_RtgZviPU{fo~RM0f_up}O+`L2URmCSH)j7b%D+fHUiV z)s3wg*7>uKR1rW0u~$I5&y{+;$mSKjS2^H&=1P@C^!l)F`2wnc=wO<6A*59#yn5Zb z-AnvjLe57$d-d}u_d;5sqEF!Df;1QE9KJ*+ehuxJl@F}b)z92#?a*&s&AVo`VmK*; zFa44S>2&Q?W9J{BNG)cDjRC7ubN5PZ$e|WH%xraI-0Rlk3ZOS=kPfQIHEaMGqG=53 zUI?ANW`2Y1bl2dWGf(%J4L8Yo66XU<5~{djVj10IqJ6YC+R&~mEZQ@i#Lz}#8WEE0 z#R-shYMN*7gXSz=vXw0}r>3*a7Kytq-O?uy_DO>OJXki;aqDODws;%ni^{Vb=y&Nd z0#%Y7Biv;^7IC0M23&??u4o0t_IqX*MALv-arAbPL{e3xI?OP(mA?gzk`eLq3LVZ+ z-qAhgG9f(D;6(5<9)TERJ^vL~KwX*nrV@U)>Jdj?Ss_EAhvTNAZf8P6%&3-c1eEiX z+oD^@w(WupASNZ{pSQkz0-4RmZpKYl;~ar~Jt3L)UKeV388mmb@!knARE2K)KoUI~ z>$e3OvMmh4t%nbPKzac-v3A_Tu}5ImPPXq3?izRGbRV|SOLDeo^0{6Hp&1EdEC-Ri z0m8t`R7XnRpD%0D7;Zj2sfchwAIIryssPCvv29_}+xen$6l;9h^8UVV?84E9Jx5Uv=3K-`!(o;pX}4 z)9#HdeXZfFzpY1?xYsAr7Ql{teDmXwL)_S@hi`xO`}2C$#Q|SeO+R*S%i_0RS4~|o zA2fCVg>b2l_Xb)sVH-BY!K7@JX(cT!Ccd>B=t3*?W)h!o>%vSOSJ?rbZuRA-;@`G; zi0%RDQ~9bVOt!Zl!ijmpVbPDIgIjVL^AR+J*UqcZx*P{BbSFCPi~J-Tn}X)=I%#MO zS73tSrFsSHo+(iSm;MzW!HmZU-&Z9EA-L=eCrlb?Yb(22D8aR8!KX$Tw8bIKfDck? zW^72WQp7m~k^L8Tu*U+F4Q2+FFZ* zx7uj?7dC?=PB`4Ue)MSdoaa5w@K?9QbF(Fyy};9_m!@wRz^n_#ukD%U8$lQXoKEgh zwMe_b$JH6}n67$rGe*dW;UAt(_hu49Hbu4q9fME@$>vd3E}e|%{F!I~FEM3Cj~~dd zE6^l$Ug0j*HDpF&xD6&7mes*}8^>HnToeXrBZHeD5An^>vGdk2RTFI*K2|^`)-bk# z9A|I9A0r~Pn@yfvrux_mT9Q>?$8TLz^LbkJ_HBg3f^OZKLApOapM?fU!Q0aSxF4Ok zgSRWQ9cWFqTU@74OqyH+tDi8wAYr6+5Hb+SYIH;MdH-`=)!y6^#f1bFCIqXEG)^Ee z7fJ})yYe|eBS=VUtmB9AaD2G*GE471GQwmka%3bO&IRS~Z)|aW^ym>LyhFJe&IJi+ zv>-KTl?iCCHhP`fb7#pCrWfK;QpUi$-1*-unkpONS=ebiiq>q>DW3rmRpxI;QNWC9 zxF&$`Yo>2)sq%*3@jh|FhI6OE!WYk2e2}(cM+I9mhEVdxVZPvJ9!y!~lIJ}!i zk?#=mhm)5_pkqijKp)!nC&HPDhsWxKg@(H89M1oCtG5H~9$f_;{z0W?bHxz|rY;sG z*W}stz!HAj-w1z4dVPaU07N6hJYnp&fxNld)^N+Kd*7Lw>&62UF3W78J|>DO-R^3CL+n3u6UX(ax^p7zt8r) zZpOweXW!&xlaNN@-70nv4L^6%YZ&8FT4a9}0_XtyoTLf7cI~kAjeBr|0tjCkIrA3jY#|mOP&DH7$HcIh za*Ea}(zY>+`f6Uc0IrD!0>8V&(hU$6(wZeZp~Vcw;%Q=a?TG_$I%vRUD430jO=w7< zdJ#k?nguS7lTES)3O&5ae^0Q#c;WZ)7ct-I?B8?ZJFJiRqfZkm=xkD*v0qER4koDv zNP+2FZNEA}Hfb zoeDsrNI3aaj&JG<7Ct=P8$&suQ8|`#79&%3?AzCxZ^%q+LjLvMb*HISWgkr*Kf#_; zQtIQ>;%Q#20! z15<@b8MYeqe`!2{{?yu=FLVn&IWd@cKV0W*n} zg~+ObkjYgOukx6RSu=?A@Z*il`trQybJ!X+6tviD`K6kgDjTuM;>Xejj~<=;G2chK z;Eo?KI~cAQS~~=!a3bQ8*@tIE9blWPu!^_~JRrR~*(9cOTStAmC~X6tu>*c&9D4Rf z97mjTiU%@mMc!3A0P1GA(uNM!hT@7C`5dIK#e11`k}hczWuJDS@}5@&3A zvM+CU1(%MQwX%FKRJC<3O;L8 z&aq7D%D@3XX7Zaq%q5m)aW&TBxh6uz-e&1LliEXVMZ*t^+z)d&?07aH#3@bn?o3cX zUUjjks1;3Jf`Jg1RFUt=`Ul_>Wlt?9Z=F0-20rO1hynr`74sWm0E&s91}7jZDW|Pb zTU%ow%gaF%&Gx&$>4OxRK7t3-iSS0UtbtUDhpRG*e-uO$gR`c)zU16DSu-SnGw=5? zlMF_q)jK}l5&g%BX^uYa^)AbL+ZvJA&W z2uyF+k9R<}Vn+{6DkK%1e39W!J5uf)hsfd}STxzA#07XGi+$uh!mCI6Mvo+tY5_-6 z$$!(1)V3RZ(rTnE`2xQcK$7aIh9<810lVYDu-h{H1SlsPu%X%eMmFcyryhlS2+?c? zmB#tm4%d{LG?@Vp`T|dWDV6aqwoskC4ra6Fb$I2|ID$zF{XmLR_DD>RX(tZNG}f>A zzUG0-_zcK2pL-m)=)#XES-nGCl-i7C0i(CUeF!m&hcLa8FjKY8LxEiHu`c5zrOcnu4>W%m`sZ zr8~-?!71u)XaHmAn!s|Wb|9Ek98b-~5pl4p1aGl`NcTKDhqN}3pWV1!{pxf=cd+LrM@`VGAB@FbrhWunGv2-E}5+&2`SW7;v`Zb@# zVW{4G4&e{-JlCRifjxx{R;pp*xC~1;ODCkRNvFYn2OuUPkz!exAu-?xShR6Kkd>Z5 z-+HjJ@UF9^3mun zMPmKJsC($p@%N-1NW~IEUhy)GE`Ba0wzG)uSj@HU6m*tR(ss!Plg^On1OoBhk4Jo%1Q0x^H*B4X;#F~M zQ>Dg@HzRqUkKvSW--x7b09RE7%i~n5jnZ#%-0bjMrGRy|iChmSw$Fi38V5ef3-2ENdS%_09<6XB;|tGW zWIMTP;r7iqiOGgDhBmkX`g9v^(RiM|?G74s9&RyCX0rj17&i|AiCJ5#ckDRU>fmPC zc~jfn`xNvtwzS&dm+fc9AX*tdVuZX-wwKDc0z!_bj5O7qO#ZAoLJXalwR+W-ojbcy zUFb*J`ZegnoFf_><6o!W$J;P~k>G)vZgWh`jL`W?gcm887U4bxi;jrWB=s@pHg9+? zPrev6Ggp5usK$oD?@7PXzeVw68~|)~_^hGX@Ge>zD21!yEY{oAMxNw)`Fg-lM1ftK zH*1CzL_dmVzBtta7o2RKWQ-JZIK|)fzgCqxc<-gNAS)@6yI;wDw}r@(I6Z4HvIQRh zMxK6@a3G4C1k_#6KqLn^36cCr)M%UMX#R$anmjCit!%D!S?m*NGL=a24L1dz*qO6u z$CIwAZKI`0i=r-OHVn{{%1SGE;>z%Ch|ZQ%uC#$gK&QNJo|hoJnX(L*8yUQ|GK52eFf8o!KL8XU|Eg4bLaouj zf#*t6#T3aowKA)!fiAnPeA;eaKbxOus2ZA^*NS%p+9(0I8UMU5^HdPCuPA%%>*(kEN7ompA2J&DI4r)@O17eL4()tP3tw(xjl_l zN*=v^!580sH{Flz$#`P&NY~>%+hmTO_-zo58?-{?-Bnr*nbQXOR?T2SR9sS0owGyL z<4qJmI|e-C17?|PD#lNo7$=UscW+=Qc~U>r-pEi%DcE?=mG`h7+@^!ns)&sEcvo1u zR9bY%=nHX*2$4p+yVN0NYiNx%!&8aEkfplB;?wz`O>9$4b1yrg>P>|6v7|x%0>VzcCf}*DBF`SzYb!UsT zud+o7X+?^_MZUiA6f+YzT?rTd@IFUCZfIYz&v(nTE+aKf8)B-R={m~dWp(w=y9dzb&7-0mpY?&!KbfZ>njUea<;I$=$Sf>;p2qF#2sg3jNccFo0AP~Jc+lPxn|1_ ziDhKoF^wCC>?ck%gn5Qk-Itr6Vh30nK^)s0_VrInoC63Q$Mc)y-iz(_v%H477N zQ&@QZwfr=^&8l=KG`B7@_A~pc+GqW*Df5^z>#_Je{g+if-;RJ@R)0BnV%eZCri186 z+B!JYgs&Q#3C2H{p=+R`%px4wtY!lBBbS@RsNbp6{ssHK8JO@ojzIDErIbB102iUr zh#U35Yu>Dr#d~}`d$paPeIekN0N(IcNy^`*Jvp5Y{y0|a&eVx5CEn>mZ+qX03mx74 z!X)@AMl(8thz7U#e2ojN)r0l!*pb`vxRYbo%LaJh$`L;KCl-nF@FYIc@QQ!mR$nXmy1UW0sa#$251rXpK@F;ZXPfdTtfW#q=IcXrXMo>&1kK z=k=qtw>~W84||-vdw^A$9HrWM`(ba^dzt&aG()2tS{N{Zb!lGLTtlt(U)_pt0go`! zPt#_Mx|Y^R?&|38IS5V0V{4O9I~%`$V8+v^gU_Bnx8dT@l~2;^RxoK3@|l}BM&95M zijY_U3Nw}@k+w`HG2PMmEY13AqgxT}OyXdfMgDBlO^?wJuz(d+{og1T{(EfH zAb7U*Gk`&ObTYso#(&m7l)jAEcZAXl_tieC#YX4x0J8*iW}k?+i{3DP*Yhvw%8f(; zFTb*8`0dJBkJzmv`5Zi!Sb5S-bdrq^jNEar+q5oMAsm6rSs9~Cz!SOcjt$pu+i(8; zZB#9W`$FRpaQmgI;I=h!MjeW%#L34Vc|Bm-z|SX|+_Q6lfq}k$Ovx&w8rQmSP!6^d zKL!~4P(K|UJEnWb)>Ia7%;&zD7v>M&7fVCQv3eI*GL6X+v=%@ExmduINpF8za?6+o z>dO%|9(~2<&N0}8zKfrYE;aKG<}?!hHYZLSv|X&N1X%zT$lVRGqIJXxAchAy>Yu2T zPL`_;iT*tMExV^{3H4YcL?wwDe-1_;?rrut8jzqPY9#9Hf2uBv!jT={A+1ml{ z`B9+*>w>NH?j%eIK{%Gwce3vkb|CG}3%j-A9VJbVl&z>FdYcUDch>&PQ_tNu!ik(}x8epE`k~Mk+Rq<|1wJnZLPDXDB8K{QJ zeIl_9r7ijGnMV&CY6*W`7x#%ZKPrDxU46}BUEx3shd9tK%+XdGsfvLS>%-zWrpF`3 zPk2u#TtRD$zi^)hFRDh);{jNl-3kh#+unE7mveW=T3lFFw}W^*7Ej&3@pJ@M(f*7!;C}u&sDYw=gZ2(zWMhO*J7ES@Z9BoD|bS* zQE{z{q3Wv7f%iCtmL@VnvHeWRd-}fDqC;_m>z?XAiYV6?1K8dzH0rO{F>g6)W?__^ zb}19%SM8#xn1eN534D(rMYK8h>7oT&R^c2K#EqCk6j2>})n^_BNhC+_Oy!xhGu&TH zmoYQiI{MRrI!bik7dO`+uumFygUstD~TQ0(cUFt41zJmVJUK8B~kTqb#hu*L?%` z86dyqew)V#!GOh+-_(z#;_CM2pRNy+Vv<8Z2{_hVo><|-_v^f4=y-ek1BBA;XH$5D zq4zX^O+PDB*CghFaK;f;~fvFmkfNJxnFti!F@)J@N8=Bvlu3flg>9nwtkPv)vUzUsTT z8SQEc;JCF5v$vN`y$Q$a=I6&}0PQth z8&6Z6ne=p`p|kVM)S6pM;2oY?{N>Xoj6k>Q^U{6T31u8FhI9&T7epGcoiO2KNs~uX zDE=A~)mKlNgir|7Kz4nHje$)c!|6#w(>Jn{34RBH3L#G#2Ozwj&7`wx*0ugjxUr3E zD7ho@h0lPoF7mJuLsYl#-HqJJsXPL1(Rkax%X}CWJHm>(wsxV#$T4#G-ogyZbAjbb zj`h1kl9GT-Rd)HX5UM_-5xTMmtsNZ_p6(2JbOe{Q)jwBA_3!0n!*lY|2~BDOe`84X zz&gr5B%G$~J{=J;HqF3St~O!e!d|@d+w_TzmzaUik%>z()u#H_sO4*>e3et327-+w2_(|FqTq-9Mu zau0o9-U?<*&nQNOdoPYmJVD0*q!`KLHBPICG@V1~F_UfT@F18fTxOb|OOTjsZ^==j z-pw3cw4yS@|0Jt>Ng$~adW-pO2~=vVry95d;y~fj$k2I6T-mEiD zY&-W93Q>vw2~gr`h}Bn@=}QGyf|^sf31C3{six|ae`5VGjxwxt`#_rQpMa%3u0ZO^r>is{m3@{E0Ticlr;ol>LLxCFBFEBZVYaF0Jm!gyU zEq<`Ieq^nK>s_=QvFIpLTuwU13kd}{$jm50|JSX1_hi?CYG}mZpddfra|nwg&z@az zW>Z}X)r6G-2)i3i*En){i@k$Z?znN;bU0NDTDD^9(%La~WCG!?o#w%H?cC6>&f>nE@NzHk%qfd2b^;YXi6sdZ0L|8>xZPd?s zeZYF<=!v7SSz(Hoaiv~M-@2CiWE!)RSL^|a#l-kERC+N$ykrkKjp@)+?TK!ouq^IG zDtkxASvsLx8#47L!=3zHSv~{Xe=;I>QYuB{CH%X9QYn#--@$1~Zvk0{u>O>T*obZ7 z*(wxJGH&zcwe;}tkiAI%PW1;3(XBn?BP=w>i{Ym<L3#^24{_r->QSP#wRmoD{2MU_H~ut$DTE(KuRC+adA{S0>gq*5 zYJE`L4#nngPappxat$(p5Sn8LXasuhYu94X$}iTuMyCQJQM8Em8JRN6lH5n3c^z(fy>r$t?Yj^T#I*fE+{!!`N^5o=|b2g?8?mZ=dFdcuI_ zV|28c`6@7TIfVa{1=FPX>4HHI>YGNi%EU;BmzHecGoF<#ZP*cY5oQ6IcuTR$Ae-=l zC%vGnlrOq>h&oXV`)Y?GQc`09l$x467T)ARm|O^(+3e!VSmxkbSUbLW2^Vh{zA94B zy$x^5bC7X)F$^>@Nm_8Vld9@?gK!(yqITNaEg8x#H5-WqqWGZG9-RKF8VR3x1oK$M zNfECMS&k>mSP)@2u(ZazsE!58?#N}K_nIJdPx74?k<=504i=wQ#&+Lmt#@288Kffg zqYZI9T6r!FzQ2>qW{P!+SZI|EHVJ8G;*DUUB};Nd4ksQ0I)@(;kCD=;4!#yhwdm0k z$8)j?MiHW$y~#@6(n#}eqIIW-ownd{N1UytrC)99s-dSZQhxr!dN#j@o~s%4k(wQC zSt!PPoN~+oNP*DLOeKs`??u3D&L9_t;a}g0jVMrGTt}X2z2k^fw%AUl77t{X(|=7p zx+*+eKjbMzRAsmrb?&Uh7ZHya z`DM@&Cn9p~LP<_;O!SH3jW3(^?d`&&&o0yc5i>!Wl8E<@^-R|{VRD~QuC1y{8&D~g za}b*-P#e;3rG5^k?4h^TgbfGBIu2x73NEc-+o6W4Yu5Loil)W$B5CR94>Obs0{d#3 z$o?SSG?u}?J>cZCpb%z-XfwjKSS|wsMIzF9)RI+6{GyIz0Pzo*Ss7?hust4&ESCHA z6MrR(ksF6wrRS@n)rQE}W3s8#w^38(ta z>@2NerjfD<2yUL9!O&kd&YFUV(yy^21(BH#L$R+Pp4n<@nG^c-$rCpWxTHra3Te?^ zP~iJ^KiZr?=W+AEP_YVRx^Y61m&*nJ`i?~N&O5f8#e*5V0@sRr-DFx&%(KuS9i{ET zg^}}w`&-@(>MZ!^rZ56|^}oJa(m&W~G1$~`VRzdpsp24s=omq$1ZB$YAU6WV>EEdRVB8FWF!tq*B@fZoiBr^A{SMA zuLY0(gmTX=CgVbQ_qTC^dA6al-Irv5%raR?L; zmWyi*YlK5PkJ@pZ6V(X0mWEzCFKt5p{pRp?znjoIZ*HEKNuUOv0ug?wMANxNo0RLch zyFpzwvF-$R`~9eG3*ziuQxR6@%0t4&3eUDs86wHwT`xOh5Yrp5wetsm>JmEQQC`t` zS|!}iYk%$_gYy4j7qJ@)1yZdoTTDwXcRFI5TKS)FoY@V$E}u9Hp1T=MF?yeTW-Y}Dm-wwIO?EQd z``hCzC)Mnp_=syOqesl=hPN}$a6$GigN`)GjY!E(@GvOv#e{|9e%bna4Om#HA<2-iLf^uvpwcjzpkU6%2-ZNXfd_j3^o@YAUDFa;iBhJmmm$zTR zjf>2bL%6N^^>Zb??;=7ePfjL!k%{n&0zsZ9BJzY*P~}_VwhiDa$f^n^z3SVYJ_pag!A~9)OhRPI9 z3~N~(AC~N?iYwxv++@S8#7lxBdAD6w7Zqg^Zwq|^vn-2L2A6T;FWtO}e{)CL`@w)r zL8ZvxJmA+v0Z6ZX{ETQYeJsCh{OuR#x>F^!heiVohf^1xgekeBkBv06@i2gxvrR%< zbn9UZkd*Ve#Xte+Zndpys^>LLF_|WIi*F#E*{c>?3aj$Mv9*hCj6^`SsDG!=oAw=q;$FOZY6lO4%MS9kEv za3%!E=xflIAx+g_sszQ`ItriOr|HPr&{(F13XKW2H?l_m2s#K6Ss6XWY`!$p$q~$N z>l4rDDUE>hPpX<|e$;iaUu^)D-T{O~_{SOfKHEfioWsv&tRmmSF-7;EdV@vNX+edO*>f1Z zq_6Z_%xSAyd}vIE=)tSRBrgZLs2EA_Xod&);z_Bk^3ZW+L=8nIB!>=4Kz4&Z6tj`i zl(l@n>eu6#c}Pg3nbvlh&>OUnd&2jov%`;ya)Vt zpLV`Ia*UyTdgD)n-9^xb3QKkt-+M7h%tK{BN#^M2=BEBWm%KNKOR+yh<=`zcStQd` zEX@EC$Pd=}8itCI4Njqu7UKINOVxbiT!s(vi&(tzIZ|Cwwa9TRNl=51pc@rHV<4OQ zmM)@9=yJFjnR2p`KHf}MX0%qMRBvK|!Ya ziLz^ktlz8{1;BKnhwLIx)2 z>557~#9>5SaZVMVL#_UQ6Ar0IITeS;xq!e^mOLGhOS{w@f2 zICr2IIi`81SPKbxY;4Y_fLeYqFsa|*)j!swapa`0K>@E$>4BMHHc^MXLgk$?THTmN zB)78(G@#rE#%<5rUsN`AysS8AFaPxX%CD_yR?X5mJdzVB5^O4m;V=Vco>$d@fR*&O zl4FG{>79(0N}?X4EoezPXYRV_qY!IT^nh+q5@L@Ii1d7$A9;UXMNY67bJNqHrn_j- zx)1wTScuS>Xy#;7VIF!GCkg7bb@XQhx^`Y+MobU~Ejzn}lJHOD4}Me1%?yZgQg;Az zB@@5;ODO>sT+1W%FKr)PHS-?Nf#iC5XTbMOh@=bm^lyXZiQHsTgg6WDHkzsV^&W^i zSAtY^bqk;w+QJ^tylo*zFY@n!c0m+MCn6?zhTZ5x5Tau*04acVogrNz9gbdQw?ZWl zPloE){`?*V_J^k<&CL$lD-+Hcm8~S;0Fga{P2GHM6`*Dl+fiaWKfS=BwHp1WUZ{N` zMv;%3hrVL*Cd1n6c-IwFY9kPY$`!@gp2@!%EV&e{D?^3vGV5B?QLkJD#xePE?h$G> z*ufmitj@m1s%!<2K<$Rc3QU5ix+<|%TX&!XdiOQFqH6Q;^b;HMtU^${h(kKBuj1Uh zOb?x;TRh+Lr%#no2OzU){NJo(wNrXW4hEy_>)hyeNYxA`L^nU4xXeAX1|&d*gRR|W z&C2(3S;(Q7WW{m}khk0&Za0R|zL5_Ks+OD!H-xgHf-fU8+KhZF&z)PnVDM_P zJsDqaX2Sd-`y(S8h)Ld?`x;yu6d6^CcZk;u5hmekxt$AwhG>Hn$57^yWkK*YRDqXs z=Y_$pTw?z%O3sVS;K_g)cF0t0H1-OeQFTj@BifU@xB5mKPd;xbexv-4ny)oN7ZLiP zM5D!*&pDQy04`>0pE~ixBz;=Ht}6x15guigms|`X0=Z~l?@2P`g1*yz)~qH7_&H}G zzH#0X9~hYU)p1YKXITT!Q4$OXDf#2sPxNCONCXfy2bzg(18l+#UN#)l!-6at5o z@EiWE8KPjq3P%E|S9E)==5O`;=guoY=pMmkCz%#Y?X z&@7&>Xs-0q+vUM7^51*XHGqPFs@HBW;5qD3J#=qR2@1-rbxYyO=YI zbqEYsF>sa^%*PldB7av0DO4%>djVcyhc6*g7Cn`R&-genEH3a`@+qLW+}XPzv&%Z+ zNsXbrwOLI_@AtpzP`)ny3+K(J42wT}SimYI*Z0@yR7&m$%J9nw*toG39wb*Eo;ut6 zN+RFt0Ivg`v!_n{L`=5?>4>i+#nfhB?TDl$wgAfhK?lFC(A~ zD1?sg8`X2)7@U+^f_DW3$!p)a(@STgi4xae%=s`45=$}JnN?KesgvO1JY|aL-Ek0P z^HuZFqer_z=6}5Kr!X3?4Ah&8T_6o(5A>06#@Q4E6i9KloCDdPg5Db+w{wou%6If5 zx(YkW)#w#mbzvZSK+wC0pvU35Ep;T{HZ(GoaUY8CqCymL#A|Ts) z>P0JEm29dY-qUdII9*IRWE9sI8EfI8CL73{R&N=lWK8cOWttlx9-tt2e*F3FGMOp6 zC8Zpv*5}x4zj@q!m~4=MJU-6W526Q*xh9xt7}@_gYc)$9G8V)oCbGHY$c^uYvTGW( z!WznU#(gA(@zeQQ&Xt2S*xBPWV+sA2nUvmbWhWm18<(Q}zR@>S+EQkLLI*R;zxnZ; z!aEk$a#v)LCQuqOW)FP`S%pW`mf-_#i3}j`-tGLE;*Th*!5s=fA5MKNv{u5@b(`wy zYnR&Q`x14<_Z?YTFHHN9RzYh8;d~)fCBlMOf}bQdxMMMiAzPQ$$m!9#j%~Qa3@R+v zGPNe7WNZ$1;p*s_0^1B)MjE_iv3A46Mz*eW(?fI(5$NF3eV~`+Dfe&}FGHnlDf} zkt)T2Mn>oz{dO6Ts!0wbY{*VNcpw|fCL!hNE1!%YfE3(uqXgtSU7{-`2Nad``_BFu zsl@ZMxYiY|Ht2;CKyHWL8{~A zD!*rRTx#(wgq4>aUweK>1#XxmU>RijoggPXl>u>Z17Oz*k7gc92%`-2@SJS6t_OGs zQ-uwrq$v5Da2Zm*7H2FN>Y4(-X9Ht$m}4@5_yXpF$=L`kVmD^YM&2cz73WXyD~A2c z{c64CZGZY_>0<{dotFSc&foYKz@`!^MNtz`5m4Pt%Ha};F<@WzpBQ)VPE%(jU2_M{ zkSOX^2hO7Fz3N)QEor)*I`zK>2dIX}bAOsC@^W);GEs54v~&k^AVUFmC7G*MdPDt_ zU~Y2|MKz?b=gWmIK0$Yy&FXkB$8oh(6wO<;>P(fOg{zpzDDX_$%Ys6Y3L_rqLSwkJ zwDjrK*KgmB1@CR!z1xmDkR&u!U;jJPTGFYGi;W$|pNaet?OGSblk#%iNA;tg54RTm z;w#vT`HU7LwU|vutOOqJ>h@ITvT*^aCTf@zo_&zwM zvw!_;82A#%DG`_v`nLc^5r(U@rGKivvE!g9g*}8NW>YbRK2$wd9zKjFcN(z~d3F8I zrJ{c!!K<|`CJ)+S_@%I?|9tPkgWCXJWd+j9jE9!PtgRJB=H|6~z9g)t+(#v{?FA`h z(%{n5BS()mq|6oX%W5O-0-3F}9X4z&pEu89upg{-<{nLESuM;(OT6T+fThIi#UdC znC^AEtiMq7lXX<;FVxQc{%-&7Tls(Bb2DaFuY=b6$lzNc#pC1RREjMv8Lj^Hsk&?v zIp*b*i8iVFyn0$XLkLJow|tB%L4zgC{}F>?aUd08Xeix7(UcR2-vk+_lQ9C9zTBJnXo^wD%*8ONb5gPwE{!Zr<{?I3o?JT4QT zu}^Cr&%d_#2MA8c7exT0Z~^Nx@3(uORW%c9LZm5#u%0zPXby>;6cha|vMr~ZDsX$( zTyUEZ2D6s@@VV1eMpP6E0PBXW+a|(T@&04kKa3ee08_ZAvnIchRt^Jxf4_bB2i#3^ zm8~lUkknVuD;(v@!~0j~kTqo=32noZ5nbCdY;h4Sft#0CQwm}!lIR=l-2E}BNOJot zG%m6sLToX2?dmmsv11hWco)g*^ns)r6|R=4X-I;cqN0t*Oo~YFt4@;9Ff-MJ!NLZ; z8Jux0tgR=IWgSNj#co63vamkO)_&2NO=TzChA>uOWuYR3j0dSQ>DK3({r&s5;XxD% zT$Y$!73&z3Faxp4cd4wa$*TX^hMSy$>Qf;mP{;<_W%caZ_3OR%@%^-WWM)r!Xd-LP zk!TDLEue{@81m2~2UZ^;$dBZ*Jb%f%jTN*OKHT|u76-C`8>YYddzZX8x!8D9{a;h| zRegPRr-e|Q5R4?i0IojoZca2HUmt0W)xqPrADQ=8{BriXkSv?IBG2wKqaFDE^Jih* z0V4$#W1G=C68BKlyv0D>-**pKAPR9Yjl86f?4nEIz(7m=5oiMg-l` z8bj`~jsOeNoD2m@Bb*SneRpODZBxe*7dTe2Vy_{&%C>0!mWPhrD}m&=7J!7-F~ci; zT;Wv2@(#VonAa8nQ$UUF)4e$l zcWz`OI1C#L^$K8exRJfNkxatxNQ`musX35#&yM%5TECoeUQBl`@g;q}zMYD&*6zvm zMT|?p#2w6y&2X+J^}Gijh{+iW3r4e&GL-F0&&UW0tp4;t$10t>P^r|u{tPUc{QdmZ z#eU*gi+3a?QXb9vkv-n}jp2F*_=joMA7V|Ff~vOuna7DIFuWj(aIa|FaJf!2)^bpf z%^lvyJ;J02j3E3E{;&!2{H8lVI}6|@*E2e500q$$73FMda!Vq+6*NOB2>9-q4{;AC1k{E7bo11Z?LB@nmOIA?887$Ghv77F~U88IWbtd4umf=PmP%9E^+g zM}=>*idF44H(vi-_hP3e_wE;#b<}$`XTtdV>htdx&+v+y*VZNd&;3}Se02429O3Ko zw#?VJXv3nx@A}NYG2rXBho?_`$lA9PVF%JwEU%3-D%vQh&a79g_}+hiS(c8afk7jV zc`!EnxFRUMt50frZgu_F`q`A0vx(#|SeA7k$CbGN+v`gw%<20tUz!c~CSIq$+G`jz zVv&X$g#fFYDlA&-MdLxa4pF#p*W5rKox{^`KXOC;B27jixi4Y&Kd3TRXl7g^MndjMMzZ&ci{+@g|&`(oNlvpgI zUtdckJfQN z@*{Q*?hG?G^c^4fe_8*B3A%QAd~vusCbd9+N!kc_%rA3sVwvdMKrXmrVDBl0Kh{hRm> z9F2`!m-}?tf?H`){iZK?yl>9z*~clwj#l+;fg4gGYH*~JW{P=aw0X$rUh}D4Z481` zZndO27bSDS8{3-&0#g_1g!H8f9h%JXp2rra?q+6@)#gu3-hcC(h07^DeJ4Oyna`_# zh+59Hx}3(0DeA9NP*%2cRgq@`6D!nsdlN4vLrJ_+b+*>Zl|JMf; zWES1^^(#+Wva5V1mMJY2CsR}1AznRY${K;QyJ8o2K}IIRH*WMq2-s2~hHBTYU8{R= zUgp?DAO>ui3692#bhiwLOqx8oPk#F;>>a44(buPM-x=( z;sO%*b(Ec5DpHyJ_8uG(PvNB%ySVQy+O+W^#JPi9+<;{KN6vqEczE5lO+Qap^~t}B zRrxN8A6XkV(66@F13Fl0Y~q5TcHdz3g_m2(RADLH?Gg;Iv!)(CdNe|yD@?#zh6(q9 z>w7c4=E&S$dynqs=DP?C&3ap|__Tg%#=gHgg~F!2cLO(Oe$!WWhIw)Ih&30|)MD^+)d@hE=w%ZtoXQ)IKE| zX!uoHr}?~3XUaJ)-@oT3hzGihQ`U%Q7n=CUbMj1CE{cvJ3|5n6tu8KqGd){3J#&xn}#&e+6cH`uHCR($vY#n@vjr|4Mf zf1w@JejG$Xs)fa^_jtf*vj=yo)ycYMCdBPE(Bb0WP;6xNOMPa=UO25u`zb; zqIj0{Cq~T3)!ewWt_AvIls#H<+C^&zOS_QpzG7~prFY)m_q`t^^u(Kj&u`SdJ2q}fn2R@_KK=2Ez5fV^pf0K+5bjU^R@r?2 z4c00)151cm2M&kP)wV$kW3W#I{aLZG85dFP9l9PhShbz!o{^DJPUDL}o^iU*hN3{e#D>I^0Yz$ZrI60vx!=bQz@AW4Hy(th^#d?L%FK zjID_M1Bc^G#PCen)WbsO7QA0!Nok?G+9dX;@CAj`&U@eIn+1h@y~WqSzN;jd6S~Wv zJSj13uAvZDbF#l^VJ9BZO^G7Wa`%D(OS61G))Cr7hz>yl$=ew02eFEYNX&2zV|bJU z+ zQWuEBt$24z(~UxaQ^VSXDvK_p=q^^|X5zVcRx+*Bn&v44=3<@jzIp~DHVmN?Ap6!R zs&Z&uAYUrC(EQepRDwt>gmOu|T+qD|4W{|6IMNL>@M@zX#!*`d2@8!Go0g_7oqudK z%Th1Ydjif%T6LSKLG2#GJTnpDo7;1r)kaXXC;RVH!_5joJ6-AM{WHNng^c z2f*U`anz&VK%76=`-%%&f3&q?6vf|Yucb9ZWZY8muKb!2VW+GY*G$-88UhQMb|f2dj(&4ub`#jY5UM6DHk(sei1@NBGE*W11Ix0G_h@Y2 zfy|N6-xuafwf!a=JBLgWpd0SaSNFU-6MiX8}~mtYz5;)fp@KT5*=h z^j!jZO4G%J7ov}1PH9uYTJAkhG9%*7Jdt#e^3VtkL9QcCE+PX!{zk#WcwtvC{}tcS zR?2$X9iX@fLOw5U>P1`lHb+pHb}iCx1Y#U`A`1L`(c{oZ<2?H4g#TCzpYr=$(k5pI z)0Euj*J#W_XjtQwUgn@Y=WimH6Vf6wg=iFxR+Ym;%cKH>U(6$2kteYgGcwYj)Kyew zJP8~AkvP?ej#_j@aG4_OAY81c{9CAV>=Dt~jq0VgBBYZFazCh8aT7&hLLM5^Qp4nO zJ9a;ffX>0SVIv&#MI1l4?XCshrBXM+oQf3?14BX-v%(;NJ8p#|61@7duWxnA%!nup zqa zAGVyaH&+|IxZ*eeoNEV7Mcco^LZs6&1HouBQjWFN=0OXe5gE~luH{G47iMiou5Vmc z?(>fqt7=$jv=)WTMd~uKhlUp|22dQWy0ho;{^!6hF*@Y}XJb6Yogiq;g5dn>pR&SR1Z#{li=dGD5#RVdO%|(J*_zxzkSmUY zFk+mo_TYA|cJskl-S@p^E#U{GD$eF;MWu5lON2clvk0|qP+LfjkX1;u?D0+C{~22% z^cDvk?c?q&CzSB9M3{~zY}oDYG(_aP^Q@Up8PZ>2mn3~Gs2VGJ5ifitjU30HpvC(PZ=P0?{v2wMmvwyIhw&zT2!lFKJu zRYbf(95K6E?GzU(;#82g%;YexC?@qhQ!=!_th^RBg>jpvutY58SfumPl1bs=nCPP5 z%l1{b(kddj%aczA7T(s*%gaN>FC{gTtixehB5wZ~v@rg}302zfVp1jIDYOy$s%`yM zi2;nei)0!4ZWvlBPL8xVkH+3f0=&xZ9%=*8dcrE=s~X4$S*)|MZ=;FHy7jv`Y2+rH z?V7zJ^_hrEq_MzMhQOeRW z_R&enLsQ=sdy36ScxZ2B6G;e#vSSK;E}iu?*mKWX897Q>?@2WrWfj38V_`)@0WB=~ zh9F_LYXr}tnP~Uq@pE{kh9|{CLOml3w;+5CpE=V|&<){0^xxIC(^Gh^h@;}TDw9|i z8wjf~>w0uErm3zkq8i4oPK=V}V9{6`aLr12Cpk!F9sbix6S`e(R0R$(k%4#qGETSX z1mtVWVg;8A#@)KDVM17xf@14T>?5O#8mX+_0D`#ELhjctO#*i#&L-9Vg}gazHPCiAGhTmJIF*h7Y+b@Nd!?suXM1I*Lh9(uCwha^fqTH4x>wArKrp?YOj z$a&B1apNMp8XAgPp2Q#hL^o*e2D9!{&#FUzme(x%y)YfOgVH$Cd;1N3VXhuNh$#v&!3)9ClakZIS zgIhw+T|RX98WT*2t@6@^N<#S{Fj6WMo$V@CwvB!#Zk|5%01LP}08q2ZkaPAaHISJr z_K&uZfi!mcF^#wxUwq&09|l6Qh<_tF zCm#REb(l9?Psu9@gqFuMLWLfoLaPwvqz_j4a=d68Fvhk9H$*;PMntKxfJ@O`BZH#d zs6>VC_w}EpQlN294<0xW0P#eX8|yq(SRa8_0aJ^LK%Dw@MJwF&2?BLQi^dRYf6-f>IZL z!TIPqoG%2V$zBbB`{lyIAUa%#mE`Q^*rx{eHHV!--XgmXX)YE}{{dP6pNZ1kkxb2t z6R+`~-+hI|B9icsRa?GzRfLIC{KOHCm9R|Fgkl{HLsn-=mSQFyU~GyD%NExAf`M+J9Hqd6=)lkq#PNbdTJ z#)^~*fZ&A|clW6Dge3AntIBZ^nZDQK7A)eFa$JOe^dT>?Cgo-mJC;Xo9o;IP=@b9`53H!yqG*s>4iKr_;`T|Cl&RF(+B!NFPoD-u zD++3yw!dFnc^(Xk$WFZ9B=PfbSvW+E$F{$R?N*)VO)(aguu6T-#QmAifG(LYs!~!? z1Y1b^fL8aM^1uC`RaWZP4wye@&b=4aYPZxut=Xu3@OsQ(aTtxo4ucv&1Mn6xQI(bR zFck8#c{%ewb#L_s5>3RcD7#mDc^wMg$}TM2a8$>3Sk;=gIcq3t`+$3etYWI~9!Km< zgmrrl{?lv!`npbg7&B(Mh!v70k1v>LGu$<5;A zQ>S(SyqZZFO6EugM;6|{A0_q`uoGrTr1D8WcWIcjf@L@qqtIY0Q6=z1h*N#(@q91T=$y>5zUU2 zcY#VR$O$b$RYzmnD&Q$Hn5mS~4jjpdm@Ki8;T@*cWPX?6vbTB{59C)YbW#po1|W$w zMQKHOxdP=FV>dghX9o@2KW%@?)N`8PNC~1cunp2CZ%-PC>52@^l3NJQ8>-FgkDNWL zg#t(R|A_Pka(K=LGf|)Op#?qg3^O9Ee;qh~UK>aOVj51}9D6K_5>+}0%xItp9u|9S zob3dut*O0Z;7GYjdHgsn-=7GP&3$85k)uKiyWTbt%Mo^z*_JPCwsuX zF(X|1b<{9XevbHXVP$SJS+xhZn~hDeEU5av=5FqvD?TZ6iA3Tjxr^H=)c~`CFabGe z^klr`s0VQU+er8u88-M2_KJ%vMeJWbKbKr7!__|^B%~<|{z56HE^(B>{D)HNwJor) zp+0DYp&aG5kIwT>G%STola)_p4pC_$4MzHHi+KT!>@uIr(~$vMF?7KLx(U-bV(J4F z(X?CE(#WKw>fk%yDk=;mEIr8~LaD4?y>ZF@^*5dyFirvcURYaw4CTrx?r?a zz_{10{{FJ-dB$x?C_<%m`03SAuYS}0A(4lwxDc-ivdJ8hWIoV5n3!bJ73_m4|6-p~ z8KCSz0c zAF+8TE-n}K;F*Y$L@L*voYGXls*ldoPVt8h^;W#*Gr8K#Ie{a}MJCLzaDIKc(ZcPzWZy%N%TnRXhmUv~p_s$5}%6@^GB-6pl@v;^>&q*v4IY;=8$~ zdzfs#cJ-?Fw)74U$R@jah1HL*{Wi7xt)wS2t&hrmSHJ$<|2%z@G11| zn{$x7pUf4K(IONlLJ;9?8^q$lTZf096Q>MXRV>UpEn;)rk7**UuQnR91ra6q7R(Y( z_d0?miNtf(Ikur(am`qGu(;H7gKR=(Lx|{I&A?zIrtH~#sFFA(HREw-|ze9yN~BMp1s|ve&6qP zt#z*RJl9$fY~t`H0YGobJYxb$UAs2=**ma*$lep_Efpw&^&L`aORtcX0+|#@(=$w(VY)MM%&70j?-RmH=2C$8p>(B{xxIG;}iuCH};ZH!~ z#o0TZ^WfkRDqdGSy3&4s^5oK(VnAeQ~q@fhk zx(q8IB%vA%*6G~&vCe$79+^-ozHXUD5AKxSJUVfDCL}pdo3@z>rU-n?T4i4z4!`LJ zjwx2VfD+GuUoE@YZ++`e-~y+?_lmlJ`;Bo1h7_K{QEtG~qE`nS=s_2D=_p26noWFlz2~Xp zE$5w-t9YJQQzR>~a7|Jf)TNx@shMHzo1<%FL3sjZmWE-B3Q~JboIJVFN=FKL7bc_| zE!fY&3gW(z;8gG+NcWc*w5UsCNLZ9iZRIi0(HbnaHY98bq?o~qji>5~O$MH6>8Ixd zVW^h08y*GG1|-@5IYKbXSkZP&*-szYPtC#a+I3S(?>)OX*A%Mqg}l%Api%!st>(>@ z0P0*EbiLTDIj&8wp5@B~zmUQR$7U`Zmk0t~bo?!viQnhLSP`6-VN}>&~6xA$PM|peG5EG%chMOC@+zsVu*X=;d$~^gq2^-HWpS*o+I! z(0Y5GdSkQvhNeIH5IsR=70{_hO2b+vkQCqS28<-*r@Ll%!+I^9tZ+#1TvsK15GuWX zzOW-2QP&sZSA_X7B}K{RGy1=UTkrXyqKT^-F^|rN^%OW5EE<7WtC+S(gq4~P94h7@ zj~C|ChTPCZaX!HhhEut?tx9sK3_1&x-f%-P;I0 zTMiy^hmW`(D;f8oP25de8@+=w>(9FYk0DG16qRg07Zyv-Cg4Ff1(bNkwr93v9Ui>j z&T-snp(c5Fd}Kj5+zD9bomEE=4)o*~rzn?D4rSm%0+@ml1SR%9ZM{7sA6r7e2;VCf zRYJEk@sv#Ixf3ddDnXf}M5I~PPDLUAJ1~~^g9!FH!WCZSkxQqGmtV@Jf#fkVy3Dcx zT^qgs-%bYE;VdE-^`3fi`;Mc{5J9LfWJkD=fG{gJz=XfQmBEH@1glD6PT-L36WmA@ zE;9Qp&L;h|0ascB?LCLI2i;4H3x6)VP=o-;EmNJR$`Ly*iUS|Do<%rt15&sSk4yXI zb3d-#E8?@??$$JKd*Xyoss1!Lgw?1NH^TqXoC6NKum$$`xHQ_yeq4kZ+1@}ga)bLF zZ#A(yn?^T^4n`V!fP;^_IIVR14|Um0AYlyE;lzR`ebCm(>k@#es-`D({sYLM~%af^AKx z^;uqnJ&0dU5tf1Vs0E;A`hxX-hJm=LWqmwY&htw_9T;O7{KPXNgoUnj-L`WJtl^5E z51)PJmjj>Z_wK!sHVDc3;EMjYb&Rt;D}9FYY-RZ*&DnD3^n3KM6hw+?OTZTDBcv?^ ze1Wh;XWYP^0~qkiv>qA8My5_j^x|d5zG!uP6a!C$2qAFD-y@?7m~-&SJx$7-2^l$n z>W7YdXsmoE7 z?}OMX-zVWp$Df(tk)@DpvIGeW38n@zRyvK*a((0}Vt0X9_V@+{(>7hP!dPanaLIMq zH;aKr2tlHd+j*<*+iAR&;S>A};Eot#4e1{1HtnIT$#5k>NQXXFo@?`VJ|5uqj0^xo zx1y*G_WFAMf{gS#bf_~PP};*jD_48@)c!VUMH4J4f{-2zQkiG$R2{pb@Y_%67U#MX z6%X=|ff(x^-95!?k=g`=$#Vm9n@W!)EE&f|wnC!t>TRU}Ouq(~;N1#-}f2ni?v z8B9}<^XKj`W?J8F_D$<5;P-_!28!`2LBPu33Y;afNA_W#BlT(GwLm5YRIsYh7vYY( zCdV7UYO^z2_OJ!YVOBG98FGNUamq|7k90;f6M*OV~j8n*KpZu z8_5iWTaO8e2c!a`5@39Sq|X6&%H};-Ie*?pCVy|?eH6;TqG9WXg2HNv7U_tU;t;_~ zqU=4OD?$x^6yOaD)$XtJI<;@>p#**|Y)=6BS~z?>LRe`?>TiR8{{{yNVZ`J-S?Ptj zPt*{qD>Zd>IPAP=$0jlk30;74)*4*}u8VRn5aLnH2l+;K{I|9W*xWMUR~9jODc$W~ zJVi$`q zG$4AQ5dS=+#EKQFh2v2qi5((+6VFm14P;!?jvWt&{l1K=1e#t7J`NwBZ8(~ZbRUSL znj_QGJ)-pRH_Cl0QUZ#iG)~JHJ_O{L>X8Azj5%{($6^@ULim>6JwkRi2sH6jp&Ao?0VwX)fcyS%3kYVNE={1sBO4hAo1DbwSm#~POw9mx572rm8 z4KZ!20gMU1_>y*EMt+i5*(Ak9N+m9xTvO?HCP50W9){al5}A`KyA4;dgt4>!NFKnxBd?un>_2kWpz*?Q>S zz;UELP*2)V>~%sr;O%6%(olxgMny%L4r{IYb1qr+^+;HIQ3Mdu`Mjotb4Y1AusmQ3 z)|J^0&+Y>!3!bugFti(CmSZm`f>B-dDVL>@8@HtxJaU&d=otH?Ywz9}Qmnu!5+g;G z|IfAX{;gB@GQ84ac;gVu_rPP-P(@|qmCIetf4?B`oJ2dK0&^x5g|1BpDJru>{TjB< z6MS-Ohl?kNU?&k33s)arv1=>Z`sD}CgVS#Z(;+b%ji6+WdiyXHY463$PS5ZSAlZjC z>gGw}*p~ScKyAXEFJhUDuWZ+D|CL|umeGokmRW^XAhSOtPP3_59ww6);B=NQ>8vg$ zJ-=^YdO1c?2Z-X2mj&C!L5HgWKJDa8I*EZAX`fgqo00ykYNAz=X%~_n>8~_h-fP-! z3Vux9dO$Of9cS}8S0?Sc#v!}diCd!@N z#a86%`<37R3{9}%C%lw;IOw8N}*Dlq1=*v0t3F&}qQ{Hp1&*cU5 z{@^n%Y);Lv%!{@$#d=$uhl~lLLu#(OP(ZifLziD!uW|ti5#pM8Gp6)B<-;hiAb-G z4-fZ)633ywg4t6Ar(bQrrB;5r6UKTTFBY8uo*@GYRVef^_6PD9#TB75_Rimj#CRud zwcu2FYm&QAsn6uR9VQ*gXo3cK1}JU%f2g zYQ!m-nn1jeM~UGRYk@2A7=bx})K3U*v%qtWtM|{ZaRokbw%c~^7HkbKgT1C_e0a-_ zqZKgx@r!X#m}A?NjxA6x&X}24Uj3YAacShSW5XIQ@SM^aSgrbUsQQv2iYLh_fFlL@ya15(u zqwldPC+o^jp-dI#7EqX5q8(tglUEF7C;QS!O`7=X@Lwu8BKVXF_##FIwKy}dTd!W$ zq?jmdGrW5cn}vhJx$0!^X#u5@X}=I_hAEgJtMT0pC0`^MRSqSA_N*?a93B@5(NQkbA7_&%YiVIH?M zr*g~rN(V43jxrPH@qCXKRb{WlUhL^9wnl&}nix)ne zfktgc1x@aPsizM<$;*>bQjFm+n!G{C9jpy?=$BYZ zaE8XnEh$?)q&-D%$IhJt!4@uj@NzF7b8VUCM{%lP?1Q)uxV~Z?fnyNk9a3!C7(ZN( z9vB0d#Us8@A{xhW`rR=yicfdoz^QioJiou!pkpEG*fD9%Foh4H7<>3Kbn6X^>kZAd zC%CvAj9P-TG88A96k_;O zzp#t~y&&JQ`p_gTxqN zcPXli`K>M-j3gHRQH7w7bl0E5r!WIy@`7uQg+AmE7B@h2I0|am!0>$_sBmA@J&?fC zVGB~V@V_wq2$Ewe{4uR|V&rCKMi{=xtaP1P>2Tz2iIEWtr1tDBJ9z=}`H7!VL|^q3 zM~nF5$2ahC3ToRG3J@l(JixdPc_Vh%@@3C40*RSf9Qyc4d`0SmA1z~)9?Qqm@#raU&Dv0u;KVBm)lyL|!; z15w=NX46zC9|pIMbY}^XhTwwr0Ny7y%GYq_HC4vzMU=d!J);12qAB}}{~a(U&L1=f zDVp%KomF>t;U~z6p^uXYq(yQP9!ySiY4chkYYxH|P;T&XW&WLd@=LJ!QUpg3iqV}H zQLIr(Wvsqf$Ntzm4nIP=mf1+M5D2@32Jq=v6c_qUe^w1z01 z@GC-_LAb+Mv(`5zmX zk4mS+4@6NSSz+!!&Z^V~!sC~e1h?#N?@ST^O;;aswdT~4QpE&_EOC;q*=}Q+-;?R2 zd;rjBi;sUX(LTJOhH_hAB*#*Q{xC)2@ANbIpTah5%8yJWfko-ddIV79(*`4KN+h97 zI|cF(I)kggKhiXcoQ(ZuJw9X85hF%?vG%`sD?;3`lydc!8w}07R{7BO19JzP-%Zz~ zWfMkti|CUGW)Iu!Ynt5yOM#GRoHg>B$hX3G|7&3r!neJ`tqzx5%ysZQGXG2{EzZF%;qg15#3c_iu;OvI!az;ZJNp6vbr0BVM*6Z(gY` z3a!HIFB2{llEeu7XiibQGf7VH($boOO>uY8Yo|oMrPMdz6#Urr7sDT76HcR0t#8!; zyBbAIj+&wQwBCZb_aMSaWhV=fdAJ}Y+`Nh#npHnatEZNUu=-!Myk`^PiNpVj$WB%< z!=*3cI|Pfu7uYI3LwU?V5X81tzXq!h(F-sVX_=;cWe9hyX2SXLb0}HF`VAV0y}F=p z1#32C$^>volq449A#*cK{fT?cB!Kv+a6oYf~x$hq<>33OEZtGollm*J4;>e6FJ z02`Fx+v|OKmfn9x%X+{%Uuo(=iPdP)QznN|JIWkZ^3}fA?xRU{Czcjj!S@HR3QCVY&tUl$yYTRbGZP*YKdTr_g`vlxPUF{gY9v9A2}Q<( zZfMHf2`nHIG4O}3^QwjRU}wX}TBEI%ROHYjX_%8x{FR1AhK-GghoR@s;v>FQYGXH* zdKX8WSPi5Bf8@>{>$$u#jfQS1>-!I%&baK=l0TL2QNXweWYC&-u9z>?X!^N^29iaa zj?9`MIxjkFWs_|%v{a|^;cc4k8sWgQB$*TIWZ)IGWDKeUc`lttT}Qzq63$`Emd0F) zJ=fccpKgdxWm6Hp7A;DMGLWHWid2RT^wzhP!^@lCetx%8>G(Py=Wo-ma~VD+#-;k# zgOLK$g16+iHV_m?E57t=?b~HEeBHiHW%0XYd$r<V=68e+pcO@f9m|fui9#;w8$7Y zILJ4tZBr|)h%#TV0RtNAUk%zEq}Q-dFCG7e!_C_5EQ<<{wmX*{`l&i4&U?Gjm0^{a z!$#~q*VSXg=Xc33k4_~{zckwM`a)!NifHA?+RkJ?EE?@UbAu!w0TTUwm2W7tYD9%Xw1S;{@yV z`^Wh!2QOc4H|_YjXG1}1H;$iwImr2)bAnhPVU}MdL;6VPJ{$@eaQmBsZq7#@xr^tU zC@LrIOU0GicHP21Y~CUT64r}$GLs&ha$wDmsXd%hd?t=k&bet1wF)k#j969j`S8SU z@*IgUv>6^`;0PIoX)0wd~}Jom^1N zj~^>r3N3+meixz%cr(!Z zrjac?nbvFj2K0tS!Ka^``W@dVRz15`*-rS<|cC(nQUC-~c8bNLh(|nYRCVG`Wp1!#7!n*@~ zwfCAIG@U!Q3)0|vD3a4V-sngzbin892sa~b3<+@=r(D+v@J0_SW4xb`AH6Sqsr&oe zu-b3`v{{x@ewgjTvl@#uLcoI&604^|21yZ%hSS0Zc@mXdB9bMx?q8KfVQz~T4+m8K z$MaAmIWGOO@WhBZnq4cIGaDiqlr6fZ6`!5aaD{_l7gCn#%oBb=2{X8aVpF0dm$|1T z29jufg{P;dX8qn0!#RL!&7!&!i2@lB!h|-oG9`$pSIHB{-^MwFpA8P6_}tQ&TY~~e zQfi9yj2yUGSt=6YO<8PntYq1S-)<57F5K>-?PG^(JZXj4mVulR=9cpY%gG>G)N!RNIv}!%mGVB`2;Y#4!{7~NNajhqt6R+v`eN-A zN)626eKTWa&)d{o{Gd%T`$t@_AW9};KmJ@qF(iF@qD1W5w@<9V(p|#m^EYr$er$8O zlUkROxcU>AaLeaP!>)1N@-pQ=6zsNLyMlmyFnMQeYj=a%{4<#kynU86*S_DrlfGrxr8l7N_-Ltjg1@&9~+A2`#{Ry6rwX(6zEx9dOp$XQVss{PMtrEI(g!Rob}(o z-VJl*nOY1uHGd?E!As8~^-JHgM~a%n)~hvEv#&tnNxJu;VMj)c^1a1Wc*;Y6_e3YB z-lzVG4PoC@?`V$czjM!|RTD2r?^B&8c55lz7bYKF;3p`}6w{MnXt0(b51e`AI>gcT zpC;5>a$(qPFR$eH-8+Ua{rmQ(L4yao(gq+*E#EARvrKGRzoy$Jw*iJz%q;!K9(@#j z;)LC?=UeM)0oERrl*`I4f=Y;eyl^GhKhsS-wBHzMt5YdJHa%Mi@oF)Des|wH9Ul7o z`;T6P*03PO#?s&Xa898NA0~|WFTPUKO5CRA2U7(UB6^FjlIt+H@9lA>iMd74I-Z{| zSIFs|GJU%1p9ZPIjhVOi$@!6P&dLI%U%xA^bnw$~+Ao)!px-j{#6|7CbJQpV@9sI_ zU3R^7*@vTvd(@`foYhCZHUp%pGVA$UP8|)8>k_Mpx6eI`LMvYd_a@N~wbZ+K&gb{f zcT>)~x8%TxkRlc<+qhFs83V{pj%&&%$rw5=^c0M2VC4s&iyoZ4W#FHBV)B9SCJ$*? z#N=z8Z^k=NrJ57rFj~D3`F^K6JzmMQK+i2N1$tv9;^{ zsPK_47oI0Sw4c@yMX!hv)6Tkw-@4mq0&ewChM@2&o-avP2WhALV_1<3&Zbi=y8Zno z3YoO9>gDQ<+h0vCfE)9CyL+yvhcZ`{jN=;?7WO3U^T&_Zm^UN?GuqNXNdA*AlR+Oh z-+VP|J1`WIb5eBi>B6yH2j9jjL5U8wXf-S*CdR7ydzvlD?AG7sjKbW=7=hQz2I7`m zS`^xUCX_dIuQEkn;G4FnhQe48P&O@CR-AlBafcAjw~{tVPtT;v_L))jG(vUN?3-rY zjDF-UE+>LSP?J4Gd;!l~?pb}bn5!i(1xZTQ^T>1%2JLk1plz^!|9<4sW^ZCCdA8x* z?KRR#6_&+t<;N*8e~;&?sH&+2(D~EFAn5%+QpSLg#ds*|sL0@C9VrjQ2D|8DX+Nq3 zwF-my*5elkBf(k3WEI>S{P`N>4QBTlF(V!^4^tlJ`;Q;7A=LkL;az(W@BC{$A5dGR zbMeMetCUy{U2-9x5+LEp_6PS(D%xWHeO{V`XBY#~l~;yNo4Q2$G=t`WrQDcR+HgbN zKD*WU&kX-2LkGmVCbuG86kKoZbd4F);zOp|aE0%lGb{xZB<8O+i!ZGwF+N{*r>96_ zpOT6ikGba9CApvP&b79)L$Qio_v@doRyV!Awfk9ZxD5@hS{W0MS>(HyiL_XwiftRn zEQB*1duQfN*cq@xJ$hodLgrQw!m0)5*5N zb>6sPt+=xDYjIWKj_xDebc)NpxSG4vW+K7H@LQ(7`P!83Bp$sb`VeL9F2U3k+{9(R=g(8wPOsM~_M6e&PoJt@*x&w$ z6D%-1{L$8s?Llsuf8NI4hcj5?ZsC(Exq;dpa!fJ9(WQC&_PzQTN(&ay8Q%G;e|vS< z$3enfp~YgYwSE(aWciohe4dT0*(xhDe9~P{N5?l!P4~k%vJ7@2t1q&Jg5T*HX-SaA zbB~fb+|7xdnT zs3P)E?<>EWF^$ur&zAXOjdNUAvqE|j_ynFmp3IefC#Q$5D=GJC%Mem1<#Yf!NoBo| zRrEwG`0X9~evnJ=(NFiD*6cZ8K+?0aJ$v_xiDvuy2J!u`+bMSjkX*akdEU|q7;Ko| zWqEGb!;!>(F{??S;(!v(tVeBMq#NDwhI4%lhl{aK<9Kt?`zFH6lBpFle%CF98N!dr z;DODNa7<#}ii>+=5Lw3|IMz9^uyg%>YLh20G0Qw|{H6kSl${^v>|fI-=q81A^Dzg8 zYy#=YjgSsw*d4m_oyAQC+BpBxt2?(S(EG^u+7%ui1#AY9wTjffbCz|8|M-9x0<9d~ zvAs)v?8`co**EY39-P=M)V534a9i>JGylt=dKXm^6^1@7*B$G&64DbitnBmakkm2E zfA5pL5OOr{+`=|{U+j(N?XIhSmjC6)v&FLY^Zt-;YQMi99=7{Yz?9?mMW^o%O($W= z?o=vD=}ud)$HaC-qdNj1iT4zOS@X@R#vHQKM?mP&DqL&K(~2EEr%?Y9-V2}$2e8ve z>HtF7dyhdJ)0sp$U40?6F!40G{S#BnZZ#Ve+^BQs?DsjDn)>SF_WU>{YXbJ9t?O`f zh6tp}jik~e`~-PSPaokL-%4Krt+aQ+KW&*()27W{m!XZ4TQS01E&wH>bXW)<^X=R9 za#YfjUxDF|j~v@EaglA@c$uqDcW;k%)*kJ)+wc0#%aB~boydCpSTqvQpKwQ*2E=Js zYp)hV{3v?6cX7Y@`6zjX;gcP;POksJN4oUw-eAw#nu>#u+(Y|V@g1np=l(cJ`6{X& zoMrUs)6Hc|6uXOtYNmghgAnUk92cRp{Z^EnKHi*OcO!?bvpL`$n^jV7ck~gyO{4j> zXu_9jemnyc^q0k$b0gQZf?YH!S-u?AQg(E~s+jhQaAgLo=dZF1q~JAY51Uy;9X@4E z{Tyxi&yG%6XU~jVoPV6ywT*YjO{D6R*CF_PcLQt|xJshOIrE5@nBLDkG1hvlR*pk! zt+I=H8d)t}!A^gUtK5;=^+O)&%ZLgpEXFbu9DJ)55dae@pskcb=TK# zI=eeF$e5i*XxEZba#D3-VO!jc4vZkR^(=W?mH$hHj*)P6azH`bz6Ko}L z$K@ph2JLSwya!!5GUAAN0v-l|&oURDe-Q|+mCndkc>}Bf4t%|O_#SMrL%u(-u#2CV zcV^y%#Oj^l;Vvw?@O-y_l^Ah&zcMpwO~0T-y#9=d58*V5I0|r@!fI&V>yYUDqTHj< zQ3Tx0pRK8$Ai>4sVMmWu!LojnmMl4;Kf8rQ>CRQ%@;e#tWK5M6S}vs262Kn8-y(jZ zu94Bi9e#h`9%B%sg^y{oi+UPynecrtF^o3HH-CY|3T1GBR;W(Og zpWnwWw%NGFDL*@>)c*GV1ripY6w|=m&Q5!d*8@CpV`T5kblMv~c=ANVEt!b|i@hN@ z_)gTYcQzAIc?W+=QWEP9Wi{g6C}l%<;jsQMJ(Z*h53lWk)o-=LvBKUIllIdoeqgqx zrcSB)&|MstNIjm!&fzl_?<^SyY=j*QsoN|Y+Qj^%{tX;F1oN5O{ltsBW~cG^qmO`> zeHz*d2?5!oSev=1znblg(G!XjBE!6=f6RGSkp!`-{8vzratDfNOC_F~*|DpJldc|y z4O7Skq%n3}^6;mzCHtC2GphM<8`Byp|%_&}jrGoNtbyMp(w9=}w`dkm9CuROulTW}po{nS11$64Q z6&LfxmY^M~4Co{&r0!q!vg?l#qefj5K9KA!GqQ2A4j{Hao*%!LiAKM^Mb=(}d={^t zgnDZ7q}t!rE{hirbAD&DCdbVP0RExhyhy@{p!{Zw9J+jp%O3r<*Bt694HG5SeG5s= zw*uKJ6i^@r%yf}S3d~bVr7$H-z4*ML&$Q#MkSt!@K5QGoN=r+Ft9W>h1pHHYpPZV~ zdQ(u~pSNIf@O-ujHu_g1*V8x3#-Y&(Z&^0jgKr!7bJTk;JK8TH@(~@G*ljwa7k+$b z4PeW4Z~I<<_Np^jfjcgQzipiO#Ars5A;sagk4n0Y)YaZC8YfMUL=f>cR{ne!nf34? z{JP42Wu{2fG_~J$u5QUEH%FIZ$A+cey!~V5wlG+@P!o8q%>;Y z#ESsWllbe}e;&q|%}*)Si@5B;weMOstWTJeAyC583JC%l6*gDW$(F&N-Y-478UkF} zgoPZW(`-2YUD->)*XbD$XQDnE4p z9XSM;CKeqc4Y1S1q#d=LqL+#)Am_irYLmih-Kq41w-m6zD zzQQ>TgW4bRB~^*E^3JkM(dT^F)UjI4ur18#KQ`i&p<0{KhYuZ+1#~a8-9~0atVP%R zu_qq(VLb=|p?%W^L*z4E3y7Ns`ZjHvHr45)t9G8)K+mk>o1?atmgymB>^j(%KzNPq zGqU;BY?amXqNh|TdT+la3Kq>PM<#ZoHMXOUj)Fk&Dw$(mSDYDh{#j|G~sntBDv%I{;u?NK- zo*&{5pTJ$=*kRqBp63e)D#in~7HCF^eg_6LoO`b=P@)5DKo`1=R6bcI=#dt=LAnYG zX4mhhQ&QTezWLaiUvk@~g}cH{z!7W1C;fBeNYmY#paz?F7v&!KVl<;clc8^I)(xiZ4g_y== zV-8Ko?P&dQTi$?n`0l^!YEd1C-InZxdqnf0jZ2v_vPZj;V`wIdfxCyjNC+0vyaRR`^wrrBxsDn*1+ zx4@|{#s>xzUe(`O{g-a^P^o$?BjO*qOI)X$gp)9HX5JFNyyrvfIaF(W32vYs4xB`~ zThV>sqw;fPfS8I?{1`SvG7_tY-}1nSi!ZG$Cl4oJVTvI$!J3JUZY^ExSz8}Hd-m*z z&qF$qrvyK*r`-MKjKDM*7%QtDkz(ekbo0IQxj$T%6jQ{ZBw$Zswc|YW-{>oT4_(u~ zoSN9Gi30gJ#J0_dh&boP3rkb%8V>8kbbx)+FGZ*;HUb21J%pGTK_-12Y> zJEWPWH6wS}c0xq+D&3!Pf4HTkk%AkptNRUVh0Xzw?m4bg5)Ly+fxBQG_Dy2ES8RA` zcDGe{2Tp$7-~Fqb{v><*XmYzm;oiN6la{z8c2lQzefchXXrS0PN=p}he_2ikHLsqa zjnzS!Nw4VPeO>*JL~_Ap2ye`B3MEAWr7W%QvbGm^3w*N%>$PZj-5|`XQf|uMek~=S zv|s&N1C5D`2qnzF?CRT72As+u-HR8S{8%zvt?Nb8hNd=Vs$KO_$hvj@-VQgJUsH#5 zKMt>|=bXI$Tnjs~AN--WwX%dWYTVeGY8wik->P-) zLJLi3=YY6sx@$I;sTF&{J6hRUczw3*-uR-jG5vv|?T^~&hxoOIj=+A@`uvK^_X?E9 zt&%{rMBJ;Ql%Y<8SZJ)5;c1|lP(RJwI}M%W>~KJ69@qf9pjRFC5?{#|7C@SI-oB|p z#R2^m4TT1W)A(u}KAI1>1wnGVNozLYuw+#AxP7D40s78KT5~kNZ*;k`G2xk9%39T~ zN6or96u){UdyFC4@kX1HnU7^*Z8CIJyz1*Q)=V|rw*K(p8SA~xHW6$a3ura+IWw&% zB6jZFJn0ig8|$#M)So6QvAt(f^693!3>bJfL|xk!=SB4Q9WFn+PRK?2 zx@c@n<=m>%;pClhAG}_$@NV?A;^c??#(+7g8(~HzP|%O3f69QXCi{+Qpb0kR9lkcs zoxdrqVV?*Wtt0=nS}w<;c} zKo^lQio*PoaX{Kx%UL9TB0uiEd>pSKcrmqS&*8?`N&5A9%CxOTuV4RFSA$o=BoG;T zk7o&77P4jWKU2@T6yN3>{HX1y8=VzM7}eD_i5*dJpsK0Od2D`dW-qW&PmjChI7X3U zb1=&d2-&kwDC4ll!B*g^-JTn_Nqy6qr`ZhSV4MgvVM{ITY=%zF^w|HTjYrQOo10rJ z6q->o7vN}OqVS1K7wlZ+g0|rJaPcZ0;pkcFdvE<%W%b=OJ|V%H<4y=jcweSwzx9IJ z9n<4fHKYYQ{T4VTkhczJx&J!ArNY$^vdcpM za{Z36&#ERIil=zyf7I{Qt6xKFo3u^b!kF6oQR_C0>QCtMl9N(-AZ+ay_H5vhwm&&rS;kA<{(@ zP5m}w^3g|#*Zl|GL%dp=>#$0_%F|do^nD`k&Hzc9JGS;}(=TzSt&!FoYnzC;)PM`> z$5sq#hjSE5m(BsFhBh{44bo4A5q~H|U)A1%eS)rRGy6DxO=+1Rz3P5(P8w0M)C|eQ z;oYA&Ubkv;@?p;Hvxb&TFpRkydZ5n$wf@BJe%)iux*UK0vFom^6-Q}EpLYDy%6)j; zJnp>OTDRSym-*Rq7A`bVn>%{e0XPmG(B@HU#dE0$eD5@mf*3cxox0U)mYU`M3Y-Np zoku1VlgKTam;}h<5sAv=z<<~9CC^6y;&pnx$UZ9EG^$I2iPXTHY+ymkhccVY81-sP zO~+pP`UZPGM%cD5pZMeE^HECiV*_057r}1x!WbIUqg7qaA741L(LE;Qo|-Cu{p;60 zA1-t@j{Q+y-f~#=vl)AXg5D20JJ#^I9;r;bLF}eU%lUcRc6S{8g<^T--g@_FQ(U&} zRFYFnmS(%*@e{R=yM3OPkdc`=`uSU~^NpKT=?^=|NSd=}ODFHvI&S}LzJZ0;SU{F= zg7wNVg(bJUbK}OL>57;{#l|zcFE-ry-ge;N!J^U9{AYcB@}x;Phtz3zYW9rT1mfQR zz0-U3YQe6;2e2SP3Myb>rq^$bRs`phUTatsDa?Yn_vpG}xcn^VvAd3_fCMTF{F}(I zF^-dYVy;DEZt$(u`^Tv8Qcu@yxu(9Z+FpC58<$&S0}>H7##gkb7Moul?u80D&fZ=o z+QY`^f&(#8LF_a)c>sAI(#L96pWV0KzT!wRk=CC*)bA&`Xom1ar9$PN?OI_&Fi-t+ zm_Bs5Fr!yW(vRWu=O5km(D-?BzZv&1TUJ2Dc~m!3|Gw{5R_@zRpYHX3{IJUgbHk|5 zOSyCmco6e_c=&9O>ABN=1*<#V&!qPRP7ipAVgJPhW>l*5P=%QTrvnc;! zWFswE^}B(Zz#PTiza2+zPjslB3CFE{{KP%TR79+#UiHmoJ_+QbkT8{zwLd%X1&!0D z=9JsO6?0XuIn`E8W+LYIrx$g39UK-D<&Ki@CQh7nWEPf0&ya6Bk$ia?3yQ@$_l`$jHc>*T4y%cGfTX{CU_+uVusuhp;KiOj&iS2Olvr z`g}@?YmWw$Vk(;i}5V#k3LaZ#z(SpIE6;n66=P}!E`8Uq{svR3XSI^Slpzfm08;uBfccA ztGRRJ)7r@d21p<23X)09qEuB+Ro;B}(OI>LU@|d`hye=dF~>M>r?$mNz{tVz@dbEm zNl}!EQYltfTTl{ZWE{@u5kr0#4-6JjiZQ9;SFaw(n-t3h8Z4vBSKP4;r*&4wXG_oa z(xn=8R^tC<22Of@zLU@6GHAm5<=~^N^(NgZ))FRpT~3*95R+)S(WuTb`=b|)=j_Du zT+VRNu6Zs48Z20}sfF%xm^+OmsZS%?kVXBp1E=TKs7*fK=I@u!vb|S9g;<+4Ev`c6 zy|yrWMR7l#>S_1*oU=!v#yHt8>S|FD7|#EDO8i9gqmN!4@cty@;H@6`NhoS@%(MK_ zO1L5?*75-uZTM`#^_hYy4wEYbUvnYTDp-g zT+qUxD$}ZDIK%c^UOo3e49E0FXcpWyR!x;R#|g+P z>4fuOT`nI*)!Ba`f8_%JL7l;Ew5=|;3d=3_cwvA3=p%QGcC>(QD&c(2%s!8T2I>6j zTve49-}^^W)f6D-2nVba?34};X)OPemnVau2{cR;)o6Yd!^zC?Q}(E>{C5j9ojD0_ zXUB}Q@k%60Iy_oBZTj@l%HO;$9;fP}8^|d6_^DIbN1oeGCW2p9{_d6hJZH_vxqZK* zsAN7Jd(h7GaE@7GuGo5szhbE4tg5W?^~gMV`gFQP0WMs@(ZrMw<%>hQ0xYP}cc`W) zLlUH?Tg(~^sosjTCiZk{fLVQ>=3lk_fg>OqW1+0q z<8uhq#5p5}+42H0Ldf zZgil};*97!RSoT>K^5uik9$QXMbJ_)x#mFf0idmHD5JywDwe;%kDf|3mz3ghkwBAK zv8AY`$x9+ODw)!dZXA$kCsD&JEn}qUfa$?|)=wuR$Ng-y&sY+LYQULu=b|~2bYZ$J zT-Y)+lHbOvz@nj_&H6CTI|m7L@ICM2h3@mlPu}w-yh?k(;K3G9rfF#hpAD5hck_b= zPY;(|J9#Foh2~O(m#w+Mfd_UpFx*DxEwqgtcowV}=Bv~>L;#j~!q zvrGkCytwt7qo~m$#nsgwe?cdGP3)Z;_AUNBs`1D=>gkidjyL7=1|NDC zSZ&k%y5d^E1&_#yUX6Tjw%_L0B{i}8_qbcG$tfN^H*|9NRC@0B^W5oElM-U10t??H z3{$BdTAXh&8EP^eBGJ24ODE#a!Q2vAZ2%A7rpb^m3b8nhbKIzYZexCA?XS7g{zM^^ zZnDF1g!;LP(r3A)DZJT2Y#_2R9JKkM$h(h@PU^?6vw5wSlYQ0CQPu@npH4XXoL?L@ zopac`t~S;E(bsWnV%4@MRNHL7M)HYXdS>98(_Xr>x7e6z{3)bHb?&>TAiS<#_6JWu z-+)1b>Q)*X%$#C7vf(97Wos-WU?G-FT3zTKYOVJ& zC-AdN*9RR-z2hp%BMPf0c;!EP`t%hAV9lUF>zFhYsgvxOkTDrl@oFfeNQ=@Ib=*Ge zgY$-tm^Ls`wvF`p+CY>hLio}ScT9O#SvszN;#+jqsBU4#58n8B>D3?L>(phbdS4TM z0gplZv#u?|NBG_A@-EZICFNjyl`kdNP8YsjLOP}3xb?12kMoZnJn*H1;^@%B332{v zPWKbcnsohAH)?v5W?iG|rs^xzH*kE@0_%=YcFY~CrWdMLBr#kz%r6(CMKI)^>0iVs6i9JD`r zl0DkeCQnmvlcAM5n2%&AH`hrKk)N}!GQEQcMc^uqXmiC9+>9}F?Xdi@qnNlz?&6t; zXAr=Eh9N(cp4T)+do(fmzIE!Ai?opY)RgB6%}X0{koLC1rLfoo9bRxhK;6T zNIfKX{2YH588jvZTy&qhiqbtR;_VVz(F=I&sSIK>AQ5!=E7LxFY^QM zF3}E&+UVkZ_==j+uo~lsnU~@gY)ShI>6;q^Uysd7r(eHwPGPn0W5Q%{(T<1+nTC(N z+lqZ9!L(Y!FBLtR8+l!P)G`Z36z^}D{g+mKpC>lTRnM2IAhF?})%Wv~j?q2F^G|q| z&Gg}GJn?NI$cEsNC?XM=V<%;D+eVohHg;WA^OtKX94LuIj=}tsjzi9?-Dkd_jImO# z;2xQ!h8;-8g%CF7)l~Aocn@KTcz^jX8SCKaU1}^=a31@UmSKi^dP*GrOIc<3uhxFs zn=dt(jOZgl+^V^|x`xgdTGAdBeNL6;Qv2g#MRoKkv+@BYAuSh2)$5)tMoe1OH!*Os zan*7zj>f;?-ZJI@DPUKys$#c=CxQ48$~-dC=}P-r@iEbtDZ}0sLE$x=u%yy+&%U5P1gixw#82ji zG8SeCHIl;;Qq(Yf6L8lEio4FXq=YMyPC> zK^U!0@C@ODiy|Mgr8VBTb&P0K_+q8nJXF`Af<j8H%09c`zN$Wi$IeGn~Lu4S*T0+7op30~IfsS^S%Sr)+|MGA)=@A=B<{Vx z!gNRXWZ1523t>_fy~chNW069E)VbzID4+;WuTraCGgK8+-#0jqoAFc&cuqqa9(`bY zp{JTdrsn8&_ghS;E-$YtFCR<-)#Nb99706JCStgzgTU>vC&?kmy1BAoeEB!kuD%ED z^ri7n24(PN8kvQOf$=)S?d@6Nd*ye@6&T>hMXr&auNwSs5(Hidu?YKM9(lDkeq%u4 za?Ky>x7oy9CSwYFcV4+r@Q7=e$(&_($yW6h=ws!cV*-l|S4>}*k(X0;ca39IMehB5 z`Dro^kt-*RIp$m$FNTXeQ(K;YmYd7VF1;j}kfiMbGxOdo8;B!aMynBVW2oAAv;(ma z^w(LxIpxL@ESifdw0_K3U9euIprGK-A_6o_1o_F&CtTagT&eb6?>ojUWFp4pEx8mz zutJk-o?FD9|MQ#RvDrc+>X7n>6JY&s(4V{Yz=5G2T}+8;M)h`071+eTsSzPbc+wLf zPYf0p@X;>YkV*<%;7X@s($BYhuU}h@1udoWRUAqH@~US zbjDYkyv)a;28xf2 zo*3zGkSET;(yubL5$7Yp3O>=}&VsGl(S)j1-YNXa(!KUaOV2qi2D1tMny`*rEVHP% zga_K+k3U>CgR^I1-z|LyofI-}kId4!V*B^+auCHLi2c>7pfjr^co#jCtayyMZGVnb zI{R1Pzo+nSKb|H?+o7ze_G`oy9Y!nBv0Ab`zjsPyU5V%X^8Dl=20K8{*HtV^`8fxT zr-;=zm0$1lmYpm`xT?~4hM`-4w1Kn_Izw~ODWP$`19m^$ZZc^YR8AjzAXlz_;+5Zn z(ECZvVuOUK7NmvV$%IF78+h+uQj3jUy6~YK0oj7MOi_Z+*p=qaeaFW}%Vt_;24Gwm zV5kqO@6Vev@+cKbJ<*M!Xwvas{b|=hl0{NDf2*d-tEQ@|oiLDCxx@l5=Rx*FW1U|2 z^NtgMFw-khaQ;##j%AfM&u*k^#rlD^q(~k1u z)DY{2apN1rji2}9_nOOV53N-}lc%g!_#)CDto8p(K1HXz)AG&u$9f+vpr^g3OK8n{ zpCM|D{1d8)-$ZPgWLyZ@AVPCgQj%xVMma8){vorY{{8E2*9dLmTPq{&G^wzYKXp?z z{NH!~^F@IzMb-Gf{}>(nQy2bueDVZcuK(|c{`tx3-TxO2|NLdQDgWin|NegZ=6?~* ze}7%u{=cm7=kID{osd?6|M$=T??=aJeyZ~)sc2+{kFd4X{?ze-z8Js4ZH=F&%Nza> zJ}NP{WTSV}e}DYLVSRQlNZM!`bMtb=%V0pCdWC`fG_O-(yZ`%)F->##%P*64HsB9+ u^S@u_|NB82Dn6e0^K}3BN#DG#ReD}>>q(u;Fg*qTjIbJGnPBeh_kREn1o&qF literal 0 HcmV?d00001